diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8a94b43d34e0..73a1e67a9beb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,10 +47,10 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - - name: JDK 21 + - name: JDK 25 uses: actions/setup-java@v4 with: - java-version: 21 + java-version: 25 distribution: 'zulu' - name: Setup Gradle @@ -115,14 +115,14 @@ jobs: - name: Create Paperclip Jar if: fromJSON(steps.determine.outputs.result).action == 'paperclip' - run: ./gradlew createMojmapPaperclipJar --stacktrace + run: ./gradlew createPaperclipJar --stacktrace - name: Upload Paperclip Jar if: fromJSON(steps.determine.outputs.result).action == 'paperclip' uses: actions/upload-artifact@v4 with: name: paper-${{ fromJSON(steps.determine.outputs.result).pr }} - path: paper-server/build/libs/paper-paperclip-*-mojmap.jar + path: paper-server/build/libs/paper-paperclip-*.jar - name: Publish Artifacts if: fromJSON(steps.determine.outputs.result).action == 'paperclip' diff --git a/.gitignore b/.gitignore index e46ae58b1898..44b14519cf11 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ nbactions.xml bin/ dist/ manifest.mf +__pycache__/ # Mac filesystem dust .DS_Store/ diff --git a/build-data/paper.at b/build-data/paper.at index 3fd1ec2ceb0f..9c8788ce2b74 100644 --- a/build-data/paper.at +++ b/build-data/paper.at @@ -32,7 +32,7 @@ public net.minecraft.network.protocol.game.ServerboundMovePlayerPacket z public net.minecraft.network.syncher.SynchedEntityData getItem(Lnet/minecraft/network/syncher/EntityDataAccessor;)Lnet/minecraft/network/syncher/SynchedEntityData$DataItem; public net.minecraft.resources.RegistryOps lookupProvider public net.minecraft.resources.RegistryOps$HolderLookupAdapter -public net.minecraft.server.Main forceUpgrade(Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;Lnet/minecraft/world/level/storage/WorldData;Lcom/mojang/datafixers/DataFixer;ZLjava/util/function/BooleanSupplier;Lnet/minecraft/core/RegistryAccess;Z)V +public net.minecraft.server.Main forceUpgrade(Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;Lcom/mojang/datafixers/DataFixer;ZLjava/util/function/BooleanSupplier;Lnet/minecraft/core/RegistryAccess;Z)V public net.minecraft.server.MinecraftServer LOGGER public net.minecraft.server.MinecraftServer doRunTask(Lnet/minecraft/server/TickTask;)V public net.minecraft.server.MinecraftServer executor @@ -41,11 +41,13 @@ public net.minecraft.server.MinecraftServer playerDataStorage public net.minecraft.server.MinecraftServer resources public net.minecraft.server.MinecraftServer serverThread public net.minecraft.server.MinecraftServer$ReloadableResources +public net.minecraft.server.MinecraftServer$ReloadableResources (Lnet/minecraft/server/packs/resources/CloseableResourceManager;Lnet/minecraft/server/ReloadableServerResources;)V public net.minecraft.server.RegistryLayer STATIC_ACCESS public net.minecraft.server.ReloadableServerResources public net.minecraft.server.ServerAdvancementManager advancements public net.minecraft.server.Services USERID_CACHE_FILE public net.minecraft.server.dedicated.DedicatedServerProperties$WorldDimensionData +public net.minecraft.server.dedicated.DedicatedServerProperties$WorldDimensionData (Lcom/google/gson/JsonObject;Ljava/lang/String;)V public net.minecraft.server.dedicated.Settings getStringRaw(Ljava/lang/String;)Ljava/lang/String; public net.minecraft.server.dedicated.Settings properties public net.minecraft.server.level.ChunkHolder oldTicketLevel @@ -96,6 +98,7 @@ public net.minecraft.server.level.ServerPlayer setShoulderEntityRight(Lnet/minec public net.minecraft.server.level.ServerPlayer triggerDimensionChangeTriggers(Lnet/minecraft/server/level/ServerLevel;)V public net.minecraft.server.level.ServerPlayer wardenSpawnTracker public net.minecraft.server.level.ServerPlayer$RespawnPosAngle +public net.minecraft.server.level.ServerPlayer$RespawnPosAngle (Lnet/minecraft/world/phys/Vec3;FF)V public net.minecraft.server.level.ServerPlayerGameMode level public net.minecraft.server.network.ServerConfigurationPacketListenerImpl clientInformation public net.minecraft.server.network.ServerConfigurationPacketListenerImpl currentTask @@ -136,8 +139,10 @@ public net.minecraft.world.damagesource.CombatTracker takingDamage public net.minecraft.world.damagesource.DamageSource (Lnet/minecraft/core/Holder;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/Vec3;)V public net.minecraft.world.effect.MobEffect attributeModifiers public net.minecraft.world.effect.MobEffect$AttributeTemplate +public net.minecraft.world.effect.MobEffect$AttributeTemplate (Lnet/minecraft/resources/Identifier;DLnet/minecraft/world/entity/ai/attributes/AttributeModifier$Operation;)V public net.minecraft.world.effect.MobEffectInstance hiddenEffect public net.minecraft.world.effect.MobEffectInstance isShorterDurationThan(Lnet/minecraft/world/effect/MobEffectInstance;)Z +public net.minecraft.world.entity.AgeableMob setAgeLocked(Z)V public net.minecraft.world.entity.AreaEffectCloud durationOnUse public net.minecraft.world.entity.AreaEffectCloud owner public net.minecraft.world.entity.AreaEffectCloud potionContents @@ -218,6 +223,7 @@ public net.minecraft.world.entity.Interaction setHeight(F)V public net.minecraft.world.entity.Interaction setResponse(Z)V public net.minecraft.world.entity.Interaction setWidth(F)V public net.minecraft.world.entity.Interaction$PlayerAction +public net.minecraft.world.entity.Interaction$PlayerAction (Ljava/util/UUID;J)V public net.minecraft.world.entity.ItemBasedSteering boostTime public net.minecraft.world.entity.ItemBasedSteering boostTimeTotal()I public net.minecraft.world.entity.ItemBasedSteering boosting @@ -249,6 +255,7 @@ public net.minecraft.world.entity.Mob getAmbientSound()Lnet/minecraft/sounds/Sou public net.minecraft.world.entity.Mob isSunBurnTick()Z public net.minecraft.world.entity.Mob lootTable public net.minecraft.world.entity.Mob lootTableSeed +public net.minecraft.world.entity.Mob persistenceRequired public net.minecraft.world.entity.OminousItemSpawner setItem(Lnet/minecraft/world/item/ItemStack;)V public net.minecraft.world.entity.OminousItemSpawner spawnItemAfterTicks public net.minecraft.world.entity.ai.attributes.Attribute sentiment @@ -276,6 +283,10 @@ public net.minecraft.world.entity.animal.bee.Bee setRolling(Z)V public net.minecraft.world.entity.animal.bee.Bee stayOutOfHiveCountdown public net.minecraft.world.entity.animal.bee.Bee ticksWithoutNectarSinceExitingHive public net.minecraft.world.entity.animal.bee.Bee timeSinceSting +public net.minecraft.world.entity.animal.chicken.Chicken getSoundVariant()Lnet/minecraft/core/Holder; +public net.minecraft.world.entity.animal.chicken.Chicken setSoundVariant(Lnet/minecraft/core/Holder;)V +public net.minecraft.world.entity.animal.cow.Cow getSoundVariant()Lnet/minecraft/core/Holder; +public net.minecraft.world.entity.animal.cow.Cow setSoundVariant(Lnet/minecraft/core/Holder;)V public net.minecraft.world.entity.animal.cow.MushroomCow setVariant(Lnet/minecraft/world/entity/animal/cow/MushroomCow$Variant;)V public net.minecraft.world.entity.animal.cow.MushroomCow stewEffects public net.minecraft.world.entity.animal.dolphin.Dolphin treasurePos @@ -285,9 +296,11 @@ public net.minecraft.world.entity.animal.equine.AbstractHorse owner public net.minecraft.world.entity.animal.equine.Horse setVariantAndMarkings(Lnet/minecraft/world/entity/animal/equine/Variant;Lnet/minecraft/world/entity/animal/equine/Markings;)V public net.minecraft.world.entity.animal.equine.Llama setVariant(Lnet/minecraft/world/entity/animal/equine/Llama$Variant;)V public net.minecraft.world.entity.animal.equine.SkeletonHorse trapTime +public net.minecraft.world.entity.animal.feline.Cat getSoundVariant()Lnet/minecraft/core/Holder; public net.minecraft.world.entity.animal.feline.Cat isRelaxStateOne()Z public net.minecraft.world.entity.animal.feline.Cat setCollarColor(Lnet/minecraft/world/item/DyeColor;)V public net.minecraft.world.entity.animal.feline.Cat setRelaxStateOne(Z)V +public net.minecraft.world.entity.animal.feline.Cat setSoundVariant(Lnet/minecraft/core/Holder;)V public net.minecraft.world.entity.animal.feline.Cat setVariant(Lnet/minecraft/core/Holder;)V public net.minecraft.world.entity.animal.feline.Ocelot isTrusting()Z public net.minecraft.world.entity.animal.feline.Ocelot setTrusting(Z)V @@ -311,6 +324,8 @@ public net.minecraft.world.entity.animal.nautilus.AbstractNautilus inventory public net.minecraft.world.entity.animal.panda.Panda getEatCounter()I public net.minecraft.world.entity.animal.panda.Panda setEatCounter(I)V public net.minecraft.world.entity.animal.parrot.Parrot setVariant(Lnet/minecraft/world/entity/animal/parrot/Parrot$Variant;)V +public net.minecraft.world.entity.animal.pig.Pig getSoundVariant()Lnet/minecraft/core/Holder; +public net.minecraft.world.entity.animal.pig.Pig setSoundVariant(Lnet/minecraft/core/Holder;)V public net.minecraft.world.entity.animal.pig.Pig setVariant(Lnet/minecraft/core/Holder;)V public net.minecraft.world.entity.animal.pig.Pig steering public net.minecraft.world.entity.animal.rabbit.Rabbit moreCarrotTicks @@ -534,20 +549,21 @@ public net.minecraft.world.inventory.Slot slot public net.minecraft.world.item.AdventureModePredicate predicates public net.minecraft.world.item.BucketItem content public net.minecraft.world.item.CrossbowItem FIREWORK_POWER -public net.minecraft.world.item.DebugStickItem handleInteraction(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/LevelAccessor;Lnet/minecraft/core/BlockPos;ZLnet/minecraft/world/item/ItemStack;)Z +public net.minecraft.world.item.DebugStickItem handleInteraction(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/LevelAccessor;Lnet/minecraft/core/BlockPos;ZLnet/minecraft/world/item/ItemStack;)Z public net.minecraft.world.item.ItemCooldowns cooldowns public net.minecraft.world.item.ItemCooldowns tickCount public net.minecraft.world.item.ItemCooldowns$CooldownInstance +public net.minecraft.world.item.ItemCooldowns$CooldownInstance (II)V public net.minecraft.world.item.ItemStackLinkedSet TYPE_AND_TAG public net.minecraft.world.item.JukeboxSongPlayer song public net.minecraft.world.item.MapItem createNewSavedData(Lnet/minecraft/server/level/ServerLevel;IIIZZLnet/minecraft/resources/ResourceKey;)Lnet/minecraft/world/level/saveddata/maps/MapId; public net.minecraft.world.item.StandingAndWallBlockItem wallBlock -public net.minecraft.world.item.component.BundleContents$Mutable getMaxAmountToAdd(Lnet/minecraft/world/item/ItemStack;)I public net.minecraft.world.item.component.ItemContainerContents MAX_SIZE public net.minecraft.world.item.component.ItemContainerContents items public net.minecraft.world.item.component.ResolvableProfile unpack()Lcom/mojang/datafixers/util/Either; public net.minecraft.world.item.component.ResolvableProfile$Dynamic (Lcom/mojang/datafixers/util/Either;Lnet/minecraft/world/entity/player/PlayerSkin$Patch;)V public net.minecraft.world.item.component.ResolvableProfile$Partial +public net.minecraft.world.item.component.ResolvableProfile$Partial (Ljava/util/Optional;Ljava/util/Optional;Lcom/mojang/authlib/properties/PropertyMap;)V public net.minecraft.world.item.component.ResolvableProfile$Partial MAP_CODEC public net.minecraft.world.item.component.ResolvableProfile$Static (Lcom/mojang/datafixers/util/Either;Lnet/minecraft/world/entity/player/PlayerSkin$Patch;)V public net.minecraft.world.item.context.UseOnContext (Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/phys/BlockHitResult;)V @@ -584,6 +600,7 @@ public net.minecraft.world.level.TicketStorage tickets public net.minecraft.world.level.biome.Biome climateSettings public net.minecraft.world.level.biome.Biome getTemperature(Lnet/minecraft/core/BlockPos;I)F public net.minecraft.world.level.biome.Biome$ClimateSettings +public net.minecraft.world.level.biome.Biome$ClimateSettings (ZFLnet/minecraft/world/level/biome/Biome$TemperatureModifier;F)V public net.minecraft.world.level.block.Block popExperience(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;I)V public net.minecraft.world.level.block.ChestBlock isBlockedChestByBlock(Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)Z public net.minecraft.world.level.block.ComposterBlock$EmptyContainer @@ -699,20 +716,20 @@ public net.minecraft.world.level.chunk.status.ChunkStatusTasks postLoadProtoChun public net.minecraft.world.level.chunk.storage.EntityStorage entityDeserializerQueue public net.minecraft.world.level.chunk.storage.EntityStorage level public net.minecraft.world.level.chunk.storage.RegionFileStorage regionCache -public net.minecraft.world.level.dimension.end.EndDragonFight GATEWAY_COUNT -public net.minecraft.world.level.dimension.end.EndDragonFight dragonEvent -public net.minecraft.world.level.dimension.end.EndDragonFight dragonUUID -public net.minecraft.world.level.dimension.end.EndDragonFight findExitPortal()Lnet/minecraft/world/level/block/state/pattern/BlockPattern$BlockPatternMatch; -public net.minecraft.world.level.dimension.end.EndDragonFight gateways -public net.minecraft.world.level.dimension.end.EndDragonFight level -public net.minecraft.world.level.dimension.end.EndDragonFight portalLocation -public net.minecraft.world.level.dimension.end.EndDragonFight previouslyKilled -public net.minecraft.world.level.dimension.end.EndDragonFight respawnCrystals -public net.minecraft.world.level.dimension.end.EndDragonFight respawnDragon(Ljava/util/List;)V -public net.minecraft.world.level.dimension.end.EndDragonFight respawnStage -public net.minecraft.world.level.dimension.end.EndDragonFight setRespawnStage(Lnet/minecraft/world/level/dimension/end/DragonRespawnAnimation;)V -public net.minecraft.world.level.dimension.end.EndDragonFight spawnExitPortal(Z)V -public net.minecraft.world.level.dimension.end.EndDragonFight spawnNewGateway(Lnet/minecraft/core/BlockPos;)V +public net.minecraft.world.level.dimension.end.EnderDragonFight GATEWAY_COUNT +public net.minecraft.world.level.dimension.end.EnderDragonFight dragonEvent +public net.minecraft.world.level.dimension.end.EnderDragonFight dragonUUID +public net.minecraft.world.level.dimension.end.EnderDragonFight exitPortalLocation +public net.minecraft.world.level.dimension.end.EnderDragonFight findExitPortal()Lnet/minecraft/world/level/block/state/pattern/BlockPattern$BlockPatternMatch; +public net.minecraft.world.level.dimension.end.EnderDragonFight gateways +public net.minecraft.world.level.dimension.end.EnderDragonFight hasPreviouslyKilledDragon +public net.minecraft.world.level.dimension.end.EnderDragonFight level +public net.minecraft.world.level.dimension.end.EnderDragonFight respawnCrystals +public net.minecraft.world.level.dimension.end.EnderDragonFight respawnDragon(Ljava/util/List;)V +public net.minecraft.world.level.dimension.end.EnderDragonFight respawnStage +public net.minecraft.world.level.dimension.end.EnderDragonFight setRespawnStage(Lnet/minecraft/world/level/dimension/end/DragonRespawnStage;)V +public net.minecraft.world.level.dimension.end.EnderDragonFight spawnExitPortal(Z)V +public net.minecraft.world.level.dimension.end.EnderDragonFight spawnNewGateway(Lnet/minecraft/core/BlockPos;)V 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 @@ -728,6 +745,7 @@ public net.minecraft.world.level.levelgen.SurfaceRules$LazyCondition public net.minecraft.world.level.levelgen.SurfaceRules$LazyYCondition public net.minecraft.world.level.levelgen.SurfaceRules$SurfaceRule public net.minecraft.world.level.levelgen.SurfaceRules$VerticalGradientConditionSource +public net.minecraft.world.level.levelgen.SurfaceRules$VerticalGradientConditionSource (Lnet/minecraft/resources/Identifier;Lnet/minecraft/world/level/levelgen/VerticalAnchor;Lnet/minecraft/world/level/levelgen/VerticalAnchor;)V public net.minecraft.world.level.levelgen.structure.StructurePiece SHAPE_CHECK_BLOCKS public net.minecraft.world.level.levelgen.structure.placement.StructurePlacement exclusionZone public net.minecraft.world.level.levelgen.structure.placement.StructurePlacement frequency @@ -736,10 +754,11 @@ public net.minecraft.world.level.levelgen.structure.placement.StructurePlacement public net.minecraft.world.level.levelgen.structure.placement.StructurePlacement salt public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate entityInfoList public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate palettes -public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager loadFromGenerated(Lnet/minecraft/resources/Identifier;)Ljava/util/Optional; -public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager loadFromResource(Lnet/minecraft/resources/Identifier;)Ljava/util/Optional; -public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager readStructure(Ljava/io/InputStream;)Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate; +public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager resourceManagerSource public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager structureRepository +public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager tryLoad(Lnet/minecraft/resources/Identifier;)Ljava/util/Optional; +public net.minecraft.world.level.levelgen.structure.templatesystem.loader.TemplateSource readStructure(Ljava/io/InputStream;)Lnet/minecraft/nbt/CompoundTag; +public net.minecraft.world.level.levelgen.structure.templatesystem.loader.TemplateSource readStructure(Lnet/minecraft/nbt/CompoundTag;)Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate; public net.minecraft.world.level.material.MapColor MATERIAL_COLORS public net.minecraft.world.level.pathfinder.Path nodes public net.minecraft.world.level.pathfinder.PathFinder nodeEvaluator @@ -748,10 +767,10 @@ public net.minecraft.world.level.saveddata.maps.MapItemSavedData carriedByPlayer public net.minecraft.world.level.saveddata.maps.MapItemSavedData decorations public net.minecraft.world.level.saveddata.maps.MapItemSavedData setColorsDirty(II)V public net.minecraft.world.level.saveddata.maps.MapItemSavedData setDecorationsDirty()V -public net.minecraft.world.level.storage.DimensionDataStorage cache public net.minecraft.world.level.storage.LevelStorageSource baseDir public net.minecraft.world.level.storage.LevelStorageSource$LevelStorageAccess levelDirectory public net.minecraft.world.level.storage.PrimaryLevelData settings +public net.minecraft.world.level.storage.SavedDataStorage cache public net.minecraft.world.level.storage.TagValueInput input public net.minecraft.world.scores.Objective displayName public net.minecraft.world.scores.criteria.ObjectiveCriteria CRITERIA_CACHE @@ -778,8 +797,6 @@ public-f net.minecraft.world.item.trading.MerchantOffer maxUses public-f net.minecraft.world.item.trading.MerchantOffer priceMultiplier public-f net.minecraft.world.item.trading.MerchantOffer rewardExp public-f net.minecraft.world.item.trading.MerchantOffer xp -public-f net.minecraft.world.level.LevelSettings hardcore -public-f net.minecraft.world.level.LevelSettings levelName public-f net.minecraft.world.level.block.ChestBlock MENU_PROVIDER_COMBINER public-f net.minecraft.world.level.block.entity.BannerBlockEntity baseColor public-f net.minecraft.world.level.saveddata.maps.MapItemSavedData centerX diff --git a/build.gradle.kts b/build.gradle.kts index 6cfd415fd2f3..2336fd226cbf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { - id("io.papermc.paperweight.core") version "2.0.0-beta.19" apply false + id("io.papermc.paperweight.core") version "2.0.0-SNAPSHOT" apply false } subprojects { @@ -11,7 +11,7 @@ subprojects { extensions.configure { toolchain { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(25) } } } @@ -21,7 +21,7 @@ val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/" subprojects { tasks.withType().configureEach { options.encoding = Charsets.UTF_8.name() - options.release = 21 + options.release = 25 options.isFork = true options.compilerArgs.addAll(listOf("-Xlint:-deprecation", "-Xlint:-removal")) } @@ -46,8 +46,8 @@ subprojects { extensions.configure { repositories { - maven("https://artifactory.papermc.io/artifactory/snapshots/") { - name = "paperSnapshots" + maven("https://artifactory.papermc.io/artifactory/releases/") { + name = "paperReleases" credentials(PasswordCredentials::class) } } diff --git a/gradle.properties b/gradle.properties index 4516647f0989..8ebc5c61df78 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ group=io.papermc.paper -version=1.21.11-R0.1-SNAPSHOT -mcVersion=1.21.11 +mcVersion=26.1.1 # 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=1.21.11 +apiVersion=26.1.1 +channel=ALPHA # Set to true while updating Minecraft version updatingMinecraft=false diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f8e1ee3125fe..d997cfc60f4c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bad7c2462f5a..c61a118f7ddb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index adff685a0348..739907dfd159 100755 --- a/gradlew +++ b/gradlew @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/paper-api/build.gradle.kts b/paper-api/build.gradle.kts index 49b247877966..82d8a47657c6 100644 --- a/paper-api/build.gradle.kts +++ b/paper-api/build.gradle.kts @@ -82,13 +82,13 @@ dependencies { api("org.jspecify:jspecify:1.0.0") // Test dependencies - testImplementation("org.apache.commons:commons-lang3:3.17.0") - testImplementation("org.junit.jupiter:junit-jupiter:5.12.2") + testImplementation("org.apache.commons:commons-lang3:3.20.0") + testImplementation("org.junit.jupiter:junit-jupiter:6.0.3") testImplementation("org.hamcrest:hamcrest:2.2") - testImplementation("org.mockito:mockito-core:5.14.1") - testImplementation("org.ow2.asm:asm-tree:9.8") - mockitoAgent("org.mockito:mockito-core:5.14.1") { isTransitive = false } // configure mockito agent that is needed in newer java versions - testRuntimeOnly("org.junit.platform:junit-platform-launcher") + testImplementation("org.mockito:mockito-core:5.22.0") + testImplementation("org.ow2.asm:asm-tree:9.9.1") + mockitoAgent("org.mockito:mockito-core:5.22.0") { isTransitive = false } // configure mockito agent that is needed in newer java versions + testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.0.3") } val generatedDir: java.nio.file.Path = layout.projectDirectory.dir("src/generated/java").asFile.toPath() diff --git a/paper-api/src/generated/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/paper-api/src/generated/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java index 50f6443b5af0..f094d7ee906e 100644 --- a/paper-api/src/generated/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java +++ b/paper-api/src/generated/java/com/destroystokyo/paper/entity/ai/VanillaGoal.java @@ -130,7 +130,7 @@ public interface VanillaGoal extends Goal { GoalKey FLEE_SUN = create("flee_sun", Creature.class); - GoalKey FOLLOW_BOAT = create("follow_boat", Creature.class); + GoalKey FOLLOW_PLAYER_RIDDEN_ENTITY = create("follow_player_ridden_entity", Creature.class); GoalKey GOLEM_RANDOM_STROLL_IN_VILLAGE = create("golem_random_stroll_in_village", Creature.class); diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/BlockTypeKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/BlockTypeKeys.java index c948b3f7b082..19df41713a1e 100644 --- a/paper-api/src/generated/java/io/papermc/paper/registry/keys/BlockTypeKeys.java +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/BlockTypeKeys.java @@ -2937,6 +2937,13 @@ public final class BlockTypeKeys { */ public static final TypedKey GOLD_ORE = create(key("gold_ore")); + /** + * {@code minecraft:golden_dandelion} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey GOLDEN_DANDELION = create(key("golden_dandelion")); + /** * {@code minecraft:granite} * @@ -5513,6 +5520,13 @@ public final class BlockTypeKeys { */ public static final TypedKey POTTED_FLOWERING_AZALEA_BUSH = create(key("potted_flowering_azalea_bush")); + /** + * {@code minecraft:potted_golden_dandelion} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey POTTED_GOLDEN_DANDELION = create(key("potted_golden_dandelion")); + /** * {@code minecraft:potted_jungle_sapling} * diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/CatSoundVariantKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/CatSoundVariantKeys.java new file mode 100644 index 000000000000..2fd93cf823f3 --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/CatSoundVariantKeys.java @@ -0,0 +1,54 @@ +package io.papermc.paper.registry.keys; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import net.kyori.adventure.key.Key; +import org.bukkit.entity.Cat; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#CAT_SOUND_VARIANT}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class CatSoundVariantKeys { + /** + * {@code minecraft:classic} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey CLASSIC = create(key("classic")); + + /** + * {@code minecraft:royal} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ROYAL = create(key("royal")); + + private CatSoundVariantKeys() { + } + + /** + * Creates a typed key for {@link Cat.SoundVariant} in the registry {@code minecraft:cat_sound_variant}. + * + * @param key the value's key in the registry + * @return a new typed key + */ + public static TypedKey create(final Key key) { + return TypedKey.create(RegistryKey.CAT_SOUND_VARIANT, key); + } +} diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/ChickenSoundVariantKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/ChickenSoundVariantKeys.java new file mode 100644 index 000000000000..a788aff2c522 --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/ChickenSoundVariantKeys.java @@ -0,0 +1,54 @@ +package io.papermc.paper.registry.keys; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import net.kyori.adventure.key.Key; +import org.bukkit.entity.Chicken; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#CHICKEN_SOUND_VARIANT}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class ChickenSoundVariantKeys { + /** + * {@code minecraft:classic} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey CLASSIC = create(key("classic")); + + /** + * {@code minecraft:picky} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey PICKY = create(key("picky")); + + private ChickenSoundVariantKeys() { + } + + /** + * Creates a typed key for {@link Chicken.SoundVariant} in the registry {@code minecraft:chicken_sound_variant}. + * + * @param key the value's key in the registry + * @return a new typed key + */ + public static TypedKey create(final Key key) { + return TypedKey.create(RegistryKey.CHICKEN_SOUND_VARIANT, key); + } +} diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/CowSoundVariantKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/CowSoundVariantKeys.java new file mode 100644 index 000000000000..d47bf078c6f5 --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/CowSoundVariantKeys.java @@ -0,0 +1,54 @@ +package io.papermc.paper.registry.keys; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import net.kyori.adventure.key.Key; +import org.bukkit.entity.Cow; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#COW_SOUND_VARIANT}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class CowSoundVariantKeys { + /** + * {@code minecraft:classic} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey CLASSIC = create(key("classic")); + + /** + * {@code minecraft:moody} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey MOODY = create(key("moody")); + + private CowSoundVariantKeys() { + } + + /** + * Creates a typed key for {@link Cow.SoundVariant} in the registry {@code minecraft:cow_sound_variant}. + * + * @param key the value's key in the registry + * @return a new typed key + */ + public static TypedKey create(final Key key) { + return TypedKey.create(RegistryKey.COW_SOUND_VARIANT, key); + } +} diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/DataComponentTypeKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/DataComponentTypeKeys.java index a3d447f8cd6d..76f44313dc63 100644 --- a/paper-api/src/generated/java/io/papermc/paper/registry/keys/DataComponentTypeKeys.java +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/DataComponentTypeKeys.java @@ -25,6 +25,13 @@ @NullMarked @GeneratedClass public final class DataComponentTypeKeys { + /** + * {@code minecraft:additional_trade_cost} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ADDITIONAL_TRADE_COST = create(key("additional_trade_cost")); + /** * {@code minecraft:attack_range} * @@ -130,6 +137,13 @@ public final class DataComponentTypeKeys { */ public static final TypedKey CAT_COLLAR = create(key("cat/collar")); + /** + * {@code minecraft:cat/sound_variant} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey CAT_SOUND_VARIANT = create(key("cat/sound_variant")); + /** * {@code minecraft:cat/variant} * @@ -144,6 +158,13 @@ public final class DataComponentTypeKeys { */ public static final TypedKey CHARGED_PROJECTILES = create(key("charged_projectiles")); + /** + * {@code minecraft:chicken/sound_variant} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey CHICKEN_SOUND_VARIANT = create(key("chicken/sound_variant")); + /** * {@code minecraft:chicken/variant} * @@ -172,6 +193,13 @@ public final class DataComponentTypeKeys { */ public static final TypedKey CONTAINER_LOOT = create(key("container_loot")); + /** + * {@code minecraft:cow/sound_variant} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey COW_SOUND_VARIANT = create(key("cow/sound_variant")); + /** * {@code minecraft:cow/variant} * @@ -242,6 +270,13 @@ public final class DataComponentTypeKeys { */ public static final TypedKey DEBUG_STICK_STATE = create(key("debug_stick_state")); + /** + * {@code minecraft:dye} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey DYE = create(key("dye")); + /** * {@code minecraft:dyed_color} * @@ -494,6 +529,13 @@ public final class DataComponentTypeKeys { */ public static final TypedKey PIERCING_WEAPON = create(key("piercing_weapon")); + /** + * {@code minecraft:pig/sound_variant} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey PIG_SOUND_VARIANT = create(key("pig/sound_variant")); + /** * {@code minecraft:pig/variant} * diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/ItemTypeKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/ItemTypeKeys.java index 33b92219df8b..edd4923254b8 100644 --- a/paper-api/src/generated/java/io/papermc/paper/registry/keys/ItemTypeKeys.java +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/ItemTypeKeys.java @@ -3959,6 +3959,13 @@ public final class ItemTypeKeys { */ public static final TypedKey GOLDEN_CHESTPLATE = create(key("golden_chestplate")); + /** + * {@code minecraft:golden_dandelion} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey GOLDEN_DANDELION = create(key("golden_dandelion")); + /** * {@code minecraft:golden_helmet} * diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/PigSoundVariantKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/PigSoundVariantKeys.java new file mode 100644 index 000000000000..e1801cfd8d33 --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/PigSoundVariantKeys.java @@ -0,0 +1,61 @@ +package io.papermc.paper.registry.keys; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import net.kyori.adventure.key.Key; +import org.bukkit.entity.Pig; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#PIG_SOUND_VARIANT}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class PigSoundVariantKeys { + /** + * {@code minecraft:big} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey BIG = create(key("big")); + + /** + * {@code minecraft:classic} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey CLASSIC = create(key("classic")); + + /** + * {@code minecraft:mini} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey MINI = create(key("mini")); + + private PigSoundVariantKeys() { + } + + /** + * Creates a typed key for {@link Pig.SoundVariant} in the registry {@code minecraft:pig_sound_variant}. + * + * @param key the value's key in the registry + * @return a new typed key + */ + public static TypedKey create(final Key key) { + return TypedKey.create(RegistryKey.PIG_SOUND_VARIANT, key); + } +} diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/SoundEventKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/SoundEventKeys.java index f12ba25ff870..876765f6a74e 100644 --- a/paper-api/src/generated/java/io/papermc/paper/registry/keys/SoundEventKeys.java +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/SoundEventKeys.java @@ -3707,6 +3707,34 @@ public final class SoundEventKeys { */ public static final TypedKey BLOCK_NOTE_BLOCK_SNARE = create(key("block.note_block.snare")); + /** + * {@code minecraft:block.note_block.trumpet} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey BLOCK_NOTE_BLOCK_TRUMPET = create(key("block.note_block.trumpet")); + + /** + * {@code minecraft:block.note_block.trumpet_exposed} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey BLOCK_NOTE_BLOCK_TRUMPET_EXPOSED = create(key("block.note_block.trumpet_exposed")); + + /** + * {@code minecraft:block.note_block.trumpet_oxidized} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey BLOCK_NOTE_BLOCK_TRUMPET_OXIDIZED = create(key("block.note_block.trumpet_oxidized")); + + /** + * {@code minecraft:block.note_block.trumpet_weathered} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey BLOCK_NOTE_BLOCK_TRUMPET_WEATHERED = create(key("block.note_block.trumpet_weathered")); + /** * {@code minecraft:block.note_block.xylophone} * @@ -6031,6 +6059,153 @@ public final class SoundEventKeys { */ public static final TypedKey ENTITY_AXOLOTL_SWIM = create(key("entity.axolotl.swim")); + /** + * {@code minecraft:entity.baby_cat.ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CAT_AMBIENT = create(key("entity.baby_cat.ambient")); + + /** + * {@code minecraft:entity.baby_cat.beg_for_food} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CAT_BEG_FOR_FOOD = create(key("entity.baby_cat.beg_for_food")); + + /** + * {@code minecraft:entity.baby_cat.death} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CAT_DEATH = create(key("entity.baby_cat.death")); + + /** + * {@code minecraft:entity.baby_cat.eat} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CAT_EAT = create(key("entity.baby_cat.eat")); + + /** + * {@code minecraft:entity.baby_cat.hiss} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CAT_HISS = create(key("entity.baby_cat.hiss")); + + /** + * {@code minecraft:entity.baby_cat.hurt} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CAT_HURT = create(key("entity.baby_cat.hurt")); + + /** + * {@code minecraft:entity.baby_cat.purr} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CAT_PURR = create(key("entity.baby_cat.purr")); + + /** + * {@code minecraft:entity.baby_cat.purreow} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CAT_PURREOW = create(key("entity.baby_cat.purreow")); + + /** + * {@code minecraft:entity.baby_cat.stray_ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CAT_STRAY_AMBIENT = create(key("entity.baby_cat.stray_ambient")); + + /** + * {@code minecraft:entity.baby_chicken.ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CHICKEN_AMBIENT = create(key("entity.baby_chicken.ambient")); + + /** + * {@code minecraft:entity.baby_chicken.death} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CHICKEN_DEATH = create(key("entity.baby_chicken.death")); + + /** + * {@code minecraft:entity.baby_chicken.hurt} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CHICKEN_HURT = create(key("entity.baby_chicken.hurt")); + + /** + * {@code minecraft:entity.baby_chicken.step} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_CHICKEN_STEP = create(key("entity.baby_chicken.step")); + + /** + * {@code minecraft:entity.baby_horse.ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_HORSE_AMBIENT = create(key("entity.baby_horse.ambient")); + + /** + * {@code minecraft:entity.baby_horse.angry} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_HORSE_ANGRY = create(key("entity.baby_horse.angry")); + + /** + * {@code minecraft:entity.baby_horse.breathe} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_HORSE_BREATHE = create(key("entity.baby_horse.breathe")); + + /** + * {@code minecraft:entity.baby_horse.death} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_HORSE_DEATH = create(key("entity.baby_horse.death")); + + /** + * {@code minecraft:entity.baby_horse.eat} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_HORSE_EAT = create(key("entity.baby_horse.eat")); + + /** + * {@code minecraft:entity.baby_horse.hurt} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_HORSE_HURT = create(key("entity.baby_horse.hurt")); + + /** + * {@code minecraft:entity.baby_horse.land} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_HORSE_LAND = create(key("entity.baby_horse.land")); + + /** + * {@code minecraft:entity.baby_horse.step} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_HORSE_STEP = create(key("entity.baby_horse.step")); + /** * {@code minecraft:entity.baby_nautilus.ambient} * @@ -6087,6 +6262,90 @@ public final class SoundEventKeys { */ public static final TypedKey ENTITY_BABY_NAUTILUS_SWIM = create(key("entity.baby_nautilus.swim")); + /** + * {@code minecraft:entity.baby_pig.ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_PIG_AMBIENT = create(key("entity.baby_pig.ambient")); + + /** + * {@code minecraft:entity.baby_pig.death} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_PIG_DEATH = create(key("entity.baby_pig.death")); + + /** + * {@code minecraft:entity.baby_pig.eat} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_PIG_EAT = create(key("entity.baby_pig.eat")); + + /** + * {@code minecraft:entity.baby_pig.hurt} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_PIG_HURT = create(key("entity.baby_pig.hurt")); + + /** + * {@code minecraft:entity.baby_pig.step} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_PIG_STEP = create(key("entity.baby_pig.step")); + + /** + * {@code minecraft:entity.baby_wolf.ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_WOLF_AMBIENT = create(key("entity.baby_wolf.ambient")); + + /** + * {@code minecraft:entity.baby_wolf.death} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_WOLF_DEATH = create(key("entity.baby_wolf.death")); + + /** + * {@code minecraft:entity.baby_wolf.growl} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_WOLF_GROWL = create(key("entity.baby_wolf.growl")); + + /** + * {@code minecraft:entity.baby_wolf.hurt} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_WOLF_HURT = create(key("entity.baby_wolf.hurt")); + + /** + * {@code minecraft:entity.baby_wolf.pant} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_WOLF_PANT = create(key("entity.baby_wolf.pant")); + + /** + * {@code minecraft:entity.baby_wolf.step} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_WOLF_STEP = create(key("entity.baby_wolf.step")); + + /** + * {@code minecraft:entity.baby_wolf.whine} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_BABY_WOLF_WHINE = create(key("entity.baby_wolf.whine")); + /** * {@code minecraft:entity.bat.ambient} * @@ -6556,6 +6815,69 @@ public final class SoundEventKeys { */ public static final TypedKey ENTITY_CAT_STRAY_AMBIENT = create(key("entity.cat.stray_ambient")); + /** + * {@code minecraft:entity.cat_royal.ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CAT_ROYAL_AMBIENT = create(key("entity.cat_royal.ambient")); + + /** + * {@code minecraft:entity.cat_royal.beg_for_food} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CAT_ROYAL_BEG_FOR_FOOD = create(key("entity.cat_royal.beg_for_food")); + + /** + * {@code minecraft:entity.cat_royal.death} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CAT_ROYAL_DEATH = create(key("entity.cat_royal.death")); + + /** + * {@code minecraft:entity.cat_royal.eat} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CAT_ROYAL_EAT = create(key("entity.cat_royal.eat")); + + /** + * {@code minecraft:entity.cat_royal.hiss} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CAT_ROYAL_HISS = create(key("entity.cat_royal.hiss")); + + /** + * {@code minecraft:entity.cat_royal.hurt} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CAT_ROYAL_HURT = create(key("entity.cat_royal.hurt")); + + /** + * {@code minecraft:entity.cat_royal.purr} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CAT_ROYAL_PURR = create(key("entity.cat_royal.purr")); + + /** + * {@code minecraft:entity.cat_royal.purreow} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CAT_ROYAL_PURREOW = create(key("entity.cat_royal.purreow")); + + /** + * {@code minecraft:entity.cat_royal.stray_ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CAT_ROYAL_STRAY_AMBIENT = create(key("entity.cat_royal.stray_ambient")); + /** * {@code minecraft:entity.chicken.ambient} * @@ -6591,6 +6913,27 @@ public final class SoundEventKeys { */ public static final TypedKey ENTITY_CHICKEN_STEP = create(key("entity.chicken.step")); + /** + * {@code minecraft:entity.chicken_picky.ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CHICKEN_PICKY_AMBIENT = create(key("entity.chicken_picky.ambient")); + + /** + * {@code minecraft:entity.chicken_picky.death} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CHICKEN_PICKY_DEATH = create(key("entity.chicken_picky.death")); + + /** + * {@code minecraft:entity.chicken_picky.hurt} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_CHICKEN_PICKY_HURT = create(key("entity.chicken_picky.hurt")); + /** * {@code minecraft:entity.cod.ambient} * @@ -6787,6 +7130,34 @@ public final class SoundEventKeys { */ public static final TypedKey ENTITY_COW_STEP = create(key("entity.cow.step")); + /** + * {@code minecraft:entity.cow_moody.ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_COW_MOODY_AMBIENT = create(key("entity.cow_moody.ambient")); + + /** + * {@code minecraft:entity.cow_moody.death} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_COW_MOODY_DEATH = create(key("entity.cow_moody.death")); + + /** + * {@code minecraft:entity.cow_moody.hurt} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_COW_MOODY_HURT = create(key("entity.cow_moody.hurt")); + + /** + * {@code minecraft:entity.cow_moody.step} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_COW_MOODY_STEP = create(key("entity.cow_moody.step")); + /** * {@code minecraft:entity.creaking.activate} * @@ -9223,6 +9594,13 @@ public final class SoundEventKeys { */ public static final TypedKey ENTITY_PIG_DEATH = create(key("entity.pig.death")); + /** + * {@code minecraft:entity.pig.eat} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_PIG_EAT = create(key("entity.pig.eat")); + /** * {@code minecraft:entity.pig.hurt} * @@ -9244,6 +9622,62 @@ public final class SoundEventKeys { */ public static final TypedKey ENTITY_PIG_STEP = create(key("entity.pig.step")); + /** + * {@code minecraft:entity.pig_big.ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_PIG_BIG_AMBIENT = create(key("entity.pig_big.ambient")); + + /** + * {@code minecraft:entity.pig_big.death} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_PIG_BIG_DEATH = create(key("entity.pig_big.death")); + + /** + * {@code minecraft:entity.pig_big.eat} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_PIG_BIG_EAT = create(key("entity.pig_big.eat")); + + /** + * {@code minecraft:entity.pig_big.hurt} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_PIG_BIG_HURT = create(key("entity.pig_big.hurt")); + + /** + * {@code minecraft:entity.pig_mini.ambient} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_PIG_MINI_AMBIENT = create(key("entity.pig_mini.ambient")); + + /** + * {@code minecraft:entity.pig_mini.death} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_PIG_MINI_DEATH = create(key("entity.pig_mini.death")); + + /** + * {@code minecraft:entity.pig_mini.eat} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_PIG_MINI_EAT = create(key("entity.pig_mini.eat")); + + /** + * {@code minecraft:entity.pig_mini.hurt} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ENTITY_PIG_MINI_HURT = create(key("entity.pig_mini.hurt")); + /** * {@code minecraft:entity.piglin.admiring_item} * @@ -12107,6 +12541,20 @@ public final class SoundEventKeys { */ public static final TypedKey ITEM_GOAT_HORN_SOUND_7 = create(key("item.goat_horn.sound.7")); + /** + * {@code minecraft:item.golden_dandelion.unuse} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ITEM_GOLDEN_DANDELION_UNUSE = create(key("item.golden_dandelion.unuse")); + + /** + * {@code minecraft:item.golden_dandelion.use} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey ITEM_GOLDEN_DANDELION_USE = create(key("item.golden_dandelion.use")); + /** * {@code minecraft:item.hoe.till} * diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/BlockTypeTagKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/BlockTypeTagKeys.java index 4f1e44542145..fba1331fc5bd 100644 --- a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/BlockTypeTagKeys.java +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/BlockTypeTagKeys.java @@ -116,13 +116,6 @@ public final class BlockTypeTagKeys { */ public static final TagKey BAMBOO_BLOCKS = create(key("bamboo_blocks")); - /** - * {@code #minecraft:bamboo_plantable_on} - * - * @apiNote This field is version-dependant and may be removed in future Minecraft versions - */ - public static final TagKey BAMBOO_PLANTABLE_ON = create(key("bamboo_plantable_on")); - /** * {@code #minecraft:banners} * @@ -194,11 +187,18 @@ public final class BlockTypeTagKeys { public static final TagKey BEEHIVES = create(key("beehives")); /** - * {@code #minecraft:big_dripleaf_placeable} + * {@code #minecraft:beneath_bamboo_podzol_replaceable} * * @apiNote This field is version-dependant and may be removed in future Minecraft versions */ - public static final TagKey BIG_DRIPLEAF_PLACEABLE = create(key("big_dripleaf_placeable")); + public static final TagKey BENEATH_BAMBOO_PODZOL_REPLACEABLE = create(key("beneath_bamboo_podzol_replaceable")); + + /** + * {@code #minecraft:beneath_tree_podzol_replaceable} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey BENEATH_TREE_PODZOL_REPLACEABLE = create(key("beneath_tree_podzol_replaceable")); /** * {@code #minecraft:birch_logs} @@ -263,6 +263,34 @@ public final class BlockTypeTagKeys { */ public static final TagKey CANDLES = create(key("candles")); + /** + * {@code #minecraft:cannot_replace_below_tree_trunk} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey CANNOT_REPLACE_BELOW_TREE_TRUNK = create(key("cannot_replace_below_tree_trunk")); + + /** + * {@code #minecraft:cannot_support_kelp} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey CANNOT_SUPPORT_KELP = create(key("cannot_support_kelp")); + + /** + * {@code #minecraft:cannot_support_seagrass} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey CANNOT_SUPPORT_SEAGRASS = create(key("cannot_support_seagrass")); + + /** + * {@code #minecraft:cannot_support_snow_layer} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey CANNOT_SUPPORT_SNOW_LAYER = create(key("cannot_support_snow_layer")); + /** * {@code #minecraft:cauldrons} * @@ -481,25 +509,32 @@ public final class BlockTypeTagKeys { public static final TagKey DRIPSTONE_REPLACEABLE_BLOCKS = create(key("dripstone_replaceable_blocks")); /** - * {@code #minecraft:dry_vegetation_may_place_on} + * {@code #minecraft:edible_for_sheep} * * @apiNote This field is version-dependant and may be removed in future Minecraft versions */ - public static final TagKey DRY_VEGETATION_MAY_PLACE_ON = create(key("dry_vegetation_may_place_on")); + public static final TagKey EDIBLE_FOR_SHEEP = create(key("edible_for_sheep")); /** - * {@code #minecraft:edible_for_sheep} + * {@code #minecraft:emerald_ores} * * @apiNote This field is version-dependant and may be removed in future Minecraft versions */ - public static final TagKey EDIBLE_FOR_SHEEP = create(key("edible_for_sheep")); + public static final TagKey EMERALD_ORES = create(key("emerald_ores")); /** - * {@code #minecraft:emerald_ores} + * {@code #minecraft:enables_bubble_column_drag_down} * * @apiNote This field is version-dependant and may be removed in future Minecraft versions */ - public static final TagKey EMERALD_ORES = create(key("emerald_ores")); + public static final TagKey ENABLES_BUBBLE_COLUMN_DRAG_DOWN = create(key("enables_bubble_column_drag_down")); + + /** + * {@code #minecraft:enables_bubble_column_push_up} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey ENABLES_BUBBLE_COLUMN_PUSH_UP = create(key("enables_bubble_column_push_up")); /** * {@code #minecraft:enchantment_power_provider} @@ -571,6 +606,13 @@ public final class BlockTypeTagKeys { */ public static final TagKey FLOWERS = create(key("flowers")); + /** + * {@code #minecraft:forest_rock_can_place_on} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey FOREST_ROCK_CAN_PLACE_ON = create(key("forest_rock_can_place_on")); + /** * {@code #minecraft:foxes_spawnable_on} * @@ -613,6 +655,20 @@ public final class BlockTypeTagKeys { */ public static final TagKey GOLD_ORES = create(key("gold_ores")); + /** + * {@code #minecraft:grass_blocks} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey GRASS_BLOCKS = create(key("grass_blocks")); + + /** + * {@code #minecraft:grows_crops} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey GROWS_CROPS = create(key("grows_crops")); + /** * {@code #minecraft:guarded_by_piglins} * @@ -634,6 +690,20 @@ public final class BlockTypeTagKeys { */ public static final TagKey HOGLIN_REPELLENTS = create(key("hoglin_repellents")); + /** + * {@code #minecraft:huge_brown_mushroom_can_place_on} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey HUGE_BROWN_MUSHROOM_CAN_PLACE_ON = create(key("huge_brown_mushroom_can_place_on")); + + /** + * {@code #minecraft:huge_red_mushroom_can_place_on} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey HUGE_RED_MUSHROOM_CAN_PLACE_ON = create(key("huge_red_mushroom_can_place_on")); + /** * {@code #minecraft:ice} * @@ -641,6 +711,13 @@ public final class BlockTypeTagKeys { */ public static final TagKey ICE = create(key("ice")); + /** + * {@code #minecraft:ice_spike_replaceable} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey ICE_SPIKE_REPLACEABLE = create(key("ice_spike_replaceable")); + /** * {@code #minecraft:impermeable} * @@ -872,6 +949,13 @@ public final class BlockTypeTagKeys { */ public static final TagKey MOOSHROOMS_SPAWNABLE_ON = create(key("mooshrooms_spawnable_on")); + /** + * {@code #minecraft:moss_blocks} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey MOSS_BLOCKS = create(key("moss_blocks")); + /** * {@code #minecraft:moss_replaceable} * @@ -880,11 +964,11 @@ public final class BlockTypeTagKeys { public static final TagKey MOSS_REPLACEABLE = create(key("moss_replaceable")); /** - * {@code #minecraft:mushroom_grow_block} + * {@code #minecraft:mud} * * @apiNote This field is version-dependant and may be removed in future Minecraft versions */ - public static final TagKey MUSHROOM_GROW_BLOCK = create(key("mushroom_grow_block")); + public static final TagKey MUD = create(key("mud")); /** * {@code #minecraft:needs_diamond_tool} @@ -935,6 +1019,13 @@ public final class BlockTypeTagKeys { */ public static final TagKey OCCLUDES_VIBRATION_SIGNALS = create(key("occludes_vibration_signals")); + /** + * {@code #minecraft:overrides_mushroom_light_requirement} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey OVERRIDES_MUSHROOM_LIGHT_REQUIREMENT = create(key("overrides_mushroom_light_requirement")); + /** * {@code #minecraft:overworld_carver_replaceables} * @@ -1005,6 +1096,13 @@ public final class BlockTypeTagKeys { */ public static final TagKey PREVENT_MOB_SPAWNING_INSIDE = create(key("prevent_mob_spawning_inside")); + /** + * {@code #minecraft:prevents_nearby_leaf_decay} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey PREVENTS_NEARBY_LEAF_DECAY = create(key("prevents_nearby_leaf_decay")); + /** * {@code #minecraft:rabbits_spawnable_on} * @@ -1096,13 +1194,6 @@ public final class BlockTypeTagKeys { */ public static final TagKey SLABS = create(key("slabs")); - /** - * {@code #minecraft:small_dripleaf_placeable} - * - * @apiNote This field is version-dependant and may be removed in future Minecraft versions - */ - public static final TagKey SMALL_DRIPLEAF_PLACEABLE = create(key("small_dripleaf_placeable")); - /** * {@code #minecraft:small_flowers} * @@ -1145,20 +1236,6 @@ public final class BlockTypeTagKeys { */ public static final TagKey SNOW = create(key("snow")); - /** - * {@code #minecraft:snow_layer_can_survive_on} - * - * @apiNote This field is version-dependant and may be removed in future Minecraft versions - */ - public static final TagKey SNOW_LAYER_CAN_SURVIVE_ON = create(key("snow_layer_can_survive_on")); - - /** - * {@code #minecraft:snow_layer_cannot_survive_on} - * - * @apiNote This field is version-dependant and may be removed in future Minecraft versions - */ - public static final TagKey SNOW_LAYER_CANNOT_SURVIVE_ON = create(key("snow_layer_cannot_survive_on")); - /** * {@code #minecraft:soul_fire_base_blocks} * @@ -1229,6 +1306,237 @@ public final class BlockTypeTagKeys { */ public static final TagKey STRIDER_WARM_BLOCKS = create(key("strider_warm_blocks")); + /** + * {@code #minecraft:substrate_overworld} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUBSTRATE_OVERWORLD = create(key("substrate_overworld")); + + /** + * {@code #minecraft:support_override_cactus_flower} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORT_OVERRIDE_CACTUS_FLOWER = create(key("support_override_cactus_flower")); + + /** + * {@code #minecraft:support_override_snow_layer} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORT_OVERRIDE_SNOW_LAYER = create(key("support_override_snow_layer")); + + /** + * {@code #minecraft:supports_azalea} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_AZALEA = create(key("supports_azalea")); + + /** + * {@code #minecraft:supports_bamboo} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_BAMBOO = create(key("supports_bamboo")); + + /** + * {@code #minecraft:supports_big_dripleaf} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_BIG_DRIPLEAF = create(key("supports_big_dripleaf")); + + /** + * {@code #minecraft:supports_cactus} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_CACTUS = create(key("supports_cactus")); + + /** + * {@code #minecraft:supports_chorus_flower} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_CHORUS_FLOWER = create(key("supports_chorus_flower")); + + /** + * {@code #minecraft:supports_chorus_plant} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_CHORUS_PLANT = create(key("supports_chorus_plant")); + + /** + * {@code #minecraft:supports_cocoa} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_COCOA = create(key("supports_cocoa")); + + /** + * {@code #minecraft:supports_crimson_fungus} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_CRIMSON_FUNGUS = create(key("supports_crimson_fungus")); + + /** + * {@code #minecraft:supports_crimson_roots} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_CRIMSON_ROOTS = create(key("supports_crimson_roots")); + + /** + * {@code #minecraft:supports_crops} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_CROPS = create(key("supports_crops")); + + /** + * {@code #minecraft:supports_dry_vegetation} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_DRY_VEGETATION = create(key("supports_dry_vegetation")); + + /** + * {@code #minecraft:supports_frogspawn} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_FROGSPAWN = create(key("supports_frogspawn")); + + /** + * {@code #minecraft:supports_hanging_mangrove_propagule} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_HANGING_MANGROVE_PROPAGULE = create(key("supports_hanging_mangrove_propagule")); + + /** + * {@code #minecraft:supports_lily_pad} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_LILY_PAD = create(key("supports_lily_pad")); + + /** + * {@code #minecraft:supports_mangrove_propagule} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_MANGROVE_PROPAGULE = create(key("supports_mangrove_propagule")); + + /** + * {@code #minecraft:supports_melon_stem} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_MELON_STEM = create(key("supports_melon_stem")); + + /** + * {@code #minecraft:supports_melon_stem_fruit} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_MELON_STEM_FRUIT = create(key("supports_melon_stem_fruit")); + + /** + * {@code #minecraft:supports_nether_sprouts} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_NETHER_SPROUTS = create(key("supports_nether_sprouts")); + + /** + * {@code #minecraft:supports_nether_wart} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_NETHER_WART = create(key("supports_nether_wart")); + + /** + * {@code #minecraft:supports_pumpkin_stem} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_PUMPKIN_STEM = create(key("supports_pumpkin_stem")); + + /** + * {@code #minecraft:supports_pumpkin_stem_fruit} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_PUMPKIN_STEM_FRUIT = create(key("supports_pumpkin_stem_fruit")); + + /** + * {@code #minecraft:supports_small_dripleaf} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_SMALL_DRIPLEAF = create(key("supports_small_dripleaf")); + + /** + * {@code #minecraft:supports_stem_crops} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_STEM_CROPS = create(key("supports_stem_crops")); + + /** + * {@code #minecraft:supports_stem_fruit} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_STEM_FRUIT = create(key("supports_stem_fruit")); + + /** + * {@code #minecraft:supports_sugar_cane} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_SUGAR_CANE = create(key("supports_sugar_cane")); + + /** + * {@code #minecraft:supports_sugar_cane_adjacently} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_SUGAR_CANE_ADJACENTLY = create(key("supports_sugar_cane_adjacently")); + + /** + * {@code #minecraft:supports_vegetation} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_VEGETATION = create(key("supports_vegetation")); + + /** + * {@code #minecraft:supports_warped_fungus} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_WARPED_FUNGUS = create(key("supports_warped_fungus")); + + /** + * {@code #minecraft:supports_warped_roots} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_WARPED_ROOTS = create(key("supports_warped_roots")); + + /** + * {@code #minecraft:supports_wither_rose} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_WITHER_ROSE = create(key("supports_wither_rose")); + /** * {@code #minecraft:sword_efficient} * diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/EnchantmentTagKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/EnchantmentTagKeys.java index ecd7ef06057f..b15d2c420c76 100644 --- a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/EnchantmentTagKeys.java +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/EnchantmentTagKeys.java @@ -183,15 +183,6 @@ public final class EnchantmentTagKeys { @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) public static final TagKey TRADES_DESERT_COMMON = create(key("trades/desert_common")); - /** - * {@code #minecraft:trades/desert_special} - * - * @apiNote This field is version-dependant and may be removed in future Minecraft versions - */ - @ApiStatus.Experimental - @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) - public static final TagKey TRADES_DESERT_SPECIAL = create(key("trades/desert_special")); - /** * {@code #minecraft:trades/jungle_common} * @@ -201,15 +192,6 @@ public final class EnchantmentTagKeys { @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) public static final TagKey TRADES_JUNGLE_COMMON = create(key("trades/jungle_common")); - /** - * {@code #minecraft:trades/jungle_special} - * - * @apiNote This field is version-dependant and may be removed in future Minecraft versions - */ - @ApiStatus.Experimental - @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) - public static final TagKey TRADES_JUNGLE_SPECIAL = create(key("trades/jungle_special")); - /** * {@code #minecraft:trades/plains_common} * @@ -219,15 +201,6 @@ public final class EnchantmentTagKeys { @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) public static final TagKey TRADES_PLAINS_COMMON = create(key("trades/plains_common")); - /** - * {@code #minecraft:trades/plains_special} - * - * @apiNote This field is version-dependant and may be removed in future Minecraft versions - */ - @ApiStatus.Experimental - @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) - public static final TagKey TRADES_PLAINS_SPECIAL = create(key("trades/plains_special")); - /** * {@code #minecraft:trades/savanna_common} * @@ -237,15 +210,6 @@ public final class EnchantmentTagKeys { @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) public static final TagKey TRADES_SAVANNA_COMMON = create(key("trades/savanna_common")); - /** - * {@code #minecraft:trades/savanna_special} - * - * @apiNote This field is version-dependant and may be removed in future Minecraft versions - */ - @ApiStatus.Experimental - @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) - public static final TagKey TRADES_SAVANNA_SPECIAL = create(key("trades/savanna_special")); - /** * {@code #minecraft:trades/snow_common} * @@ -255,15 +219,6 @@ public final class EnchantmentTagKeys { @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) public static final TagKey TRADES_SNOW_COMMON = create(key("trades/snow_common")); - /** - * {@code #minecraft:trades/snow_special} - * - * @apiNote This field is version-dependant and may be removed in future Minecraft versions - */ - @ApiStatus.Experimental - @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) - public static final TagKey TRADES_SNOW_SPECIAL = create(key("trades/snow_special")); - /** * {@code #minecraft:trades/swamp_common} * @@ -273,15 +228,6 @@ public final class EnchantmentTagKeys { @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) public static final TagKey TRADES_SWAMP_COMMON = create(key("trades/swamp_common")); - /** - * {@code #minecraft:trades/swamp_special} - * - * @apiNote This field is version-dependant and may be removed in future Minecraft versions - */ - @ApiStatus.Experimental - @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) - public static final TagKey TRADES_SWAMP_SPECIAL = create(key("trades/swamp_special")); - /** * {@code #minecraft:trades/taiga_common} * @@ -291,15 +237,6 @@ public final class EnchantmentTagKeys { @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) public static final TagKey TRADES_TAIGA_COMMON = create(key("trades/taiga_common")); - /** - * {@code #minecraft:trades/taiga_special} - * - * @apiNote This field is version-dependant and may be removed in future Minecraft versions - */ - @ApiStatus.Experimental - @MinecraftExperimental(MinecraftExperimental.Requires.TRADE_REBALANCE) - public static final TagKey TRADES_TAIGA_SPECIAL = create(key("trades/taiga_special")); - /** * {@code #minecraft:treasure} * diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/EntityTypeTagKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/EntityTypeTagKeys.java index f24cd0efc90f..e1cd2c06e838 100644 --- a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/EntityTypeTagKeys.java +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/EntityTypeTagKeys.java @@ -144,6 +144,13 @@ public final class EntityTypeTagKeys { */ public static final TagKey CANDIDATE_FOR_IRON_GOLEM_GIFT = create(key("candidate_for_iron_golem_gift")); + /** + * {@code #minecraft:cannot_be_age_locked} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey CANNOT_BE_AGE_LOCKED = create(key("cannot_be_age_locked")); + /** * {@code #minecraft:cannot_be_pushed_onto_boats} * diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/FluidTagKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/FluidTagKeys.java index 5b38f4d91c57..a9fc24a6eb7c 100644 --- a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/FluidTagKeys.java +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/FluidTagKeys.java @@ -25,6 +25,13 @@ @NullMarked @GeneratedClass public final class FluidTagKeys { + /** + * {@code #minecraft:bubble_column_can_occupy} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey BUBBLE_COLUMN_CAN_OCCUPY = create(key("bubble_column_can_occupy")); + /** * {@code #minecraft:lava} * @@ -32,6 +39,27 @@ public final class FluidTagKeys { */ public static final TagKey LAVA = create(key("lava")); + /** + * {@code #minecraft:supports_frogspawn} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_FROGSPAWN = create(key("supports_frogspawn")); + + /** + * {@code #minecraft:supports_lily_pad} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_LILY_PAD = create(key("supports_lily_pad")); + + /** + * {@code #minecraft:supports_sugar_cane_adjacently} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SUPPORTS_SUGAR_CANE_ADJACENTLY = create(key("supports_sugar_cane_adjacently")); + /** * {@code #minecraft:water} * diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/ItemTypeTagKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/ItemTypeTagKeys.java index 40202e9e23ad..1a73196592a0 100644 --- a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/ItemTypeTagKeys.java +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/ItemTypeTagKeys.java @@ -186,6 +186,13 @@ public final class ItemTypeTagKeys { */ public static final TagKey CANDLES = create(key("candles")); + /** + * {@code #minecraft:cat_collar_dyes} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey CAT_COLLAR_DYES = create(key("cat_collar_dyes")); + /** * {@code #minecraft:cat_food} * @@ -193,6 +200,13 @@ public final class ItemTypeTagKeys { */ public static final TagKey CAT_FOOD = create(key("cat_food")); + /** + * {@code #minecraft:cauldron_can_remove_dye} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey CAULDRON_CAN_REMOVE_DYE = create(key("cauldron_can_remove_dye")); + /** * {@code #minecraft:chains} * @@ -397,11 +411,11 @@ public final class ItemTypeTagKeys { public static final TagKey DUPLICATES_ALLAYS = create(key("duplicates_allays")); /** - * {@code #minecraft:dyeable} + * {@code #minecraft:dyes} * * @apiNote This field is version-dependant and may be removed in future Minecraft versions */ - public static final TagKey DYEABLE = create(key("dyeable")); + public static final TagKey DYES = create(key("dyes")); /** * {@code #minecraft:eggs} @@ -655,6 +669,13 @@ public final class ItemTypeTagKeys { */ public static final TagKey GOLD_TOOL_MATERIALS = create(key("gold_tool_materials")); + /** + * {@code #minecraft:grass_blocks} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey GRASS_BLOCKS = create(key("grass_blocks")); + /** * {@code #minecraft:hanging_signs} * @@ -816,6 +837,20 @@ public final class ItemTypeTagKeys { */ public static final TagKey LOGS_THAT_BURN = create(key("logs_that_burn")); + /** + * {@code #minecraft:loom_dyes} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey LOOM_DYES = create(key("loom_dyes")); + + /** + * {@code #minecraft:loom_patterns} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey LOOM_PATTERNS = create(key("loom_patterns")); + /** * {@code #minecraft:mangrove_logs} * @@ -837,6 +872,27 @@ public final class ItemTypeTagKeys { */ public static final TagKey MEAT = create(key("meat")); + /** + * {@code #minecraft:metal_nuggets} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey METAL_NUGGETS = create(key("metal_nuggets")); + + /** + * {@code #minecraft:moss_blocks} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey MOSS_BLOCKS = create(key("moss_blocks")); + + /** + * {@code #minecraft:mud} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey MUD = create(key("mud")); + /** * {@code #minecraft:nautilus_bucket_food} * @@ -1320,6 +1376,13 @@ public final class ItemTypeTagKeys { */ public static final TagKey WITHER_SKELETON_DISLIKED_WEAPONS = create(key("wither_skeleton_disliked_weapons")); + /** + * {@code #minecraft:wolf_collar_dyes} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey WOLF_COLLAR_DYES = create(key("wolf_collar_dyes")); + /** * {@code #minecraft:wolf_food} * diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/PotionTypeTagKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/PotionTypeTagKeys.java new file mode 100644 index 000000000000..fd20b7a6e394 --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/PotionTypeTagKeys.java @@ -0,0 +1,47 @@ +package io.papermc.paper.registry.keys.tags; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.tag.TagKey; +import net.kyori.adventure.key.Key; +import org.bukkit.potion.PotionType; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla tag keys for {@link RegistryKey#POTION}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class PotionTypeTagKeys { + /** + * {@code #minecraft:tradeable} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey TRADEABLE = create(key("tradeable")); + + private PotionTypeTagKeys() { + } + + /** + * Creates a tag key for {@link PotionType} in the registry {@code minecraft:potion}. + * + * @param key the tag key's key + * @return a new tag key + */ + public static TagKey create(final Key key) { + return TagKey.create(RegistryKey.POTION, key); + } +} diff --git a/paper-api/src/main/java/com/destroystokyo/paper/ParticleBuilder.java b/paper-api/src/main/java/com/destroystokyo/paper/ParticleBuilder.java index 6df8b48cb703..f82255cea258 100644 --- a/paper-api/src/main/java/com/destroystokyo/paper/ParticleBuilder.java +++ b/paper-api/src/main/java/com/destroystokyo/paper/ParticleBuilder.java @@ -165,8 +165,7 @@ public ParticleBuilder receivers(final int radius, final boolean byDistance) { throw new IllegalStateException("Please set location first"); } this.receivers = Lists.newArrayList(); - for (final Player nearbyPlayer : this.location.getWorld() - .getNearbyPlayers(this.location, radius, radius, radius)) { + for (final Player nearbyPlayer : this.location.getWorld().getNearbyPlayers(this.location, radius, radius, radius)) { final Location loc = nearbyPlayer.getLocation(); final double x = NumberConversions.square(this.location.getX() - loc.getX()); final double y = NumberConversions.square(this.location.getY() - loc.getY()); diff --git a/paper-api/src/main/java/com/destroystokyo/paper/entity/villager/Reputation.java b/paper-api/src/main/java/com/destroystokyo/paper/entity/villager/Reputation.java index cbbf70507c2d..50bca6f96d72 100644 --- a/paper-api/src/main/java/com/destroystokyo/paper/entity/villager/Reputation.java +++ b/paper-api/src/main/java/com/destroystokyo/paper/entity/villager/Reputation.java @@ -18,7 +18,7 @@ public Reputation() { } public Reputation(final Map reputation) { - Preconditions.checkNotNull(reputation, "reputation cannot be null"); + Preconditions.checkArgument(reputation != null, "reputation cannot be null"); this.reputation = reputation; } @@ -29,7 +29,7 @@ public Reputation(final Map reputation) { * @return The value of the {@link ReputationType type}. */ public int getReputation(final ReputationType type) { - Preconditions.checkNotNull(type, "the reputation type cannot be null"); + Preconditions.checkArgument(type != null, "type cannot be null"); return this.reputation.getOrDefault(type, 0); } @@ -40,7 +40,7 @@ public int getReputation(final ReputationType type) { * @param value The value of the {@link ReputationType type}. */ public void setReputation(final ReputationType type, final int value) { - Preconditions.checkNotNull(type, "the reputation type cannot be null"); + Preconditions.checkArgument(type != null, "type cannot be null"); this.reputation.put(type, value); } diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java index 1fcdc62cff07..de9f62cd3eae 100644 --- a/paper-api/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/DataComponentTypes.java @@ -45,7 +45,7 @@ import io.papermc.paper.datacomponent.item.WritableBookContent; import io.papermc.paper.datacomponent.item.WrittenBookContent; import io.papermc.paper.item.MapPostProcessing; -import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.registry.set.RegistryKeySet; import java.util.List; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; @@ -239,8 +239,9 @@ public final class DataComponentTypes { * @see #ENCHANTMENTS */ public static final DataComponentType.Valued STORED_ENCHANTMENTS = valued("stored_enchantments"); + public static final DataComponentType.Valued DYE = valued("dye"); /** - * Represents a color applied to a dyeable item (in the {@link io.papermc.paper.registry.keys.tags.ItemTypeTagKeys#DYEABLE} item tag). + * Represents a color applied to a dyeable item. */ public static final DataComponentType.Valued DYED_COLOR = valued("dyed_color"); /** @@ -306,7 +307,7 @@ public final class DataComponentTypes { */ public static final DataComponentType.Valued OMINOUS_BOTTLE_AMPLIFIER = valued("ominous_bottle_amplifier"); public static final DataComponentType.Valued JUKEBOX_PLAYABLE = valued("jukebox_playable"); - public static final DataComponentType.Valued> PROVIDES_BANNER_PATTERNS = valued("provides_banner_patterns"); + public static final DataComponentType.Valued> PROVIDES_BANNER_PATTERNS = valued("provides_banner_patterns"); /** * List of recipes that should be unlocked when using the Knowledge Book item. */ @@ -378,9 +379,11 @@ public final class DataComponentTypes { public static final DataComponentType.Valued MOOSHROOM_VARIANT = valued("mooshroom/variant"); public static final DataComponentType.Valued RABBIT_VARIANT = valued("rabbit/variant"); public static final DataComponentType.Valued PIG_VARIANT = valued("pig/variant"); + public static final DataComponentType.Valued PIG_SOUND_VARIANT = valued("pig/sound_variant"); public static final DataComponentType.Valued COW_VARIANT = valued("cow/variant"); + public static final DataComponentType.Valued COW_SOUND_VARIANT = valued("cow/sound_variant"); public static final DataComponentType.Valued CHICKEN_VARIANT = valued("chicken/variant"); - // This is a eitherholder? Why specifically the chicken?? Oh wait this is prolly for chicken egg cause legacy item loading + public static final DataComponentType.Valued CHICKEN_SOUND_VARIANT = valued("chicken/sound_variant"); public static final DataComponentType.Valued FROG_VARIANT = valued("frog/variant"); public static final DataComponentType.Valued HORSE_VARIANT = valued("horse/variant"); public static final DataComponentType.Valued PAINTING_VARIANT = valued("painting/variant"); @@ -388,6 +391,7 @@ public final class DataComponentTypes { public static final DataComponentType.Valued AXOLOTL_VARIANT = valued("axolotl/variant"); public static final DataComponentType.Valued ZOMBIE_NAUTILUS_VARIANT = valued("zombie_nautilus/variant"); public static final DataComponentType.Valued CAT_VARIANT = valued("cat/variant"); + public static final DataComponentType.Valued CAT_SOUND_VARIANT = valued("cat/sound_variant"); public static final DataComponentType.Valued CAT_COLLAR = valued("cat/collar"); public static final DataComponentType.Valued SHEEP_COLOR = valued("sheep/color"); public static final DataComponentType.Valued SHULKER_COLOR = valued("shulker/color"); diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/BlocksAttacks.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/BlocksAttacks.java index e36616f7ab30..6d94fed93ede 100644 --- a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/BlocksAttacks.java +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/BlocksAttacks.java @@ -3,7 +3,7 @@ import io.papermc.paper.datacomponent.DataComponentBuilder; import io.papermc.paper.datacomponent.item.blocksattacks.DamageReduction; import io.papermc.paper.datacomponent.item.blocksattacks.ItemDamageFunction; -import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.registry.set.RegistryKeySet; import java.util.List; import net.kyori.adventure.key.Key; import org.bukkit.damage.DamageType; @@ -68,7 +68,7 @@ static Builder blocksAttacks() { * @return a damage type tag key, or null if there is no such tag key */ @Contract(pure = true) - @Nullable TagKey bypassedBy(); + @Nullable RegistryKeySet bypassedBy(); /** * Gets the key sound to play when an attack is successfully blocked. @@ -109,7 +109,7 @@ interface Builder extends DataComponentBuilder { Builder itemDamage(ItemDamageFunction function); @Contract(value = "_ -> this", mutates = "this") - Builder bypassedBy(@Nullable TagKey bypassedBy); + Builder bypassedBy(@Nullable RegistryKeySet bypassedBy); @Contract(value = "_ -> this", mutates = "this") Builder blockSound(@Nullable Key sound); diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java index 384098ea55d7..79e03746c5c3 100644 --- a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/DamageResistant.java @@ -1,6 +1,6 @@ package io.papermc.paper.datacomponent.item; -import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.registry.set.RegistryKeySet; import org.bukkit.damage.DamageType; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; @@ -16,15 +16,15 @@ public interface DamageResistant { @Contract(value = "_ -> new", pure = true) - static DamageResistant damageResistant(final TagKey types) { + static DamageResistant damageResistant(final RegistryKeySet types) { return ItemComponentTypesBridge.bridge().damageResistant(types); } /** * The types that this damage type is invincible to. * - * @return the key of the tag holding the respective damage types. + * @return the registry key set holding the respective damage types. */ @Contract(value = "-> new", pure = true) - TagKey types(); + RegistryKeySet types(); } diff --git a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java index 9a35d98bc101..c74366daa72f 100644 --- a/paper-api/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java +++ b/paper-api/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridge.java @@ -100,7 +100,7 @@ static ItemComponentTypesBridge bridge() { UseCooldown.Builder useCooldown(final float seconds); - DamageResistant damageResistant(TagKey types); + DamageResistant damageResistant(RegistryKeySet types); Enchantable enchantable(int level); diff --git a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerArmSwingEvent.java b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerArmSwingEvent.java index 84dfb8da90c5..bc8f99a852d2 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerArmSwingEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerArmSwingEvent.java @@ -8,7 +8,7 @@ import org.jspecify.annotations.NullMarked; @NullMarked -public class PlayerArmSwingEvent extends PlayerAnimationEvent { +public class PlayerArmSwingEvent extends PlayerAnimationEvent { // todo redundant with parent? private final EquipmentSlot equipmentSlot; diff --git a/paper-api/src/main/java/io/papermc/paper/event/world/WorldGameRuleChangeEvent.java b/paper-api/src/main/java/io/papermc/paper/event/world/WorldGameRuleChangeEvent.java index c44006faf327..73569979b732 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/world/WorldGameRuleChangeEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/world/WorldGameRuleChangeEvent.java @@ -11,7 +11,8 @@ import org.jspecify.annotations.Nullable; /** - * Called when a world's gamerule is changed, either by command or by api. + * Called when a world's gamerule is changed, either by command, world options menu, or by api. + * @see Modifying game rules - Minecraft wiki */ @NullMarked public class WorldGameRuleChangeEvent extends WorldEvent implements Cancellable { diff --git a/paper-api/src/main/java/io/papermc/paper/event/world/border/WorldBorderBoundsChangeEvent.java b/paper-api/src/main/java/io/papermc/paper/event/world/border/WorldBorderBoundsChangeEvent.java index 906f3912f9ba..25c663b6852f 100644 --- a/paper-api/src/main/java/io/papermc/paper/event/world/border/WorldBorderBoundsChangeEvent.java +++ b/paper-api/src/main/java/io/papermc/paper/event/world/border/WorldBorderBoundsChangeEvent.java @@ -68,7 +68,7 @@ public double getNewSize() { * @param newSize the new size */ public void setNewSize(final double newSize) { - this.newSize = Math.clamp(newSize, 1.0D, this.worldBorder.getMaxSize()); + this.newSize = Math.clamp(newSize, 1.0, this.worldBorder.getMaxSize()); } /** diff --git a/paper-api/src/main/java/io/papermc/paper/item/MapPostProcessing.java b/paper-api/src/main/java/io/papermc/paper/item/MapPostProcessing.java index 5843768d0be2..7071cacd66ef 100644 --- a/paper-api/src/main/java/io/papermc/paper/item/MapPostProcessing.java +++ b/paper-api/src/main/java/io/papermc/paper/item/MapPostProcessing.java @@ -1,6 +1,8 @@ package io.papermc.paper.item; public enum MapPostProcessing { + // Start generate - MapPostProcessing LOCK, - SCALE + SCALE; + // End generate - MapPostProcessing } diff --git a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java index dcee3d88f5b7..de4ff2303c5a 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java @@ -201,6 +201,11 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { * @see io.papermc.paper.registry.keys.CatVariantKeys */ RegistryKey CAT_VARIANT = create("cat_variant"); + /** + * Data-driven registry for cat sound variants. + * @see io.papermc.paper.registry.keys.CatSoundVariantKeys + */ + RegistryKey CAT_SOUND_VARIANT = create("cat_sound_variant"); /** * Data-driven registry for frog variants. * @see io.papermc.paper.registry.keys.FrogVariantKeys @@ -211,16 +216,31 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { * @see io.papermc.paper.registry.keys.ChickenVariantKeys */ RegistryKey CHICKEN_VARIANT = create("chicken_variant"); + /** + * Data-driven registry for chicken sound variants. + * @see io.papermc.paper.registry.keys.ChickenSoundVariantKeys + */ + RegistryKey CHICKEN_SOUND_VARIANT = create("chicken_sound_variant"); /** * Data-driven registry for cow variants. * @see io.papermc.paper.registry.keys.CowVariantKeys */ RegistryKey COW_VARIANT = create("cow_variant"); + /** + * Data-driven registry for cow sound variants. + * @see io.papermc.paper.registry.keys.CowSoundVariantKeys + */ + RegistryKey COW_SOUND_VARIANT = create("cow_sound_variant"); /** * Data-driven registry for pig variants. * @see io.papermc.paper.registry.keys.PigVariantKeys */ RegistryKey PIG_VARIANT = create("pig_variant"); + /** + * Data-driven registry for pig sound variants. + * @see io.papermc.paper.registry.keys.PigSoundVariantKeys + */ + RegistryKey PIG_SOUND_VARIANT = create("pig_sound_variant"); /** * Data-driven registry for zombie nautilus variants. * @see io.papermc.paper.registry.keys.ZombieNautilusVariantKeys diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/CatTypeRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/CatTypeRegistryEntry.java index b818d61cd66d..c6b1c4a9066d 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/data/CatTypeRegistryEntry.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/CatTypeRegistryEntry.java @@ -20,12 +20,20 @@ public interface CatTypeRegistryEntry { */ ClientTextureAsset clientTextureAsset(); + /** + * Provides the client texture asset of the cat type for baby cats. + * + * @return the baby client texture asset. + */ + ClientTextureAsset babyClientTextureAsset(); + /** * A mutable builder for the {@link CatTypeRegistryEntry} plugins may change in applicable registry events. *

* The following values are required for each builder: *

    *
  • {@link #clientTextureAsset(ClientTextureAsset)}
  • + *
  • {@link #babyClientTextureAsset(ClientTextureAsset)}
  • *
*/ @ApiStatus.Experimental @@ -41,5 +49,15 @@ interface Builder extends CatTypeRegistryEntry, RegistryBuilder { */ @Contract(value = "_ -> this", mutates = "this") Builder clientTextureAsset(ClientTextureAsset clientTextureAsset); + + /** + * Sets the client texture asset of the cat type for baby cats. + * + * @param babyClientTextureAsset the baby client texture asset. + * @return this builder instance. + * @see CatTypeRegistryEntry#babyClientTextureAsset() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder babyClientTextureAsset(ClientTextureAsset babyClientTextureAsset); } } diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/ChickenVariantRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/ChickenVariantRegistryEntry.java index 104c01ddc353..f70d189203d5 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/data/ChickenVariantRegistryEntry.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/ChickenVariantRegistryEntry.java @@ -35,6 +35,13 @@ enum Model { */ ClientTextureAsset clientTextureAsset(); + /** + * Provides the client texture asset of the baby chicken variant, which represents the texture to use. + * + * @return the baby client texture asset. + */ + ClientTextureAsset babyClientTextureAsset(); + /** * Provides the model of the chicken variant. * @@ -48,6 +55,7 @@ enum Model { * The following values are required for each builder: *
    *
  • {@link #clientTextureAsset(ClientTextureAsset)}
  • + *
  • {@link #babyClientTextureAsset(ClientTextureAsset)}
  • *
  • {@link #model(Model)}
  • *
*/ @@ -65,6 +73,16 @@ interface Builder extends ChickenVariantRegistryEntry, RegistryBuilder *
  • {@link #clientTextureAsset(ClientTextureAsset)}
  • + *
  • {@link #babyClientTextureAsset(ClientTextureAsset)}
  • *
  • {@link #model(Model)}
  • * */ @@ -70,6 +78,16 @@ interface Builder extends CowVariantRegistryEntry, RegistryBuilder @Contract(value = "_ -> this", mutates = "this") Builder clientTextureAsset(ClientTextureAsset clientTextureAsset); + /** + * Sets the client texture asset of the baby cow variant, which is the location of the texture to use. + * + * @param babyClientTextureAsset the baby client texture asset. + * @return this builder instance. + * @see CowVariantRegistryEntry#babyClientTextureAsset() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder babyClientTextureAsset(ClientTextureAsset babyClientTextureAsset); + /** * Sets the model to use for this cow variant. * diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/PigVariantRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/PigVariantRegistryEntry.java index 02c043e835d7..0b482be5990f 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/data/PigVariantRegistryEntry.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/PigVariantRegistryEntry.java @@ -35,6 +35,13 @@ enum Model { */ ClientTextureAsset clientTextureAsset(); + /** + * Provides the client texture asset of the baby pig variant, which represents the texture to use. + * + * @return the baby client texture asset. + */ + ClientTextureAsset babyClientTextureAsset(); + /** * Provides the model of the pig variant. * @@ -48,6 +55,7 @@ enum Model { * The following values are required for each builder: *
      *
    • {@link #clientTextureAsset(ClientTextureAsset)}
    • + *
    • {@link #babyClientTextureAsset(ClientTextureAsset)}
    • *
    • {@link #model(Model)}
    • *
    */ @@ -65,6 +73,16 @@ interface Builder extends PigVariantRegistryEntry, RegistryBuilder @Contract(value = "_ -> this", mutates = "this") Builder clientTextureAsset(ClientTextureAsset clientTextureAsset); + /** + * Sets the client texture asset of the baby pig variant, which is the location of the texture to use. + * + * @param babyClientTextureAsset the baby client texture asset. + * @return this builder instance. + * @see PigVariantRegistryEntry#babyClientTextureAsset() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder babyClientTextureAsset(ClientTextureAsset babyClientTextureAsset); + /** * Sets the model to use for this pig variant. * diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/WolfVariantRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/WolfVariantRegistryEntry.java index 786b8bd822c7..921c533dd226 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/data/WolfVariantRegistryEntry.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/WolfVariantRegistryEntry.java @@ -34,6 +34,27 @@ public interface WolfVariantRegistryEntry { */ ClientTextureAsset tameClientTextureAsset(); + /** + * Provides the client texture asset of the wolf variant for when it is an angry baby. + * + * @return the baby angry client texture asset. + */ + ClientTextureAsset babyAngryClientTextureAsset(); + + /** + * Provides the client texture asset of the wolf variant for when it is a wild baby. + * + * @return the baby wild client texture asset. + */ + ClientTextureAsset babyWildClientTextureAsset(); + + /** + * Provides the client texture asset of the wolf variant for when it is a tame baby. + * + * @return the baby tame client texture asset. + */ + ClientTextureAsset babyTameClientTextureAsset(); + /** * A mutable builder for the {@link WolfVariantRegistryEntry} plugins may change in applicable registry events. *

    @@ -42,6 +63,9 @@ public interface WolfVariantRegistryEntry { *

  • {@link #angryClientTextureAsset(ClientTextureAsset)}
  • *
  • {@link #wildClientTextureAsset(ClientTextureAsset)}
  • *
  • {@link #tameClientTextureAsset(ClientTextureAsset)}
  • + *
  • {@link #babyAngryClientTextureAsset(ClientTextureAsset)}
  • + *
  • {@link #babyWildClientTextureAsset(ClientTextureAsset)}
  • + *
  • {@link #babyTameClientTextureAsset(ClientTextureAsset)}
  • * */ @ApiStatus.Experimental @@ -77,5 +101,35 @@ interface Builder extends WolfVariantRegistryEntry, RegistryBuilder BY_DATA = Maps.newHashMap(); - - // Paper - remove ctor (the server still uses the byte magic value) - private Instrument(final int type, final Sound sound) { - this.type = (byte) type; + Instrument(final Sound sound) { this.sound = sound; } @@ -128,10 +138,11 @@ public Sound getSound() { /** * @return The type ID of this instrument. + * @deprecated use {@link #ordinal()}, there's no meaning to this id */ - @org.jetbrains.annotations.ApiStatus.Internal // Paper + @Deprecated(since = "26.1") public byte getType() { - return this.type; + return (byte) this.ordinal(); } /** @@ -139,16 +150,11 @@ public byte getType() { * * @param type The type ID * @return The instrument + * @deprecated type is just the ordinal of the enum, no meaning in the game */ - @org.jetbrains.annotations.ApiStatus.Internal // Paper @Nullable + @Deprecated(since = "26.1") public static Instrument getByType(final byte type) { - return BY_DATA.get(type); - } - - static { - for (Instrument instrument : Instrument.values()) { - BY_DATA.put(instrument.getType(), instrument); - } + return ArrayUtils.get(values(), type); } } diff --git a/paper-api/src/main/java/org/bukkit/Material.java b/paper-api/src/main/java/org/bukkit/Material.java index 8e663e2edc0f..3e0df7b1abcd 100644 --- a/paper-api/src/main/java/org/bukkit/Material.java +++ b/paper-api/src/main/java/org/bukkit/Material.java @@ -1057,6 +1057,7 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla GLOWSTONE(-1), GOLD_BLOCK(-1), GOLD_ORE(-1), + GOLDEN_DANDELION(-1), GRANITE(-1), GRANITE_SLAB(-1, Slab.class), GRANITE_STAIRS(-1, Stairs.class), @@ -1425,6 +1426,7 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla POTTED_DEAD_BUSH(-1), POTTED_FERN(-1), POTTED_FLOWERING_AZALEA_BUSH(-1), + POTTED_GOLDEN_DANDELION(-1), POTTED_JUNGLE_SAPLING(-1), POTTED_LILY_OF_THE_VALLEY(-1), POTTED_MANGROVE_PROPAGULE(-1), diff --git a/paper-api/src/main/java/org/bukkit/NamespacedKey.java b/paper-api/src/main/java/org/bukkit/NamespacedKey.java index 57d95e22db8c..b53028683bfa 100644 --- a/paper-api/src/main/java/org/bukkit/NamespacedKey.java +++ b/paper-api/src/main/java/org/bukkit/NamespacedKey.java @@ -81,7 +81,7 @@ public NamespacedKey(@NotNull Plugin plugin, @NotNull String key) { private void validate() { Preconditions.checkArgument(this.namespace.length() + 1 + this.key.length() <= Short.MAX_VALUE, "NamespacedKey must be less than 32768 characters"); - checkError("[a-z0-9_-.]", "namespace", this.namespace, Key.checkNamespace(this.namespace)); + checkError("[a-z0-9_-.]", "namespace", this.namespace, Key.checkNamespace(this.namespace)); // note: for now we will pretend ".." is a valid namespace like adventure to not break conversion checkError("[a-z0-9_-./]", "key", this.key, Key.checkValue(this.key)); } diff --git a/paper-api/src/main/java/org/bukkit/Particle.java b/paper-api/src/main/java/org/bukkit/Particle.java index 4f1ebb38e5d7..a2e40e0185f8 100644 --- a/paper-api/src/main/java/org/bukkit/Particle.java +++ b/paper-api/src/main/java/org/bukkit/Particle.java @@ -175,6 +175,8 @@ public enum Particle implements Keyed { */ BLOCK_MARKER("block_marker", BlockData.class), COPPER_FIRE_FLAME("copper_fire_flame"), + PAUSE_MOB_GROWTH("pause_mob_growth"), + RESET_MOB_GROWTH("reset_mob_growth"), ; private final NamespacedKey key; diff --git a/paper-api/src/main/java/org/bukkit/Server.java b/paper-api/src/main/java/org/bukkit/Server.java index 0ec885b97d65..ccd936efb12d 100644 --- a/paper-api/src/main/java/org/bukkit/Server.java +++ b/paper-api/src/main/java/org/bukkit/Server.java @@ -5,6 +5,7 @@ import java.io.File; import java.io.Serializable; import java.net.InetAddress; +import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -1565,7 +1566,6 @@ default int broadcast(net.kyori.adventure.text.@NotNull Component message) { @NotNull public ConsoleCommandSender getConsoleSender(); - // Paper start /** * Creates a special {@link CommandSender} which redirects command feedback (in the form of chat messages) to the * specified listener. The returned sender will have the same effective permissions as {@link #getConsoleSender()}. @@ -1575,16 +1575,31 @@ default int broadcast(net.kyori.adventure.text.@NotNull Component message) { */ @NotNull public CommandSender createCommandSender(final @NotNull java.util.function.Consumer feedback); - // Paper end /** - * Gets the folder that contains all of the various {@link World}s. + * Gets the folder that contains {@link #getLevelDirectory()}. + * + *

    This is usually the server's current working directory + * but can be overridden using command line flags (i.e. {@code --universe} or {@code --world-container}).

    * - * @return folder that contains all worlds + * @return folder that contains the level directory */ + @ApiStatus.Obsolete @NotNull public File getWorldContainer(); + /** + * Gets the level directory. + * + *

    This is the {@code ./world} directory in a fresh default server. Contains player data, dimensions, datapacks, + * and other world data.

    + * + * @return the level directory + */ + @ApiStatus.Experimental + @NotNull + Path getLevelDirectory(); + /** * Gets every player that has ever played on this server. *

    diff --git a/paper-api/src/main/java/org/bukkit/Sound.java b/paper-api/src/main/java/org/bukkit/Sound.java index 1ba2f4969419..c98aaff3d0c8 100644 --- a/paper-api/src/main/java/org/bukkit/Sound.java +++ b/paper-api/src/main/java/org/bukkit/Sound.java @@ -1075,6 +1075,14 @@ public interface Sound extends OldEnum, Keyed, net.kyori.adventure.sound. Sound BLOCK_NOTE_BLOCK_SNARE = getSound("block.note_block.snare"); + Sound BLOCK_NOTE_BLOCK_TRUMPET = getSound("block.note_block.trumpet"); + + Sound BLOCK_NOTE_BLOCK_TRUMPET_EXPOSED = getSound("block.note_block.trumpet_exposed"); + + Sound BLOCK_NOTE_BLOCK_TRUMPET_OXIDIZED = getSound("block.note_block.trumpet_oxidized"); + + Sound BLOCK_NOTE_BLOCK_TRUMPET_WEATHERED = getSound("block.note_block.trumpet_weathered"); + Sound BLOCK_NOTE_BLOCK_XYLOPHONE = getSound("block.note_block.xylophone"); Sound BLOCK_NYLIUM_BREAK = getSound("block.nylium.break"); @@ -1739,6 +1747,48 @@ public interface Sound extends OldEnum, Keyed, net.kyori.adventure.sound. Sound ENTITY_AXOLOTL_SWIM = getSound("entity.axolotl.swim"); + Sound ENTITY_BABY_CAT_AMBIENT = getSound("entity.baby_cat.ambient"); + + Sound ENTITY_BABY_CAT_BEG_FOR_FOOD = getSound("entity.baby_cat.beg_for_food"); + + Sound ENTITY_BABY_CAT_DEATH = getSound("entity.baby_cat.death"); + + Sound ENTITY_BABY_CAT_EAT = getSound("entity.baby_cat.eat"); + + Sound ENTITY_BABY_CAT_HISS = getSound("entity.baby_cat.hiss"); + + Sound ENTITY_BABY_CAT_HURT = getSound("entity.baby_cat.hurt"); + + Sound ENTITY_BABY_CAT_PURR = getSound("entity.baby_cat.purr"); + + Sound ENTITY_BABY_CAT_PURREOW = getSound("entity.baby_cat.purreow"); + + Sound ENTITY_BABY_CAT_STRAY_AMBIENT = getSound("entity.baby_cat.stray_ambient"); + + Sound ENTITY_BABY_CHICKEN_AMBIENT = getSound("entity.baby_chicken.ambient"); + + Sound ENTITY_BABY_CHICKEN_DEATH = getSound("entity.baby_chicken.death"); + + Sound ENTITY_BABY_CHICKEN_HURT = getSound("entity.baby_chicken.hurt"); + + Sound ENTITY_BABY_CHICKEN_STEP = getSound("entity.baby_chicken.step"); + + Sound ENTITY_BABY_HORSE_AMBIENT = getSound("entity.baby_horse.ambient"); + + Sound ENTITY_BABY_HORSE_ANGRY = getSound("entity.baby_horse.angry"); + + Sound ENTITY_BABY_HORSE_BREATHE = getSound("entity.baby_horse.breathe"); + + Sound ENTITY_BABY_HORSE_DEATH = getSound("entity.baby_horse.death"); + + Sound ENTITY_BABY_HORSE_EAT = getSound("entity.baby_horse.eat"); + + Sound ENTITY_BABY_HORSE_HURT = getSound("entity.baby_horse.hurt"); + + Sound ENTITY_BABY_HORSE_LAND = getSound("entity.baby_horse.land"); + + Sound ENTITY_BABY_HORSE_STEP = getSound("entity.baby_horse.step"); + Sound ENTITY_BABY_NAUTILUS_AMBIENT = getSound("entity.baby_nautilus.ambient"); Sound ENTITY_BABY_NAUTILUS_AMBIENT_LAND = getSound("entity.baby_nautilus.ambient_land"); @@ -1755,6 +1805,30 @@ public interface Sound extends OldEnum, Keyed, net.kyori.adventure.sound. Sound ENTITY_BABY_NAUTILUS_SWIM = getSound("entity.baby_nautilus.swim"); + Sound ENTITY_BABY_PIG_AMBIENT = getSound("entity.baby_pig.ambient"); + + Sound ENTITY_BABY_PIG_DEATH = getSound("entity.baby_pig.death"); + + Sound ENTITY_BABY_PIG_EAT = getSound("entity.baby_pig.eat"); + + Sound ENTITY_BABY_PIG_HURT = getSound("entity.baby_pig.hurt"); + + Sound ENTITY_BABY_PIG_STEP = getSound("entity.baby_pig.step"); + + Sound ENTITY_BABY_WOLF_AMBIENT = getSound("entity.baby_wolf.ambient"); + + Sound ENTITY_BABY_WOLF_DEATH = getSound("entity.baby_wolf.death"); + + Sound ENTITY_BABY_WOLF_GROWL = getSound("entity.baby_wolf.growl"); + + Sound ENTITY_BABY_WOLF_HURT = getSound("entity.baby_wolf.hurt"); + + Sound ENTITY_BABY_WOLF_PANT = getSound("entity.baby_wolf.pant"); + + Sound ENTITY_BABY_WOLF_STEP = getSound("entity.baby_wolf.step"); + + Sound ENTITY_BABY_WOLF_WHINE = getSound("entity.baby_wolf.whine"); + Sound ENTITY_BAT_AMBIENT = getSound("entity.bat.ambient"); Sound ENTITY_BAT_DEATH = getSound("entity.bat.death"); @@ -1889,6 +1963,24 @@ public interface Sound extends OldEnum, Keyed, net.kyori.adventure.sound. Sound ENTITY_CAT_STRAY_AMBIENT = getSound("entity.cat.stray_ambient"); + Sound ENTITY_CAT_ROYAL_AMBIENT = getSound("entity.cat_royal.ambient"); + + Sound ENTITY_CAT_ROYAL_BEG_FOR_FOOD = getSound("entity.cat_royal.beg_for_food"); + + Sound ENTITY_CAT_ROYAL_DEATH = getSound("entity.cat_royal.death"); + + Sound ENTITY_CAT_ROYAL_EAT = getSound("entity.cat_royal.eat"); + + Sound ENTITY_CAT_ROYAL_HISS = getSound("entity.cat_royal.hiss"); + + Sound ENTITY_CAT_ROYAL_HURT = getSound("entity.cat_royal.hurt"); + + Sound ENTITY_CAT_ROYAL_PURR = getSound("entity.cat_royal.purr"); + + Sound ENTITY_CAT_ROYAL_PURREOW = getSound("entity.cat_royal.purreow"); + + Sound ENTITY_CAT_ROYAL_STRAY_AMBIENT = getSound("entity.cat_royal.stray_ambient"); + Sound ENTITY_CHICKEN_AMBIENT = getSound("entity.chicken.ambient"); Sound ENTITY_CHICKEN_DEATH = getSound("entity.chicken.death"); @@ -1899,6 +1991,12 @@ public interface Sound extends OldEnum, Keyed, net.kyori.adventure.sound. Sound ENTITY_CHICKEN_STEP = getSound("entity.chicken.step"); + Sound ENTITY_CHICKEN_PICKY_AMBIENT = getSound("entity.chicken_picky.ambient"); + + Sound ENTITY_CHICKEN_PICKY_DEATH = getSound("entity.chicken_picky.death"); + + Sound ENTITY_CHICKEN_PICKY_HURT = getSound("entity.chicken_picky.hurt"); + Sound ENTITY_COD_AMBIENT = getSound("entity.cod.ambient"); Sound ENTITY_COD_DEATH = getSound("entity.cod.death"); @@ -1955,6 +2053,14 @@ public interface Sound extends OldEnum, Keyed, net.kyori.adventure.sound. Sound ENTITY_COW_STEP = getSound("entity.cow.step"); + Sound ENTITY_COW_MOODY_AMBIENT = getSound("entity.cow_moody.ambient"); + + Sound ENTITY_COW_MOODY_DEATH = getSound("entity.cow_moody.death"); + + Sound ENTITY_COW_MOODY_HURT = getSound("entity.cow_moody.hurt"); + + Sound ENTITY_COW_MOODY_STEP = getSound("entity.cow_moody.step"); + Sound ENTITY_CREAKING_ACTIVATE = getSound("entity.creaking.activate"); Sound ENTITY_CREAKING_AMBIENT = getSound("entity.creaking.ambient"); @@ -2651,12 +2757,30 @@ public interface Sound extends OldEnum, Keyed, net.kyori.adventure.sound. Sound ENTITY_PIG_DEATH = getSound("entity.pig.death"); + Sound ENTITY_PIG_EAT = getSound("entity.pig.eat"); + Sound ENTITY_PIG_HURT = getSound("entity.pig.hurt"); Sound ENTITY_PIG_SADDLE = getSound("entity.pig.saddle"); Sound ENTITY_PIG_STEP = getSound("entity.pig.step"); + Sound ENTITY_PIG_BIG_AMBIENT = getSound("entity.pig_big.ambient"); + + Sound ENTITY_PIG_BIG_DEATH = getSound("entity.pig_big.death"); + + Sound ENTITY_PIG_BIG_EAT = getSound("entity.pig_big.eat"); + + Sound ENTITY_PIG_BIG_HURT = getSound("entity.pig_big.hurt"); + + Sound ENTITY_PIG_MINI_AMBIENT = getSound("entity.pig_mini.ambient"); + + Sound ENTITY_PIG_MINI_DEATH = getSound("entity.pig_mini.death"); + + Sound ENTITY_PIG_MINI_EAT = getSound("entity.pig_mini.eat"); + + Sound ENTITY_PIG_MINI_HURT = getSound("entity.pig_mini.hurt"); + Sound ENTITY_PIGLIN_ADMIRING_ITEM = getSound("entity.piglin.admiring_item"); Sound ENTITY_PIGLIN_AMBIENT = getSound("entity.piglin.ambient"); @@ -3475,6 +3599,10 @@ public interface Sound extends OldEnum, Keyed, net.kyori.adventure.sound. Sound ITEM_GOAT_HORN_SOUND_7 = getSound("item.goat_horn.sound.7"); + Sound ITEM_GOLDEN_DANDELION_UNUSE = getSound("item.golden_dandelion.unuse"); + + Sound ITEM_GOLDEN_DANDELION_USE = getSound("item.golden_dandelion.use"); + Sound ITEM_HOE_TILL = getSound("item.hoe.till"); Sound ITEM_HONEY_BOTTLE_DRINK = getSound("item.honey_bottle.drink"); diff --git a/paper-api/src/main/java/org/bukkit/Tag.java b/paper-api/src/main/java/org/bukkit/Tag.java index 5fbb1ccaeb42..7e2f386ce22f 100644 --- a/paper-api/src/main/java/org/bukkit/Tag.java +++ b/paper-api/src/main/java/org/bukkit/Tag.java @@ -48,8 +48,6 @@ public interface Tag extends Keyed { Tag BAMBOO_BLOCKS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("bamboo_blocks"), Material.class); - Tag BAMBOO_PLANTABLE_ON = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("bamboo_plantable_on"), Material.class); - Tag BANNERS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("banners"), Material.class); Tag BARS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("bars"), Material.class); @@ -70,7 +68,9 @@ public interface Tag extends Keyed { Tag BEEHIVES = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("beehives"), Material.class); - Tag BIG_DRIPLEAF_PLACEABLE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("big_dripleaf_placeable"), Material.class); + Tag BENEATH_BAMBOO_PODZOL_REPLACEABLE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("beneath_bamboo_podzol_replaceable"), Material.class); + + Tag BENEATH_TREE_PODZOL_REPLACEABLE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("beneath_tree_podzol_replaceable"), Material.class); Tag BIRCH_LOGS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("birch_logs"), Material.class); @@ -90,6 +90,14 @@ public interface Tag extends Keyed { Tag CANDLES = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("candles"), Material.class); + Tag CANNOT_REPLACE_BELOW_TREE_TRUNK = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("cannot_replace_below_tree_trunk"), Material.class); + + Tag CANNOT_SUPPORT_KELP = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("cannot_support_kelp"), Material.class); + + Tag CANNOT_SUPPORT_SEAGRASS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("cannot_support_seagrass"), Material.class); + + Tag CANNOT_SUPPORT_SNOW_LAYER = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("cannot_support_snow_layer"), Material.class); + Tag CAULDRONS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("cauldrons"), Material.class); Tag CAVE_VINES = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("cave_vines"), Material.class); @@ -152,12 +160,14 @@ public interface Tag extends Keyed { Tag DRIPSTONE_REPLACEABLE_BLOCKS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("dripstone_replaceable_blocks"), Material.class); - Tag DRY_VEGETATION_MAY_PLACE_ON = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("dry_vegetation_may_place_on"), Material.class); - Tag EDIBLE_FOR_SHEEP = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("edible_for_sheep"), Material.class); Tag EMERALD_ORES = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("emerald_ores"), Material.class); + Tag ENABLES_BUBBLE_COLUMN_DRAG_DOWN = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("enables_bubble_column_drag_down"), Material.class); + + Tag ENABLES_BUBBLE_COLUMN_PUSH_UP = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("enables_bubble_column_push_up"), Material.class); + Tag ENCHANTMENT_POWER_PROVIDER = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("enchantment_power_provider"), Material.class); Tag ENCHANTMENT_POWER_TRANSMITTER = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("enchantment_power_transmitter"), Material.class); @@ -178,6 +188,8 @@ public interface Tag extends Keyed { Tag FLOWERS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("flowers"), Material.class); + Tag FOREST_ROCK_CAN_PLACE_ON = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("forest_rock_can_place_on"), Material.class); + Tag FOXES_SPAWNABLE_ON = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("foxes_spawnable_on"), Material.class); Tag FROG_PREFER_JUMP_TO = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("frog_prefer_jump_to"), Material.class); @@ -190,14 +202,24 @@ public interface Tag extends Keyed { Tag GOLD_ORES = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("gold_ores"), Material.class); + Tag GRASS_BLOCKS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("grass_blocks"), Material.class); + + Tag GROWS_CROPS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("grows_crops"), Material.class); + Tag GUARDED_BY_PIGLINS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("guarded_by_piglins"), Material.class); Tag HAPPY_GHAST_AVOIDS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("happy_ghast_avoids"), Material.class); Tag HOGLIN_REPELLENTS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("hoglin_repellents"), Material.class); + Tag HUGE_BROWN_MUSHROOM_CAN_PLACE_ON = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("huge_brown_mushroom_can_place_on"), Material.class); + + Tag HUGE_RED_MUSHROOM_CAN_PLACE_ON = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("huge_red_mushroom_can_place_on"), Material.class); + Tag ICE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("ice"), Material.class); + Tag ICE_SPIKE_REPLACEABLE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("ice_spike_replaceable"), Material.class); + Tag IMPERMEABLE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("impermeable"), Material.class); Tag INCORRECT_FOR_COPPER_TOOL = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("incorrect_for_copper_tool"), Material.class); @@ -264,9 +286,11 @@ public interface Tag extends Keyed { Tag MOOSHROOMS_SPAWNABLE_ON = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("mooshrooms_spawnable_on"), Material.class); + Tag MOSS_BLOCKS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("moss_blocks"), Material.class); + Tag MOSS_REPLACEABLE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("moss_replaceable"), Material.class); - Tag MUSHROOM_GROW_BLOCK = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("mushroom_grow_block"), Material.class); + Tag MUD = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("mud"), Material.class); Tag NEEDS_DIAMOND_TOOL = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("needs_diamond_tool"), Material.class); @@ -282,6 +306,8 @@ public interface Tag extends Keyed { Tag OCCLUDES_VIBRATION_SIGNALS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("occludes_vibration_signals"), Material.class); + Tag OVERRIDES_MUSHROOM_LIGHT_REQUIREMENT = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("overrides_mushroom_light_requirement"), Material.class); + Tag OVERWORLD_CARVER_REPLACEABLES = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("overworld_carver_replaceables"), Material.class); Tag OVERWORLD_NATURAL_LOGS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("overworld_natural_logs"), Material.class); @@ -302,6 +328,8 @@ public interface Tag extends Keyed { Tag PREVENT_MOB_SPAWNING_INSIDE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("prevent_mob_spawning_inside"), Material.class); + Tag PREVENTS_NEARBY_LEAF_DECAY = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("prevents_nearby_leaf_decay"), Material.class); + Tag RABBITS_SPAWNABLE_ON = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("rabbits_spawnable_on"), Material.class); Tag RAILS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("rails"), Material.class); @@ -328,8 +356,6 @@ public interface Tag extends Keyed { Tag SLABS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("slabs"), Material.class); - Tag SMALL_DRIPLEAF_PLACEABLE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("small_dripleaf_placeable"), Material.class); - Tag SMALL_FLOWERS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("small_flowers"), Material.class); Tag SMELTS_TO_GLASS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("smelts_to_glass"), Material.class); @@ -342,10 +368,6 @@ public interface Tag extends Keyed { Tag SNOW = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("snow"), Material.class); - Tag SNOW_LAYER_CAN_SURVIVE_ON = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("snow_layer_can_survive_on"), Material.class); - - Tag SNOW_LAYER_CANNOT_SURVIVE_ON = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("snow_layer_cannot_survive_on"), Material.class); - Tag SOUL_FIRE_BASE_BLOCKS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("soul_fire_base_blocks"), Material.class); Tag SOUL_SPEED_BLOCKS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("soul_speed_blocks"), Material.class); @@ -366,6 +388,72 @@ public interface Tag extends Keyed { Tag STRIDER_WARM_BLOCKS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("strider_warm_blocks"), Material.class); + Tag SUBSTRATE_OVERWORLD = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("substrate_overworld"), Material.class); + + Tag SUPPORT_OVERRIDE_CACTUS_FLOWER = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("support_override_cactus_flower"), Material.class); + + Tag SUPPORT_OVERRIDE_SNOW_LAYER = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("support_override_snow_layer"), Material.class); + + Tag SUPPORTS_AZALEA = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_azalea"), Material.class); + + Tag SUPPORTS_BAMBOO = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_bamboo"), Material.class); + + Tag SUPPORTS_BIG_DRIPLEAF = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_big_dripleaf"), Material.class); + + Tag SUPPORTS_CACTUS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_cactus"), Material.class); + + Tag SUPPORTS_CHORUS_FLOWER = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_chorus_flower"), Material.class); + + Tag SUPPORTS_CHORUS_PLANT = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_chorus_plant"), Material.class); + + Tag SUPPORTS_COCOA = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_cocoa"), Material.class); + + Tag SUPPORTS_CRIMSON_FUNGUS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_crimson_fungus"), Material.class); + + Tag SUPPORTS_CRIMSON_ROOTS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_crimson_roots"), Material.class); + + Tag SUPPORTS_CROPS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_crops"), Material.class); + + Tag SUPPORTS_DRY_VEGETATION = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_dry_vegetation"), Material.class); + + Tag SUPPORTS_FROGSPAWN = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_frogspawn"), Material.class); + + Tag SUPPORTS_HANGING_MANGROVE_PROPAGULE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_hanging_mangrove_propagule"), Material.class); + + Tag SUPPORTS_LILY_PAD = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_lily_pad"), Material.class); + + Tag SUPPORTS_MANGROVE_PROPAGULE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_mangrove_propagule"), Material.class); + + Tag SUPPORTS_MELON_STEM = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_melon_stem"), Material.class); + + Tag SUPPORTS_MELON_STEM_FRUIT = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_melon_stem_fruit"), Material.class); + + Tag SUPPORTS_NETHER_SPROUTS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_nether_sprouts"), Material.class); + + Tag SUPPORTS_NETHER_WART = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_nether_wart"), Material.class); + + Tag SUPPORTS_PUMPKIN_STEM = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_pumpkin_stem"), Material.class); + + Tag SUPPORTS_PUMPKIN_STEM_FRUIT = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_pumpkin_stem_fruit"), Material.class); + + Tag SUPPORTS_SMALL_DRIPLEAF = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_small_dripleaf"), Material.class); + + Tag SUPPORTS_STEM_CROPS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_stem_crops"), Material.class); + + Tag SUPPORTS_STEM_FRUIT = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_stem_fruit"), Material.class); + + Tag SUPPORTS_SUGAR_CANE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_sugar_cane"), Material.class); + + Tag SUPPORTS_SUGAR_CANE_ADJACENTLY = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_sugar_cane_adjacently"), Material.class); + + Tag SUPPORTS_VEGETATION = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_vegetation"), Material.class); + + Tag SUPPORTS_WARPED_FUNGUS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_warped_fungus"), Material.class); + + Tag SUPPORTS_WARPED_ROOTS = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_warped_roots"), Material.class); + + Tag SUPPORTS_WITHER_ROSE = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("supports_wither_rose"), Material.class); + Tag SWORD_EFFICIENT = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("sword_efficient"), Material.class); Tag SWORD_INSTANTLY_MINES = Bukkit.getTag(REGISTRY_BLOCKS, NamespacedKey.minecraft("sword_instantly_mines"), Material.class); @@ -478,8 +566,12 @@ public interface Tag extends Keyed { Tag ITEMS_CANDLES = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("candles"), Material.class); + Tag ITEMS_CAT_COLLAR_DYES = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("cat_collar_dyes"), Material.class); + Tag ITEMS_CAT_FOOD = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("cat_food"), Material.class); + Tag ITEMS_CAULDRON_CAN_REMOVE_DYE = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("cauldron_can_remove_dye"), Material.class); + Tag ITEMS_CHAINS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("chains"), Material.class); Tag ITEMS_CHERRY_LOGS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("cherry_logs"), Material.class); @@ -538,7 +630,7 @@ public interface Tag extends Keyed { Tag ITEMS_DUPLICATES_ALLAYS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("duplicates_allays"), Material.class); - Tag ITEMS_DYEABLE = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("dyeable"), Material.class); + Tag ITEMS_DYES = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("dyes"), Material.class); Tag ITEMS_EGGS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("eggs"), Material.class); @@ -612,6 +704,8 @@ public interface Tag extends Keyed { Tag ITEMS_GOLD_TOOL_MATERIALS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("gold_tool_materials"), Material.class); + Tag ITEMS_GRASS_BLOCKS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("grass_blocks"), Material.class); + Tag ITEMS_HANGING_SIGNS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("hanging_signs"), Material.class); Tag ITEMS_HAPPY_GHAST_FOOD = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("happy_ghast_food"), Material.class); @@ -658,12 +752,22 @@ public interface Tag extends Keyed { Tag ITEMS_LOGS_THAT_BURN = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("logs_that_burn"), Material.class); + Tag ITEMS_LOOM_DYES = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("loom_dyes"), Material.class); + + Tag ITEMS_LOOM_PATTERNS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("loom_patterns"), Material.class); + Tag ITEMS_MANGROVE_LOGS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("mangrove_logs"), Material.class); Tag ITEMS_MAP_INVISIBILITY_EQUIPMENT = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("map_invisibility_equipment"), Material.class); Tag ITEMS_MEAT = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("meat"), Material.class); + Tag ITEMS_METAL_NUGGETS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("metal_nuggets"), Material.class); + + Tag ITEMS_MOSS_BLOCKS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("moss_blocks"), Material.class); + + Tag ITEMS_MUD = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("mud"), Material.class); + Tag ITEMS_NAUTILUS_BUCKET_FOOD = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("nautilus_bucket_food"), Material.class); Tag ITEMS_NAUTILUS_FOOD = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("nautilus_food"), Material.class); @@ -802,6 +906,8 @@ public interface Tag extends Keyed { Tag ITEMS_WITHER_SKELETON_DISLIKED_WEAPONS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("wither_skeleton_disliked_weapons"), Material.class); + Tag ITEMS_WOLF_COLLAR_DYES = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("wolf_collar_dyes"), Material.class); + Tag ITEMS_WOLF_FOOD = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("wolf_food"), Material.class); Tag ITEMS_WOODEN_BUTTONS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("wooden_buttons"), Material.class); @@ -830,8 +936,16 @@ public interface Tag extends Keyed { String REGISTRY_FLUIDS = "fluids"; + Tag FLUIDS_BUBBLE_COLUMN_CAN_OCCUPY = Bukkit.getTag(REGISTRY_FLUIDS, NamespacedKey.minecraft("bubble_column_can_occupy"), Fluid.class); + Tag FLUIDS_LAVA = Bukkit.getTag(REGISTRY_FLUIDS, NamespacedKey.minecraft("lava"), Fluid.class); + Tag FLUIDS_SUPPORTS_FROGSPAWN = Bukkit.getTag(REGISTRY_FLUIDS, NamespacedKey.minecraft("supports_frogspawn"), Fluid.class); + + Tag FLUIDS_SUPPORTS_LILY_PAD = Bukkit.getTag(REGISTRY_FLUIDS, NamespacedKey.minecraft("supports_lily_pad"), Fluid.class); + + Tag FLUIDS_SUPPORTS_SUGAR_CANE_ADJACENTLY = Bukkit.getTag(REGISTRY_FLUIDS, NamespacedKey.minecraft("supports_sugar_cane_adjacently"), Fluid.class); + Tag FLUIDS_WATER = Bukkit.getTag(REGISTRY_FLUIDS, NamespacedKey.minecraft("water"), Fluid.class); String REGISTRY_ENTITY_TYPES = "entity_types"; @@ -870,6 +984,8 @@ public interface Tag extends Keyed { Tag ENTITY_TYPES_CANDIDATE_FOR_IRON_GOLEM_GIFT = Bukkit.getTag(REGISTRY_ENTITY_TYPES, NamespacedKey.minecraft("candidate_for_iron_golem_gift"), EntityType.class); + Tag ENTITY_TYPES_CANNOT_BE_AGE_LOCKED = Bukkit.getTag(REGISTRY_ENTITY_TYPES, NamespacedKey.minecraft("cannot_be_age_locked"), EntityType.class); + Tag ENTITY_TYPES_CANNOT_BE_PUSHED_ONTO_BOATS = Bukkit.getTag(REGISTRY_ENTITY_TYPES, NamespacedKey.minecraft("cannot_be_pushed_onto_boats"), EntityType.class); Tag ENTITY_TYPES_DEFLECTS_PROJECTILES = Bukkit.getTag(REGISTRY_ENTITY_TYPES, NamespacedKey.minecraft("deflects_projectiles"), EntityType.class); @@ -940,18 +1056,54 @@ public interface Tag extends Keyed { Tag GAME_EVENT_WARDEN_CAN_LISTEN = Bukkit.getTag(REGISTRY_GAME_EVENTS, NamespacedKey.minecraft("warden_can_listen"), GameEvent.class); // End generate - Tag + // /** - * @deprecated {@link #WOOL_CARPETS}. + * @deprecated replaced by {@link #WOOL_CARPETS}. */ - @Deprecated(since = "1.19") + @Deprecated(since = "1.19", forRemoval = true) Tag CARPETS = WOOL_CARPETS; + /** + * @deprecated replaced by {@link #CANNOT_SUPPORT_SNOW_LAYER} + */ + @Deprecated(since = "26.1", forRemoval = true) + Tag SNOW_LAYER_CANNOT_SURVIVE_ON = CANNOT_SUPPORT_SNOW_LAYER; + /** + * @deprecated replaced by {@link #SUPPORT_OVERRIDE_SNOW_LAYER} + */ + @Deprecated(since = "26.1", forRemoval = true) + Tag SNOW_LAYER_CAN_SURVIVE_ON = SUPPORT_OVERRIDE_SNOW_LAYER; + /** + * @deprecated partially replaced by {@link #OVERRIDES_MUSHROOM_LIGHT_REQUIREMENT} + */ + @Deprecated(since = "26.1", forRemoval = true) + Tag MUSHROOM_GROW_BLOCK = OVERRIDES_MUSHROOM_LIGHT_REQUIREMENT; + /** + * @deprecated replaced by {@link #SUPPORTS_SMALL_DRIPLEAF} + */ + @Deprecated(since = "26.1", forRemoval = true) + Tag SMALL_DRIPLEAF_PLACEABLE = SUPPORTS_SMALL_DRIPLEAF; + /** + * @deprecated replaced by {@link #SUPPORTS_BIG_DRIPLEAF} + */ + @Deprecated(since = "26.1", forRemoval = true) + Tag BIG_DRIPLEAF_PLACEABLE = SUPPORTS_BIG_DRIPLEAF; + /** + * @deprecated replaced by {@link #SUPPORTS_BAMBOO} + */ + @Deprecated(since = "26.1", forRemoval = true) + Tag BAMBOO_PLANTABLE_ON = SUPPORTS_BAMBOO; + /** + * @deprecated replaced by {@link #SUPPORTS_DRY_VEGETATION} + */ + @Deprecated(since = "26.1", forRemoval = true) + Tag DRY_VEGETATION_MAY_PLACE_ON = SUPPORTS_DRY_VEGETATION; /** * Vanilla block tag representing all blocks which dead bushes may be placed on. * - * @deprecated partially replaced by {@link #DRY_VEGETATION_MAY_PLACE_ON} + * @deprecated partially replaced by {@link #SUPPORTS_DRY_VEGETATION} */ @Deprecated(since = "1.21.5", forRemoval = true) - Tag DEAD_BUSH_MAY_PLACE_ON = DRY_VEGETATION_MAY_PLACE_ON; + Tag DEAD_BUSH_MAY_PLACE_ON = SUPPORTS_DRY_VEGETATION; /** * @deprecated replaced by {@link #TRIGGERS_AMBIENT_DESERT_DRY_VEGETATION_BLOCK_SOUNDS} */ @@ -961,23 +1113,23 @@ public interface Tag extends Keyed { * Vanilla block tag representing all blocks that are replaceable by * dripstone. * - * @deprecated use {@link #DRIPSTONE_REPLACEABLE_BLOCKS} + * @deprecated replaced by {@link #DRIPSTONE_REPLACEABLE_BLOCKS} */ @Deprecated(since = "1.21.4", forRemoval = true) Tag DRIPSTONE_REPLACEABLE = DRIPSTONE_REPLACEABLE_BLOCKS; /** * Vanilla item tag representing all piglin food. * - * @deprecated use {@link #ITEMS_PIGLIN_FOOD} + * @deprecated replaced by {@link #ITEMS_PIGLIN_FOOD} */ - @Deprecated(since = "1.20.5") + @Deprecated(since = "1.20.5", forRemoval = true) Tag PIGLIN_FOOD = ITEMS_PIGLIN_FOOD; /** * Vanilla item tag representing all fox food. * - * @deprecated use {@link #ITEMS_FOX_FOOD} + * @deprecated replaced by {@link #ITEMS_FOX_FOOD} */ - @Deprecated(since = "1.20.5") + @Deprecated(since = "1.20.5", forRemoval = true) Tag FOX_FOOD = ITEMS_FOX_FOOD; /** * Vanilla item tag representing all furnace materials. @@ -989,7 +1141,7 @@ public interface Tag extends Keyed { /** * Vanilla item tag representing all items which modify note block sounds when placed on top. * - * @deprecated use {@link #ITEMS_NOTEBLOCK_TOP_INSTRUMENTS} + * @deprecated replaced by {@link #ITEMS_NOTEBLOCK_TOP_INSTRUMENTS} */ @Deprecated(since = "1.21.4", forRemoval = true) Tag ITEMS_NOTE_BLOCK_TOP_INSTRUMENTS = ITEMS_NOTEBLOCK_TOP_INSTRUMENTS; @@ -1005,16 +1157,18 @@ public interface Tag extends Keyed { /** * Vanilla item tag representing all items which tempt axolotls. * - * @deprecated use {@link #ITEMS_AXOLOTL_FOOD} + * @deprecated replaced by {@link #ITEMS_AXOLOTL_FOOD} */ - @Deprecated(since = "1.20.5") + @Deprecated(since = "1.20.5", forRemoval = true) Tag AXOLOTL_TEMPT_ITEMS = ITEMS_AXOLOTL_FOOD; /** * Vanilla tag representing entities which deflect arrows. - * @deprecated use {@link #ENTITY_TYPES_DEFLECTS_PROJECTILES} + * + * @deprecated replaced by {@link #ENTITY_TYPES_DEFLECTS_PROJECTILES} */ - @Deprecated(since = "1.20.5") + @Deprecated(since = "1.20.5", forRemoval = true) Tag ENTITY_TYPES_DEFLECTS_ARROWS = ENTITY_TYPES_DEFLECTS_PROJECTILES; + // /** * Returns whether or not this tag has an entry for the specified item. diff --git a/paper-api/src/main/java/org/bukkit/UnsafeValues.java b/paper-api/src/main/java/org/bukkit/UnsafeValues.java index 82d8febbc565..04bb829f956e 100644 --- a/paper-api/src/main/java/org/bukkit/UnsafeValues.java +++ b/paper-api/src/main/java/org/bukkit/UnsafeValues.java @@ -60,7 +60,7 @@ public interface UnsafeValues { int getDataVersion(); - ItemStack modifyItemStack(ItemStack stack, String arguments); + ItemStack modifyItemStack(ItemStack item, String components); void checkSupported(PluginDescriptionFile pdf) throws InvalidPluginException; diff --git a/paper-api/src/main/java/org/bukkit/World.java b/paper-api/src/main/java/org/bukkit/World.java index b202bdb84bae..e2b65c0d2d6b 100644 --- a/paper-api/src/main/java/org/bukkit/World.java +++ b/paper-api/src/main/java/org/bukkit/World.java @@ -1809,7 +1809,7 @@ default RayTraceResult rayTraceEntities(@NotNull Location start, @NotNull Vector */ @Nullable default RayTraceResult rayTraceEntities(@NotNull Location start, @NotNull Vector direction, double maxDistance, @Nullable Predicate filter) { - return this.rayTraceEntities(start, direction, maxDistance, 0.0D, filter); + return this.rayTraceEntities(start, direction, maxDistance, 0.0, filter); } /** @@ -2051,7 +2051,7 @@ default boolean setSpawnLocation(int x, int y, int z) { } /** - * Gets the relative in-game time of this world. + * Gets the relative in-game time of this world, or {@code 0} if this world does not have a world clock. *

    * The relative time is analogous to hours * 1000 * @@ -2076,7 +2076,7 @@ default boolean setSpawnLocation(int x, int y, int z) { public void setTime(long time); /** - * Gets the full in-game time on this world + * Gets the full in-game time on this world, or {@code 0} if this world does not have a world clock. * * @return The current absolute time * @see #getTime() Returns a relative time of this world @@ -2091,7 +2091,10 @@ default boolean setSpawnLocation(int x, int y, int z) { * * @param time The new absolute time to set this world to * @see #setTime(long) Sets the relative time of this world + * @deprecated all overworlds share the same world clock by default now + * @throws IllegalArgumentException if this world does not have a world clock (e.g. the nether) */ + @Deprecated // TODO world clock API with links to it public void setFullTime(long time); // Paper start @@ -4104,7 +4107,6 @@ default void spawnParticle(@NotNull Particle particle, double x, double y, d public void spawnParticle(@NotNull Particle particle, @Nullable List receivers, @Nullable Player source, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data, boolean force); // Paper end - /** * Spawns the particle (the number of times specified by count) * at the target location. The position of each particle will be diff --git a/paper-api/src/main/java/org/bukkit/WorldBorder.java b/paper-api/src/main/java/org/bukkit/WorldBorder.java index 10e69cab2068..0f2f0d1589e9 100644 --- a/paper-api/src/main/java/org/bukkit/WorldBorder.java +++ b/paper-api/src/main/java/org/bukkit/WorldBorder.java @@ -36,7 +36,7 @@ public interface WorldBorder { * * @param newSize The new size of the border. * - * @throws IllegalArgumentException if newSize is less than 1.0D or greater than {@link #getMaxSize()} + * @throws IllegalArgumentException if newSize is less than 1.0 or greater than {@link #getMaxSize()} * @see #changeSize(double, long) */ void setSize(double newSize); @@ -47,7 +47,7 @@ public interface WorldBorder { * @param newSize The new side length of the border. * @param ticks The time in ticks in which the border grows or shrinks from the previous size to that being set. * - * @throws IllegalArgumentException if newSize is less than 1.0D or greater than {@link #getMaxSize()} + * @throws IllegalArgumentException if newSize is less than 1.0 or greater than {@link #getMaxSize()} * @throws IllegalArgumentException if ticks is out of range * @see #setSize(double) */ @@ -64,7 +64,7 @@ public interface WorldBorder { */ @Deprecated(since = "1.21.11", forRemoval = true) default void setSize(double newSize, long seconds) { - this.changeSize(Math.clamp(newSize, 1.0D, this.getMaxSize()), Tick.tick().fromDuration(Duration.ofSeconds(seconds))); + this.changeSize(Math.clamp(newSize, 1.0, this.getMaxSize()), Tick.tick().fromDuration(Duration.ofSeconds(seconds))); } /** @@ -74,7 +74,7 @@ default void setSize(double newSize, long seconds) { * @param unit The time unit. * @param time The time in which the border grows or shrinks from the previous size to that being set. * - * @throws IllegalArgumentException if newSize is less than 1.0D or greater than {@link #getMaxSize()} + * @throws IllegalArgumentException if newSize is less than 1.0 or greater than {@link #getMaxSize()} * @throws IllegalArgumentException if time is out of range once converted in ticks * @deprecated Use {@link #changeSize(double, long)} instead */ diff --git a/paper-api/src/main/java/org/bukkit/WorldCreator.java b/paper-api/src/main/java/org/bukkit/WorldCreator.java index 4b312399eced..171bde1ab971 100644 --- a/paper-api/src/main/java/org/bukkit/WorldCreator.java +++ b/paper-api/src/main/java/org/bukkit/WorldCreator.java @@ -31,11 +31,10 @@ public class WorldCreator { * @param name Name of the world that will be created */ public WorldCreator(@NotNull String name) { - // Paper start - this(name, getWorldKey(name)); + this(name, defaultWorldKey(name)); } - private static NamespacedKey getWorldKey(String name) { + private static NamespacedKey defaultWorldKey(String name) { final String mainLevelName = Bukkit.getUnsafe().getMainLevelName(); if (name.equals(mainLevelName)) { return NamespacedKey.minecraft("overworld"); @@ -58,6 +57,9 @@ public WorldCreator(@NotNull String levelName, @NotNull NamespacedKey worldKey) if (levelName == null || worldKey == null) { throw new IllegalArgumentException("World name and key cannot be null"); } + if (!worldKey.equals(defaultWorldKey(levelName))) { + throw new UnsupportedOperationException("Custom world keys not yet implemented"); + } this.name = levelName; this.seed = (new Random()).nextLong(); this.key = worldKey; @@ -104,7 +106,6 @@ public static WorldCreator ofNameAndKey(@NotNull String levelName, @NotNull Name public static WorldCreator ofKey(@NotNull NamespacedKey worldKey) { return new WorldCreator(worldKey); } - // Paper end /** * Copies the options from the specified world @@ -589,7 +590,6 @@ public static BiomeProvider getBiomeProviderForName(@NotNull String world, @Null return result; } - // Paper start - keep spawn loaded tristate /** * Returns the current intent to keep the world loaded, @see {@link WorldCreator#keepSpawnLoaded(net.kyori.adventure.util.TriState)} * @@ -615,5 +615,4 @@ public net.kyori.adventure.util.TriState keepSpawnLoaded() { public WorldCreator keepSpawnLoaded(@NotNull net.kyori.adventure.util.TriState keepSpawnLoaded) { return this; } - // Paper end - keep spawn loaded tristate } diff --git a/paper-api/src/main/java/org/bukkit/block/Block.java b/paper-api/src/main/java/org/bukkit/block/Block.java index fbee4ab2faae..5c75b3d049f2 100644 --- a/paper-api/src/main/java/org/bukkit/block/Block.java +++ b/paper-api/src/main/java/org/bukkit/block/Block.java @@ -232,18 +232,16 @@ public static int getBlockKeyZ(long packed) { } // Paper end - // Paper start - add isValidTool /** * Checks if the itemstack is a valid tool to * break the block with * - * @param itemStack The (tool) itemstack + * @param tool The tool * @return whether the block will drop items * @deprecated partially replaced by {@link Block#isPreferredTool(ItemStack)} */ @Deprecated(since = "1.21", forRemoval = true) // Paper - boolean isValidTool(@NotNull ItemStack itemStack); - // Paper end - add isValidTool + boolean isValidTool(@NotNull ItemStack tool); /** * Gets the Location of the block @@ -438,11 +436,18 @@ public static int getBlockKeyZ(long packed) { int getBlockPower(@NotNull BlockFace face); /** - * Returns the redstone power being provided to this block + * Returns the redstone power being provided to this block. + *

    + * Equivalent to: + * {@snippet lang="java" : + * getBlockPower(BlockFace.SELF); + * } * * @return The power level. */ - int getBlockPower(); + default int getBlockPower() { + return this.getBlockPower(BlockFace.SELF); + } /** * Checks if this block is empty. diff --git a/paper-api/src/main/java/org/bukkit/block/BlockType.java b/paper-api/src/main/java/org/bukkit/block/BlockType.java index dc2d24162deb..0e42965a729f 100644 --- a/paper-api/src/main/java/org/bukkit/block/BlockType.java +++ b/paper-api/src/main/java/org/bukkit/block/BlockType.java @@ -1024,6 +1024,8 @@ interface Typed extends BlockType { BlockType.Typed GOLD_ORE = getBlockType("gold_ore"); + BlockType.Typed GOLDEN_DANDELION = getBlockType("golden_dandelion"); + BlockType.Typed GRANITE = getBlockType("granite"); BlockType.Typed GRANITE_SLAB = getBlockType("granite_slab"); @@ -1760,6 +1762,8 @@ interface Typed extends BlockType { BlockType.Typed POTTED_FLOWERING_AZALEA_BUSH = getBlockType("potted_flowering_azalea_bush"); + BlockType.Typed POTTED_GOLDEN_DANDELION = getBlockType("potted_golden_dandelion"); + BlockType.Typed POTTED_JUNGLE_SAPLING = getBlockType("potted_jungle_sapling"); BlockType.Typed POTTED_LILY_OF_THE_VALLEY = getBlockType("potted_lily_of_the_valley"); diff --git a/paper-api/src/main/java/org/bukkit/block/CommandBlock.java b/paper-api/src/main/java/org/bukkit/block/CommandBlock.java index d455b14c810e..556fec493590 100644 --- a/paper-api/src/main/java/org/bukkit/block/CommandBlock.java +++ b/paper-api/src/main/java/org/bukkit/block/CommandBlock.java @@ -41,8 +41,7 @@ public interface CommandBlock extends TileState, io.papermc.paper.command.Comman /** * Sets the name of this CommandBlock. The name is used with commands - * that this CommandBlock executes. Setting the name to null is the - * same as setting it to "@". + * that this CommandBlock executes. * * @param name New name for this CommandBlock. * @deprecated in favour of {@link #name(net.kyori.adventure.text.Component)} @@ -61,8 +60,7 @@ public interface CommandBlock extends TileState, io.papermc.paper.command.Comman /** * Sets the name of this CommandBlock. The name is used with commands - * that this CommandBlock executes. Setting the name to null is the - * same as setting it to a {@link net.kyori.adventure.text.TextComponent} containing {@code @}. + * that this CommandBlock executes. * * @param name New name for this CommandBlock. */ diff --git a/paper-api/src/main/java/org/bukkit/boss/DragonBattle.java b/paper-api/src/main/java/org/bukkit/boss/DragonBattle.java index 5dbd12a786a6..fb55f1b8c21d 100644 --- a/paper-api/src/main/java/org/bukkit/boss/DragonBattle.java +++ b/paper-api/src/main/java/org/bukkit/boss/DragonBattle.java @@ -76,14 +76,14 @@ public interface DragonBattle { /** * Try to initiate a respawn sequence to summon the dragon. * - * @param enderCrystals the {@link EnderCrystal EnderCrystals} to use in the + * @param crystals the {@link EnderCrystal EnderCrystals} to use in the * respawn, or a null or empty list to render the respawn sequence * uncancellable. null entries or crystals that do not reside in the same * world as this dragon battle will be ignored. * * @return true if the respawn was initiated, false otherwise. */ - public boolean initiateRespawn(@Nullable Collection enderCrystals); + public boolean initiateRespawn(@Nullable Collection crystals); /** * Get this battle's current respawn phase. diff --git a/paper-api/src/main/java/org/bukkit/entity/Ageable.java b/paper-api/src/main/java/org/bukkit/entity/Ageable.java index 866fc0128e02..fc5e20c3a790 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Ageable.java +++ b/paper-api/src/main/java/org/bukkit/entity/Ageable.java @@ -5,69 +5,83 @@ */ public interface Ageable extends Creature { /** - * Gets the age of this mob. + * Gets the age of this creature. * * @return Age */ public int getAge(); /** - * Sets the age of this mob. + * Sets the age of this creature. * * @param age New age */ public void setAge(int age); /** - * Lock the age of the animal, setting this will prevent the animal from - * maturing or getting ready for mating. + * Lock the age of the creature, setting this will prevent the creature from + * maturing or getting ready for mating. Plugins can still increase the age + * manually, however. * - * @param lock new lock - * @deprecated see {@link Breedable#setAgeLock(boolean)} + * @param lock new lock state */ - @Deprecated(since = "1.16.2") public void setAgeLock(boolean lock); /** - * Gets the current agelock. + * Checks if the age of the creature is locked. * - * @return the current agelock - * @deprecated see {@link Breedable#getAgeLock()} + * @return the current lock state + * @see #setAgeLock(boolean) */ - @Deprecated(since = "1.16.2") public boolean getAgeLock(); /** - * Sets the age of the mob to a baby + * Transforms this creature to its baby counter-part. + *

    + * This will not have any effect if the creature is already in + * this state. + * + * @apiNote some of them cannot be represented as such, so it's + * wise to check if {@link #isAdult()} returns {@code false} after this method + * for general purpose use. + * @see #setAdult() + * @see #setAge(int) */ public void setBaby(); /** - * Sets the age of the mob to an adult + * Transforms this creature to its adult counter-part. + *

    + * This will not have any effect if the creature is already in + * this state. + * + * @see #setBaby() + * @see #setAge(int) */ public void setAdult(); /** - * Returns true if the mob is an adult. + * Returns true if the creature is an adult. * - * @return return true if the mob is an adult + * @return if the creature is an adult + * @see #getAge() */ public boolean isAdult(); /** - * Return the ability to breed of the animal. + * Return the ability to breed of the creature. * - * @return the ability to breed of the animal + * @return the ability to breed of the creature * @deprecated see {@link Breedable#canBreed()} */ @Deprecated(since = "1.16.2") public boolean canBreed(); /** - * Set breedability of the animal, if the animal is a baby and set to + * Set breedability of the creature, if the creature is a baby and set to * breed it will instantly grow up. * - * @param breed breedability of the animal + * @param breed breedability of the creature * @deprecated see {@link Breedable#setBreed(boolean)} */ @Deprecated(since = "1.16.2") diff --git a/paper-api/src/main/java/org/bukkit/entity/Breedable.java b/paper-api/src/main/java/org/bukkit/entity/Breedable.java index 0cf8ce497814..e25bc21f704a 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Breedable.java +++ b/paper-api/src/main/java/org/bukkit/entity/Breedable.java @@ -5,21 +5,6 @@ */ public interface Breedable extends Ageable { - /** - * Lock the age of the animal, setting this will prevent the animal from - * maturing or getting ready for mating. - * - * @param lock new lock - */ - public void setAgeLock(boolean lock); - - /** - * Gets the current agelock. - * - * @return the current agelock - */ - public boolean getAgeLock(); - /** * Return the ability to breed of the animal. * diff --git a/paper-api/src/main/java/org/bukkit/entity/Cat.java b/paper-api/src/main/java/org/bukkit/entity/Cat.java index b8ac85e471f1..e3f49d5030ca 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Cat.java +++ b/paper-api/src/main/java/org/bukkit/entity/Cat.java @@ -2,9 +2,11 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Lists; -import java.util.Locale; import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; +import java.util.Locale; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; import org.bukkit.DyeColor; import org.bukkit.Keyed; import org.bukkit.NamespacedKey; @@ -32,6 +34,21 @@ public interface Cat extends Tameable, Sittable, io.papermc.paper.entity.CollarC */ public void setCatType(@NotNull Type type); + /** + * Get the sound variant of this cat. + * + * @return cat sound variant + */ + @NotNull + SoundVariant getSoundVariant(); + + /** + * Set the sound variant of this cat. + * + * @param variant cat sound variant + */ + void setSoundVariant(@NotNull SoundVariant variant); + /** * Get the collar color of this cat * @@ -107,6 +124,23 @@ static Type[] values() { } } + /** + * Represents the sound variant of a cat. + */ + interface SoundVariant extends Keyed { + + // Start generate - CatSoundVariant + SoundVariant CLASSIC = getSoundVariant("classic"); + + SoundVariant ROYAL = getSoundVariant("royal"); + // End generate - CatSoundVariant + + @NotNull + private static SoundVariant getSoundVariant(final @NotNull @KeyPattern.Value String key) { + return RegistryAccess.registryAccess().getRegistry(RegistryKey.CAT_SOUND_VARIANT).getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key)); + } + } + /** * Sets if the cat is lying down. * This is visual and does not affect the behaviour of the cat. diff --git a/paper-api/src/main/java/org/bukkit/entity/Chicken.java b/paper-api/src/main/java/org/bukkit/entity/Chicken.java index 14c2df2916fe..e0f9c37e7a23 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Chicken.java +++ b/paper-api/src/main/java/org/bukkit/entity/Chicken.java @@ -2,6 +2,8 @@ import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; import org.bukkit.Keyed; import org.bukkit.NamespacedKey; import org.jspecify.annotations.NullMarked; @@ -26,6 +28,20 @@ public interface Chicken extends Animals { */ void setVariant(Variant variant); + /** + * Get the sound variant of this chicken. + * + * @return chicken sound variant + */ + SoundVariant getSoundVariant(); + + /** + * Set the sound variant of this chicken. + * + * @param variant chicken sound variant + */ + void setSoundVariant(SoundVariant variant); + /** * Gets if this chicken was spawned as a chicken jockey. * @@ -71,4 +87,20 @@ private static Variant getVariant(String key) { return RegistryAccess.registryAccess().getRegistry(RegistryKey.CHICKEN_VARIANT).getOrThrow(NamespacedKey.minecraft(key)); } } + + /** + * Represents the sound variant of a chicken. + */ + interface SoundVariant extends Keyed { + + // Start generate - ChickenSoundVariant + SoundVariant CLASSIC = getSoundVariant("classic"); + + SoundVariant PICKY = getSoundVariant("picky"); + // End generate - ChickenSoundVariant + + private static SoundVariant getSoundVariant(final @KeyPattern.Value String key) { + return RegistryAccess.registryAccess().getRegistry(RegistryKey.CHICKEN_SOUND_VARIANT).getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key)); + } + } } diff --git a/paper-api/src/main/java/org/bukkit/entity/Cow.java b/paper-api/src/main/java/org/bukkit/entity/Cow.java index 44877f12cf5e..2e1ef0955d9e 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Cow.java +++ b/paper-api/src/main/java/org/bukkit/entity/Cow.java @@ -2,6 +2,8 @@ import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; import org.bukkit.Keyed; import org.bukkit.NamespacedKey; import org.jspecify.annotations.NullMarked; @@ -26,6 +28,20 @@ public interface Cow extends AbstractCow { */ void setVariant(Variant variant); + /** + * Get the sound variant of this cow. + * + * @return cow sound variant + */ + SoundVariant getSoundVariant(); + + /** + * Set the sound variant of this cow. + * + * @param variant cow sound variant + */ + void setSoundVariant(SoundVariant variant); + /** * Represents the variant of a cow. */ @@ -43,4 +59,20 @@ private static Variant getVariant(String key) { return RegistryAccess.registryAccess().getRegistry(RegistryKey.COW_VARIANT).getOrThrow(NamespacedKey.minecraft(key)); } } + + /** + * Represents the sound variant of a cow. + */ + interface SoundVariant extends Keyed { + + // Start generate - CowSoundVariant + SoundVariant CLASSIC = getSoundVariant("classic"); + + SoundVariant MOODY = getSoundVariant("moody"); + // End generate - CowSoundVariant + + private static SoundVariant getSoundVariant(final @KeyPattern.Value String key) { + return RegistryAccess.registryAccess().getRegistry(RegistryKey.COW_SOUND_VARIANT).getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key)); + } + } } diff --git a/paper-api/src/main/java/org/bukkit/entity/Pig.java b/paper-api/src/main/java/org/bukkit/entity/Pig.java index 24a4b588bbda..c49a9ec606dc 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Pig.java +++ b/paper-api/src/main/java/org/bukkit/entity/Pig.java @@ -2,6 +2,8 @@ import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; import org.bukkit.Keyed; import org.bukkit.NamespacedKey; import org.jspecify.annotations.NullMarked; @@ -26,6 +28,20 @@ public interface Pig extends Steerable, Vehicle { */ void setVariant(Variant variant); + /** + * Get the sound variant of this pig. + * + * @return pig sound variant + */ + SoundVariant getSoundVariant(); + + /** + * Set the sound variant of this pig. + * + * @param variant pig sound variant + */ + void setSoundVariant(SoundVariant variant); + /** * Represents the variant of a pig. */ @@ -43,4 +59,22 @@ private static Variant getVariant(String key) { return RegistryAccess.registryAccess().getRegistry(RegistryKey.PIG_VARIANT).getOrThrow(NamespacedKey.minecraft(key)); } } + + /** + * Represents the sound variant of a pig. + */ + interface SoundVariant extends Keyed { + + // Start generate - PigSoundVariant + SoundVariant BIG = getSoundVariant("big"); + + SoundVariant CLASSIC = getSoundVariant("classic"); + + SoundVariant MINI = getSoundVariant("mini"); + // End generate - PigSoundVariant + + private static SoundVariant getSoundVariant(final @KeyPattern.Value String key) { + return RegistryAccess.registryAccess().getRegistry(RegistryKey.PIG_SOUND_VARIANT).getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key)); + } + } } diff --git a/paper-api/src/main/java/org/bukkit/entity/PiglinAbstract.java b/paper-api/src/main/java/org/bukkit/entity/PiglinAbstract.java index 41aeb01f7538..f7e44b8a617a 100644 --- a/paper-api/src/main/java/org/bukkit/entity/PiglinAbstract.java +++ b/paper-api/src/main/java/org/bukkit/entity/PiglinAbstract.java @@ -64,9 +64,9 @@ public interface PiglinAbstract extends Monster, Ageable { /** * Sets whether the piglin is a baby * - * @param flag Whether the piglin is a baby + * @param baby Whether the piglin is a baby * @deprecated see {@link Ageable#setBaby()} and {@link Ageable#setAdult()} */ @Deprecated(since = "1.16.2") - public void setBaby(boolean flag); + public void setBaby(boolean baby); } diff --git a/paper-api/src/main/java/org/bukkit/entity/Player.java b/paper-api/src/main/java/org/bukkit/entity/Player.java index 26d9139886d8..5fb337eb2784 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Player.java +++ b/paper-api/src/main/java/org/bukkit/entity/Player.java @@ -17,6 +17,7 @@ import java.util.concurrent.CompletableFuture; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.object.PlayerHeadObjectContents; +import org.apache.commons.lang3.ArrayUtils; import org.bukkit.BanEntry; import org.bukkit.DyeColor; import org.bukkit.Effect; @@ -648,7 +649,7 @@ default void setBedSpawnLocation(@Nullable Location location, boolean force) { */ @Deprecated(since = "1.6.2") default void playNote(Location loc, byte instrument, byte note) { - this.playNote(loc, Instrument.getByType(instrument), new Note(note)); + this.playNote(loc, ArrayUtils.get(Instrument.values(), instrument), new Note(note)); } /** @@ -1794,15 +1795,14 @@ public default void sendMessage(net.md_5.bungee.api.ChatMessageType position, ne * * @param time The current player's perceived time or the player's time * offset from the server time. - * @param relative When true the player time is kept relative to its world - * time. + * @param tickTime if true, the player time keeps ticking up relative to its world time. */ - public void setPlayerTime(long time, boolean relative); + public void setPlayerTime(long time, boolean tickTime); /** * Returns the player's current timestamp. * - * @return The player's time + * @return The player's time, or {@code 0} if the current world does not have a world clock. */ public long getPlayerTime(); diff --git a/paper-api/src/main/java/org/bukkit/entity/Tadpole.java b/paper-api/src/main/java/org/bukkit/entity/Tadpole.java index a3d3a198b7a6..a91e01d7f9e5 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Tadpole.java +++ b/paper-api/src/main/java/org/bukkit/entity/Tadpole.java @@ -6,31 +6,32 @@ public interface Tadpole extends Fish { /** - * Gets the age of this mob. + * Gets the age of this tadpole. * * @return Age */ public int getAge(); /** - * Sets the age of this mob. + * Sets the age of this tadpole. * * @param age New age */ public void setAge(int age); /** - * Lock the age of the animal, setting this will prevent the animal from - * maturing. + * Lock the age of the tadpole, setting this will prevent the tadpole from + * maturing. Plugins can still increase the age manually, however. * - * @param lock new lock + * @param lock new lock state */ void setAgeLock(boolean lock); /** - * Gets the current agelock. + * Checks if the age of the tadpole is locked. * - * @return the current agelock + * @return the current lock state + * @see #setAgeLock(boolean) */ boolean getAgeLock(); } diff --git a/paper-api/src/main/java/org/bukkit/entity/Villager.java b/paper-api/src/main/java/org/bukkit/entity/Villager.java index d1ab8a25589a..63171289092f 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Villager.java +++ b/paper-api/src/main/java/org/bukkit/entity/Villager.java @@ -87,7 +87,6 @@ public interface Villager extends AbstractVillager { */ public void setVillagerExperience(int experience); - // Paper start /** * Increases the level of this villager. * The villager will also unlock new recipes unlike the raw @@ -96,13 +95,11 @@ public interface Villager extends AbstractVillager { * A villager with a level of 1 and no experience is liable to lose its * profession. *

    - * A master villager has a level of 5 in its profession and - * will unlock 10 trades (2 per level). + * A master villager has a level of 5 in its profession. * * @param amount The amount of level - * @return Whether trades are unlocked - * @throws IllegalArgumentException if current level plus the amount - * isn't between [1, 5] or the amount isn't positive + * @return Whether level got increased + * @throws IllegalArgumentException if the amount is not positive * @see #setVillagerLevel(int) */ boolean increaseLevel(int amount); @@ -127,7 +124,6 @@ public interface Villager extends AbstractVillager { * @param restocksToday new restock count */ public void setRestocksToday(int restocksToday); - // Paper end /** * Attempts to make this villager sleep at the given location. diff --git a/paper-api/src/main/java/org/bukkit/entity/Zoglin.java b/paper-api/src/main/java/org/bukkit/entity/Zoglin.java index 1db066d3ce17..6a451d6c8d7c 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Zoglin.java +++ b/paper-api/src/main/java/org/bukkit/entity/Zoglin.java @@ -17,9 +17,9 @@ public interface Zoglin extends Monster, Ageable { /** * Sets whether the zoglin is a baby * - * @param flag Whether the zoglin is a baby + * @param baby Whether the zoglin is a baby * @deprecated see {@link Ageable#setBaby()} and {@link Ageable#setAdult()} */ @Deprecated(since = "1.16.2") - public void setBaby(boolean flag); + public void setBaby(boolean baby); } diff --git a/paper-api/src/main/java/org/bukkit/entity/Zombie.java b/paper-api/src/main/java/org/bukkit/entity/Zombie.java index 8d11ce904f87..b9ba8a47bb62 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Zombie.java +++ b/paper-api/src/main/java/org/bukkit/entity/Zombie.java @@ -20,11 +20,11 @@ public interface Zombie extends Monster, Ageable { /** * Sets whether the zombie is a baby * - * @param flag Whether the zombie is a baby + * @param baby Whether the zombie is a baby * @deprecated see {@link Ageable#setBaby()} and {@link Ageable#setAdult()} */ @Deprecated(since = "1.16.2") - public void setBaby(boolean flag); + public void setBaby(boolean baby); /** * Gets whether the zombie is a villager @@ -173,5 +173,7 @@ public interface Zombie extends Monster, Ageable { * @deprecated Since 1.21.2 all zombie types can break doors if instructed as MC-137053 was fixed. */ @Deprecated(since = "1.21.2", forRemoval = true) - boolean supportsBreakingDoors(); + default boolean supportsBreakingDoors() { + return true; // All zombies are now capable of breaking doors, see https://bugs.mojang.com/browse/MC-137053 + } } diff --git a/paper-api/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java b/paper-api/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java index 6a6021ad3a0e..6853997774b8 100644 --- a/paper-api/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java +++ b/paper-api/src/main/java/org/bukkit/entity/minecart/CommandMinecart.java @@ -8,7 +8,7 @@ public interface CommandMinecart extends Minecart, io.papermc.paper.command.Comm /** * Gets the command that this CommandMinecart will run when activated. - * This will never return null. If the CommandMinecart does not have a + * This will never return null. If the CommandMinecart does not have a * command, an empty String will be returned instead. * * @return Command that this CommandMinecart will run when powered. @@ -27,9 +27,8 @@ public interface CommandMinecart extends Minecart, io.papermc.paper.command.Comm public void setCommand(@Nullable String command); /** - * Sets the name of this CommandMinecart. The name is used with commands - * that this CommandMinecart executes. Setting the name to null is the - * same as setting it to "@". + * Sets the name of this CommandMinecart. The name is used with commands + * that this CommandMinecart executes. * * @param name New name for this CommandMinecart. * @deprecated in favour of {@link #customName(net.kyori.adventure.text.Component)} diff --git a/paper-api/src/main/java/org/bukkit/event/block/BlockMultiPlaceEvent.java b/paper-api/src/main/java/org/bukkit/event/block/BlockMultiPlaceEvent.java index 0df9dfa44dcc..7f417d5b9f7f 100644 --- a/paper-api/src/main/java/org/bukkit/event/block/BlockMultiPlaceEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/block/BlockMultiPlaceEvent.java @@ -18,18 +18,18 @@ */ public class BlockMultiPlaceEvent extends BlockPlaceEvent { - private final List states; + private final List replacedStates; @ApiStatus.Internal @Deprecated(forRemoval = true) - public BlockMultiPlaceEvent(@NotNull List states, @NotNull Block clicked, @NotNull ItemStack itemInHand, @NotNull Player thePlayer, boolean canBuild) { - this(states, clicked, itemInHand, thePlayer, canBuild, org.bukkit.inventory.EquipmentSlot.HAND); + public BlockMultiPlaceEvent(@NotNull List replacedStates, @NotNull Block clicked, @NotNull ItemStack itemInHand, @NotNull Player thePlayer, boolean canBuild) { + this(replacedStates, clicked, itemInHand, thePlayer, canBuild, org.bukkit.inventory.EquipmentSlot.HAND); } @ApiStatus.Internal - public BlockMultiPlaceEvent(@NotNull List states, @NotNull Block clicked, @NotNull ItemStack itemInHand, @NotNull Player thePlayer, boolean canBuild, @NotNull org.bukkit.inventory.EquipmentSlot hand) { - super(states.get(0).getBlock(), states.get(0), clicked, itemInHand, thePlayer, canBuild, hand); - this.states = ImmutableList.copyOf(states); + public BlockMultiPlaceEvent(@NotNull List replacedStates, @NotNull Block clicked, @NotNull ItemStack itemInHand, @NotNull Player thePlayer, boolean canBuild, @NotNull org.bukkit.inventory.EquipmentSlot hand) { + super(replacedStates.getFirst().getBlock(), replacedStates.getFirst(), clicked, itemInHand, thePlayer, canBuild, hand); + this.replacedStates = ImmutableList.copyOf(replacedStates); } /** @@ -41,6 +41,6 @@ public BlockMultiPlaceEvent(@NotNull List states, @NotNull Block cli */ @NotNull public List getReplacedBlockStates() { - return this.states; + return this.replacedStates; } } diff --git a/paper-api/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java b/paper-api/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java index 81dd17b033ea..394ae727e3ee 100644 --- a/paper-api/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java @@ -22,7 +22,7 @@ public class BlockPlaceEvent extends BlockEvent implements Cancellable { protected Block placedAgainst; protected ItemStack itemInHand; protected Player player; - protected BlockState replacedBlockState; + protected BlockState replacedState; protected boolean canBuild; protected EquipmentSlot hand; @@ -30,17 +30,17 @@ public class BlockPlaceEvent extends BlockEvent implements Cancellable { @ApiStatus.Internal @Deprecated(since = "1.9", forRemoval = true) - public BlockPlaceEvent(@NotNull final Block placedBlock, @NotNull final BlockState replacedBlockState, @NotNull final Block placedAgainst, @NotNull final ItemStack itemInHand, @NotNull final Player thePlayer, final boolean canBuild) { - this(placedBlock, replacedBlockState, placedAgainst, itemInHand, thePlayer, canBuild, EquipmentSlot.HAND); + public BlockPlaceEvent(@NotNull final Block placedBlock, @NotNull final BlockState replacedState, @NotNull final Block placedAgainst, @NotNull final ItemStack itemInHand, @NotNull final Player thePlayer, final boolean canBuild) { + this(placedBlock, replacedState, placedAgainst, itemInHand, thePlayer, canBuild, EquipmentSlot.HAND); } @ApiStatus.Internal - public BlockPlaceEvent(@NotNull final Block placedBlock, @NotNull final BlockState replacedBlockState, @NotNull final Block placedAgainst, @NotNull final ItemStack itemInHand, @NotNull final Player thePlayer, final boolean canBuild, @NotNull final EquipmentSlot hand) { + public BlockPlaceEvent(@NotNull final Block placedBlock, @NotNull final BlockState replacedState, @NotNull final Block placedAgainst, @NotNull final ItemStack itemInHand, @NotNull final Player thePlayer, final boolean canBuild, @NotNull final EquipmentSlot hand) { super(placedBlock); this.placedAgainst = placedAgainst; this.itemInHand = itemInHand; this.player = thePlayer; - this.replacedBlockState = replacedBlockState; + this.replacedState = replacedState; this.canBuild = canBuild; this.hand = hand; } @@ -95,7 +95,7 @@ public Block getBlockPlaced() { */ @NotNull public BlockState getBlockReplacedState() { - return this.replacedBlockState; + return this.replacedState; } /** diff --git a/paper-api/src/main/java/org/bukkit/event/entity/EntityTransformEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/EntityTransformEvent.java index 6557aa19eadc..28314db2c463 100644 --- a/paper-api/src/main/java/org/bukkit/event/entity/EntityTransformEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/entity/EntityTransformEvent.java @@ -16,17 +16,17 @@ public class EntityTransformEvent extends EntityEvent implements Cancellable { private static final HandlerList HANDLER_LIST = new HandlerList(); - private final List convertedList; + private final List transformedEntities; private final Entity converted; private final TransformReason transformReason; private boolean cancelled; @ApiStatus.Internal - public EntityTransformEvent(Entity original, List convertedList, TransformReason transformReason) { + public EntityTransformEvent(Entity original, List transformedEntities, TransformReason transformReason) { super(original); - this.convertedList = Collections.unmodifiableList(convertedList); - this.converted = convertedList.getFirst(); + this.transformedEntities = Collections.unmodifiableList(transformedEntities); + this.converted = transformedEntities.getFirst(); this.transformReason = transformReason; } @@ -36,7 +36,7 @@ public EntityTransformEvent(Entity original, List convertedList, Transfo * @return The transformed entities. */ public List getTransformedEntities() { - return this.convertedList; + return this.transformedEntities; } /** diff --git a/paper-api/src/main/java/org/bukkit/event/entity/PigZapEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/PigZapEvent.java index d09523630380..739794558f78 100644 --- a/paper-api/src/main/java/org/bukkit/event/entity/PigZapEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/entity/PigZapEvent.java @@ -11,7 +11,7 @@ /** * Stores data for pigs being zapped */ -public class PigZapEvent extends EntityZapEvent implements Cancellable { +public class PigZapEvent extends EntityZapEvent implements Cancellable { // todo redundant with parent? private final PigZombie zombifiedPiglin; private final LightningStrike bolt; diff --git a/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractAtEntityEvent.java b/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractAtEntityEvent.java index e790cb487d70..160b70f0d4da 100644 --- a/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractAtEntityEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractAtEntityEvent.java @@ -11,17 +11,9 @@ /** * Represents an event that is called when a player right clicks an entity that * also contains the location where the entity was clicked. - *
    - * Note that the client may sometimes spuriously send this packet in addition to {@link PlayerInteractEntityEvent}. - * Users are advised to listen to this (parent) class unless specifically required. - *
    - * Note that interacting with Armor Stands fires this event only and not its parent and as such users are expressly required - * to listen to this event for that scenario. */ public class PlayerInteractAtEntityEvent extends PlayerInteractEntityEvent { - private static final HandlerList HANDLER_LIST = new HandlerList(); - private final Vector position; @ApiStatus.Internal @@ -39,15 +31,4 @@ public PlayerInteractAtEntityEvent(@NotNull Player player, @NotNull Entity click public Vector getClickedPosition() { return this.position.clone(); } - - @NotNull - @Override - public HandlerList getHandlers() { - return HANDLER_LIST; - } - - @NotNull - public static HandlerList getHandlerList() { - return HANDLER_LIST; - } } diff --git a/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractEntityEvent.java b/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractEntityEvent.java index 6dac09f9ca45..b96956c59551 100644 --- a/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractEntityEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractEntityEvent.java @@ -10,7 +10,11 @@ /** * Represents an event that is called when a player right clicks an entity. + * + * @apiNote this event is no longer called without being a {@link PlayerInteractAtEntityEvent}, it's therefore + * recommended to listen to that event instead which hold more informations. */ +@ApiStatus.Obsolete public class PlayerInteractEntityEvent extends PlayerEvent implements Cancellable { private static final HandlerList HANDLER_LIST = new HandlerList(); diff --git a/paper-api/src/main/java/org/bukkit/event/world/TimeSkipEvent.java b/paper-api/src/main/java/org/bukkit/event/world/TimeSkipEvent.java index 0a9134e7e2fc..ffcc09589e24 100644 --- a/paper-api/src/main/java/org/bukkit/event/world/TimeSkipEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/world/TimeSkipEvent.java @@ -11,6 +11,7 @@ *

    * If the event is cancelled the time will not change. */ +// TODO - snapshot - 26.1 API needed for clock! public class TimeSkipEvent extends WorldEvent implements Cancellable { private static final HandlerList HANDLER_LIST = new HandlerList(); diff --git a/paper-api/src/main/java/org/bukkit/inventory/ItemType.java b/paper-api/src/main/java/org/bukkit/inventory/ItemType.java index d1a5e35ccc6e..e0a19bc5c31b 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/ItemType.java +++ b/paper-api/src/main/java/org/bukkit/inventory/ItemType.java @@ -1218,6 +1218,8 @@ interface Typed extends ItemType { ItemType.Typed GOLDEN_CHESTPLATE = getItemType("golden_chestplate"); + ItemType.Typed GOLDEN_DANDELION = getItemType("golden_dandelion"); + ItemType.Typed GOLDEN_HELMET = getItemType("golden_helmet"); ItemType.Typed GOLDEN_HOE = getItemType("golden_hoe"); diff --git a/paper-api/src/main/java/org/bukkit/inventory/ShapedRecipe.java b/paper-api/src/main/java/org/bukkit/inventory/ShapedRecipe.java index 462a6d1da59b..56c2820aa18d 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/ShapedRecipe.java +++ b/paper-api/src/main/java/org/bukkit/inventory/ShapedRecipe.java @@ -88,7 +88,7 @@ public ShapedRecipe shape(@NotNull final String... shape) { continue; } - newIngredients.put(c, ingredients.get(c)); + newIngredients.put(c, this.ingredients.get(c)); } } this.ingredients = newIngredients; @@ -129,7 +129,11 @@ public ShapedRecipe setIngredient(char key, @NotNull MaterialData ingredient) { */ @NotNull public ShapedRecipe setIngredient(char key, @NotNull Material ingredient) { - return setIngredient(key, ingredient, 0); + Preconditions.checkArgument(key != ' ', "Space in recipe shape must represent no ingredient"); + Preconditions.checkArgument(this.ingredients.containsKey(key), "Symbol does not appear in the shape:", key); + + this.ingredients.put(key, new RecipeChoice.MaterialChoice(Collections.singletonList(ingredient))); + return this; } /** @@ -149,16 +153,7 @@ public ShapedRecipe setIngredient(char key, @NotNull Material ingredient) { @Deprecated(since = "1.6.2") @NotNull public ShapedRecipe setIngredient(char key, @NotNull Material ingredient, int raw) { - Preconditions.checkArgument(key != ' ', "Space in recipe shape must represent no ingredient"); - Preconditions.checkArgument(ingredients.containsKey(key), "Symbol does not appear in the shape:", key); - - // -1 is the old wildcard, map to Short.MAX_VALUE as the new one - if (raw == -1) { - raw = Short.MAX_VALUE; - } - - ingredients.put(key, new RecipeChoice.MaterialChoice(Collections.singletonList(ingredient))); - return this; + return setIngredient(key, ingredient); } /** @@ -176,19 +171,17 @@ public ShapedRecipe setIngredient(char key, @NotNull Material ingredient, int ra @NotNull public ShapedRecipe setIngredient(char key, @NotNull RecipeChoice ingredient) { Preconditions.checkArgument(key != ' ', "Space in recipe shape must represent no ingredient"); - Preconditions.checkArgument(ingredients.containsKey(key), "Symbol does not appear in the shape:", key); + Preconditions.checkArgument(this.ingredients.containsKey(key), "Symbol does not appear in the shape:", key); - ingredients.put(key, ingredient.validate(false).clone()); // Paper + this.ingredients.put(key, ingredient.validate(false).clone()); // Paper return this; } - // Paper start @NotNull public ShapedRecipe setIngredient(char key, @NotNull ItemStack item) { Preconditions.checkArgument(!item.getType().isAir(), "Item cannot be air"); // Paper return setIngredient(key, new RecipeChoice.ExactChoice(item.clone())); // Paper } - // Paper end /** * Get a copy of the ingredients map. @@ -199,8 +192,8 @@ public ShapedRecipe setIngredient(char key, @NotNull ItemStack item) { @Deprecated // Paper @NotNull public Map getIngredientMap() { - HashMap result = new HashMap(); - for (Map.Entry ingredient : ingredients.entrySet()) { + HashMap result = new HashMap<>(); + for (Map.Entry ingredient : this.ingredients.entrySet()) { if (ingredient.getValue() == null) { result.put(ingredient.getKey(), null); } else { @@ -218,7 +211,7 @@ public Map getIngredientMap() { @NotNull public Map getChoiceMap() { Map result = new HashMap<>(); - for (Map.Entry ingredient : ingredients.entrySet()) { + for (Map.Entry ingredient : this.ingredients.entrySet()) { if (ingredient.getValue() == null) { result.put(ingredient.getKey(), null); } else { @@ -235,6 +228,6 @@ public Map getChoiceMap() { * @throws NullPointerException when not set yet */ public @NotNull String @NotNull [] getShape() { - return rows.clone(); + return this.rows.clone(); } } diff --git a/paper-api/src/main/java/org/bukkit/inventory/ShapelessRecipe.java b/paper-api/src/main/java/org/bukkit/inventory/ShapelessRecipe.java index ea90b25de4aa..4f9de4c96563 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/ShapelessRecipe.java +++ b/paper-api/src/main/java/org/bukkit/inventory/ShapelessRecipe.java @@ -60,7 +60,7 @@ public ShapelessRecipe addIngredient(@NotNull MaterialData ingredient) { */ @NotNull public ShapelessRecipe addIngredient(@NotNull Material ingredient) { - return addIngredient(1, ingredient, 0); + return addIngredient(1, ingredient); } /** @@ -100,7 +100,12 @@ public ShapelessRecipe addIngredient(int count, @NotNull MaterialData ingredient */ @NotNull public ShapelessRecipe addIngredient(int count, @NotNull Material ingredient) { - return addIngredient(count, ingredient, 0); + Preconditions.checkArgument(this.ingredients.size() + count <= 9, "Shapeless recipes cannot have more than 9 ingredients"); + + while (count-- > 0) { + this.ingredients.add(new RecipeChoice.MaterialChoice(Collections.singletonList(ingredient))); + } + return this; } /** @@ -115,24 +120,14 @@ public ShapelessRecipe addIngredient(int count, @NotNull Material ingredient) { @Deprecated(since = "1.6.2") @NotNull public ShapelessRecipe addIngredient(int count, @NotNull Material ingredient, int rawdata) { - Preconditions.checkArgument(ingredients.size() + count <= 9, "Shapeless recipes cannot have more than 9 ingredients"); - - // -1 is the old wildcard, map to Short.MAX_VALUE as the new one - if (rawdata == -1) { - rawdata = Short.MAX_VALUE; - } - - while (count-- > 0) { - ingredients.add(new RecipeChoice.MaterialChoice(Collections.singletonList(ingredient))); - } - return this; + return this.addIngredient(count, ingredient); } @NotNull public ShapelessRecipe addIngredient(@NotNull RecipeChoice ingredient) { - Preconditions.checkArgument(ingredients.size() + 1 <= 9, "Shapeless recipes cannot have more than 9 ingredients"); + Preconditions.checkArgument(this.ingredients.size() + 1 <= 9, "Shapeless recipes cannot have more than 9 ingredients"); - ingredients.add(ingredient.validate(false).clone()); // Paper + this.ingredients.add(ingredient.validate(false).clone()); // Paper return this; } @@ -144,11 +139,11 @@ public ShapelessRecipe addIngredient(@NotNull ItemStack item) { @NotNull public ShapelessRecipe addIngredient(int count, @NotNull ItemStack item) { - Preconditions.checkArgument(ingredients.size() + count <= 9, "Shapeless recipes cannot have more than 9 ingredients"); + Preconditions.checkArgument(this.ingredients.size() + count <= 9, "Shapeless recipes cannot have more than 9 ingredients"); Preconditions.checkArgument(!item.getType().isAir(), "Item cannot be air"); // Paper item = item.clone(); // Paper while (count-- > 0) { - ingredients.add(new RecipeChoice.ExactChoice(item)); + this.ingredients.add(new RecipeChoice.ExactChoice(item)); } return this; } @@ -160,7 +155,7 @@ public ShapelessRecipe removeIngredient(@NotNull ItemStack item) { @NotNull public ShapelessRecipe removeIngredient(int count, @NotNull ItemStack item) { - Iterator iterator = ingredients.iterator(); + Iterator iterator = this.ingredients.iterator(); while (count > 0 && iterator.hasNext()) { RecipeChoice choice = iterator.next(); if (choice.test(item)) { @@ -180,7 +175,7 @@ public ShapelessRecipe removeIngredient(int count, @NotNull ItemStack item) { */ @NotNull public ShapelessRecipe removeIngredient(@NotNull RecipeChoice ingredient) { - ingredients.remove(ingredient); + this.ingredients.remove(ingredient); return this; } @@ -273,7 +268,7 @@ public ShapelessRecipe removeIngredient(@NotNull Material ingredient, int rawdat @Deprecated(since = "1.6.2") @NotNull public ShapelessRecipe removeIngredient(int count, @NotNull Material ingredient, int rawdata) { - Iterator iterator = ingredients.iterator(); + Iterator iterator = this.ingredients.iterator(); while (count > 0 && iterator.hasNext()) { ItemStack stack = iterator.next().getItemStack(); if (stack.getType() == ingredient && stack.getDurability() == rawdata) { @@ -293,8 +288,8 @@ public ShapelessRecipe removeIngredient(int count, @NotNull Material ingredient, @Deprecated // Paper @NotNull public List getIngredientList() { - ArrayList result = new ArrayList(ingredients.size()); - for (RecipeChoice ingredient : ingredients) { + ArrayList result = new ArrayList(this.ingredients.size()); + for (RecipeChoice ingredient : this.ingredients) { result.add(ingredient.getItemStack().clone()); } return result; @@ -302,8 +297,8 @@ public List getIngredientList() { @NotNull public List getChoiceList() { - List result = new ArrayList<>(ingredients.size()); - for (RecipeChoice ingredient : ingredients) { + List result = new ArrayList<>(this.ingredients.size()); + for (RecipeChoice ingredient : this.ingredients) { result.add(ingredient.clone()); } return result; diff --git a/paper-api/src/main/java/org/bukkit/inventory/StonecuttingRecipe.java b/paper-api/src/main/java/org/bukkit/inventory/StonecuttingRecipe.java index 17b33f8e6e3d..209fc64d81ea 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/StonecuttingRecipe.java +++ b/paper-api/src/main/java/org/bukkit/inventory/StonecuttingRecipe.java @@ -14,7 +14,6 @@ public class StonecuttingRecipe implements Recipe, Keyed { private final NamespacedKey key; private ItemStack output; private RecipeChoice ingredient; - private String group = ""; /** * Create a Stonecutting recipe to craft the specified ItemStack. @@ -109,10 +108,12 @@ public NamespacedKey getKey() { * together when displayed in the client. * * @return recipe group. An empty string denotes no group. May not be null. + * @deprecated no longer used for this recipe */ @NotNull + @Deprecated(since = "26.1") public String getGroup() { - return group; + return ""; } /** @@ -121,9 +122,10 @@ public String getGroup() { * * @param group recipe group. An empty string denotes no group. May not be * null. + * @deprecated no longer used for this recipe */ + @Deprecated(since = "26.1") public void setGroup(@NotNull String group) { Preconditions.checkArgument(group != null, "group cannot be null"); - this.group = group; } } diff --git a/paper-api/src/main/java/org/bukkit/inventory/meta/ItemMeta.java b/paper-api/src/main/java/org/bukkit/inventory/meta/ItemMeta.java index 08fd7877df04..d4ca244f3855 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/meta/ItemMeta.java +++ b/paper-api/src/main/java/org/bukkit/inventory/meta/ItemMeta.java @@ -1,13 +1,15 @@ package org.bukkit.inventory.meta; import com.google.common.collect.Multimap; +import io.papermc.paper.datacomponent.DataComponentType; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.ItemAdventurePredicate; +import io.papermc.paper.registry.keys.tags.DamageTypeTagKeys; +import io.papermc.paper.registry.set.RegistryKeySet; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; -import io.papermc.paper.datacomponent.DataComponentType; -import io.papermc.paper.datacomponent.DataComponentTypes; -import io.papermc.paper.datacomponent.item.ItemAdventurePredicate; import net.kyori.adventure.text.Component; import org.bukkit.NamespacedKey; import org.bukkit.Tag; @@ -29,7 +31,6 @@ import org.bukkit.inventory.meta.components.UseCooldownComponent; import org.bukkit.inventory.meta.tags.CustomItemTagContainer; import org.bukkit.persistence.PersistentDataHolder; -import org.bukkit.tag.DamageTypeTags; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -634,8 +635,7 @@ default void displayName(final net.kyori.adventure.text.@Nullable Component disp * or lava. * * @return fire_resistant - * @deprecated use {@link #getDamageResistant()} and - * {@link DamageTypeTags#IS_FIRE} + * @deprecated use {@link #getDamageResistantTypes()} and check if it matches any {@link DamageTypeTagKeys#IS_FIRE fire damage type} */ @Deprecated(since = "1.21.2") boolean isFireResistant(); @@ -645,8 +645,7 @@ default void displayName(final net.kyori.adventure.text.@Nullable Component disp * or lava. * * @param fireResistant fire_resistant - * @deprecated use {@link #setDamageResistant(org.bukkit.Tag)} and - * {@link DamageTypeTags#IS_FIRE} + * @deprecated use {@link #setDamageResistantTypes(RegistryKeySet)} with {@link DamageTypeTagKeys#IS_FIRE} */ @Deprecated(since = "1.21.2") void setFireResistant(boolean fireResistant); @@ -662,12 +661,11 @@ default void displayName(final net.kyori.adventure.text.@Nullable Component disp * Gets the type of damage this item will be resistant to when in entity * form. * - * Plugins should check {@link #hasDamageResistant()} before calling this - * method. - * - * @return damage type + * @return damage type tag + * @deprecated use {@link #getDamageResistantTypes()} */ @Nullable + @Deprecated(since = "26.1") Tag getDamageResistant(); /** @@ -675,9 +673,27 @@ default void displayName(final net.kyori.adventure.text.@Nullable Component disp * form. * * @param tag the tag, or null to clear + * @deprecated use {@link #setDamageResistantTypes(RegistryKeySet)} */ + @Deprecated(since = "26.1") void setDamageResistant(@Nullable Tag tag); + /** + * Gets the type of damage this item will be resistant to when in entity + * form. + * + * @return the registry key set holding the respective damage types. + */ + @Nullable RegistryKeySet getDamageResistantTypes(); + + /** + * Sets the type of damage this item will be resistant to when in entity + * form. + * + * @param types the registry key set, or null to clear + */ + void setDamageResistantTypes(@Nullable RegistryKeySet types); + /** * Gets if the max_stack_size is set. * @@ -1039,9 +1055,9 @@ default void displayName(final net.kyori.adventure.text.@Nullable Component disp * assert itemStack.isSimilar(recreatedItemStack); // Should be true* * *

    - * *Components not represented or explicitly overridden by this ItemMeta instance - * will not be included in the resulting string and therefore may result in ItemStacks - * that do not match exactly. For example, if {@link #setDisplayName(String)} + * *Components not represented or explicitly overridden by this ItemMeta instance and + * transient components will not be included in the resulting string and therefore may + * result in ItemStacks that do not match exactly. For example, if {@link #setDisplayName(String)} * is not set, then the custom name component will not be included. Or if this ItemMeta * is a PotionMeta, it will not include any components related to lodestone compasses, * banners, or books, etc., only components modifiable by a PotionMeta instance. diff --git a/paper-api/src/main/java/org/bukkit/inventory/meta/components/EquippableComponent.java b/paper-api/src/main/java/org/bukkit/inventory/meta/components/EquippableComponent.java index e462ae9f9c72..015c553d41c8 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/meta/components/EquippableComponent.java +++ b/paper-api/src/main/java/org/bukkit/inventory/meta/components/EquippableComponent.java @@ -164,4 +164,32 @@ public interface EquippableComponent extends ConfigurationSerializable { * @param equip whether the item equips on interact */ void setEquipOnInteract(boolean equip); + + /** + * Checks if the item can be unequipped when interacting with an entity using shears. + * + * @return whether the item can be unequipped using shears + */ + boolean canBeSheared(); + + /** + * Sets if the item can be unequipped when interacting with an entity using shears. + * + * @param sheared whether the item can be unequipped using shears + */ + void setCanBeSheared(boolean sheared); + + /** + * Gets the sound to play when the item is sheared. + * + * @return the sound + */ + @Nullable Sound getShearingSound(); + + /** + * Sets the sound to play when the item is sheared. + * + * @param sound sound or null for current default + */ + void setShearingSound(@Nullable Sound sound); } diff --git a/paper-api/src/main/java/org/bukkit/plugin/java/LibraryLoader.java b/paper-api/src/main/java/org/bukkit/plugin/java/LibraryLoader.java index 6fe82caf86fc..35b36ba09010 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/java/LibraryLoader.java +++ b/paper-api/src/main/java/org/bukkit/plugin/java/LibraryLoader.java @@ -46,8 +46,7 @@ public class LibraryLoader { private final RepositorySystem repository; private final DefaultRepositorySystemSession session; private final List repositories; - public static java.util.function.BiFunction LIBRARY_LOADER_FACTORY; // Paper - rewrite reflection in libraries - public static java.util.function.Function, List> REMAPPER; // Paper - remap libraries + public static java.util.function.BiFunction LIBRARY_LOADER_FACTORY; // Paper - bytecode rewriting hook private static List getRepositories() { return List.of(new RemoteRepository.Builder("central", "default", MavenLibraryResolver.MAVEN_CENTRAL_DEFAULT_MIRROR).build()); @@ -131,9 +130,6 @@ public ClassLoader createLoader(@NotNull PluginDescriptionFile desc, java.util.@ jarPaths.add(artifact.getArtifact().getFile().toPath()); } } - if (REMAPPER != null) { - jarPaths = REMAPPER.apply(jarPaths); - } for (java.nio.file.Path path : jarPaths) { File file = path.toFile(); // Paper end - remap libraries @@ -152,14 +148,14 @@ public ClassLoader createLoader(@NotNull PluginDescriptionFile desc, java.util.@ }); } - // Paper start - rewrite reflection in libraries + // Paper start - bytecode rewriting hook URLClassLoader loader; if (LIBRARY_LOADER_FACTORY == null) { loader = new URLClassLoader(jarFiles.toArray(new URL[jarFiles.size()]), getClass().getClassLoader()); } else { loader = LIBRARY_LOADER_FACTORY.apply(jarFiles.toArray(new URL[jarFiles.size()]), getClass().getClassLoader()); } - // Paper end - rewrite reflection in libraries + // Paper end - bytecode rewriting hook return loader; } diff --git a/paper-api/src/main/java/org/bukkit/structure/StructureManager.java b/paper-api/src/main/java/org/bukkit/structure/StructureManager.java index f7e7be0cf4bb..3941d127968c 100644 --- a/paper-api/src/main/java/org/bukkit/structure/StructureManager.java +++ b/paper-api/src/main/java/org/bukkit/structure/StructureManager.java @@ -139,7 +139,7 @@ public interface StructureManager { /** * Gets the location where a structure file would exist in the primary world * directory based on the NamespacedKey using the format - * world/generated/{NAMESPACE}/structures/{KEY}.nbt. This method will always + * world/generated/{NAMESPACE}/structure/{KEY}.nbt. This method will always * return a file, even if none exists at the moment. * * @param structureKey The key to build the filepath from. diff --git a/paper-api/src/main/java/org/bukkit/util/NumberConversions.java b/paper-api/src/main/java/org/bukkit/util/NumberConversions.java index e10b9a4e5343..07464b85895d 100644 --- a/paper-api/src/main/java/org/bukkit/util/NumberConversions.java +++ b/paper-api/src/main/java/org/bukkit/util/NumberConversions.java @@ -20,7 +20,7 @@ public static int ceil(final double num) { } public static int round(double num) { - return floor(num + 0.5d); + return floor(num + 0.5); } public static double square(double num) { diff --git a/paper-api/src/main/java/org/bukkit/util/Vector.java b/paper-api/src/main/java/org/bukkit/util/Vector.java index 4607b15f3c58..acae8a9f35f5 100644 --- a/paper-api/src/main/java/org/bukkit/util/Vector.java +++ b/paper-api/src/main/java/org/bukkit/util/Vector.java @@ -388,9 +388,9 @@ public boolean isZero() { */ @NotNull Vector normalizeZeros() { - if (x == -0.0D) x = 0.0D; - if (y == -0.0D) y = 0.0D; - if (z == -0.0D) z = 0.0D; + if (x == -0.0) x = 0.0; + if (y == -0.0) y = 0.0; + if (z == -0.0) z = 0.0; return this; } diff --git a/paper-api/src/test/java/org/bukkit/metadata/FixedMetadataValueTest.java b/paper-api/src/test/java/org/bukkit/metadata/FixedMetadataValueTest.java index 5dd537cf4dc0..2cc2bf3e87ca 100644 --- a/paper-api/src/test/java/org/bukkit/metadata/FixedMetadataValueTest.java +++ b/paper-api/src/test/java/org/bukkit/metadata/FixedMetadataValueTest.java @@ -24,7 +24,7 @@ public void testNumberTypes() { assertTrue(subject.asBoolean()); assertEquals(5, subject.asByte()); assertEquals(5.0, subject.asFloat(), 0.1e-8); - assertEquals(5.0D, subject.asDouble(), 0.1e-8D); + assertEquals(5.0, subject.asDouble(), 0.1e-8); assertEquals(5L, subject.asLong()); assertEquals(5, subject.asShort()); assertEquals("5", subject.asString()); diff --git a/paper-api/src/test/java/org/bukkit/util/BoundingBoxTest.java b/paper-api/src/test/java/org/bukkit/util/BoundingBoxTest.java index 3141c007d115..ea3754b0d74a 100644 --- a/paper-api/src/test/java/org/bukkit/util/BoundingBoxTest.java +++ b/paper-api/src/test/java/org/bukkit/util/BoundingBoxTest.java @@ -16,33 +16,33 @@ public void testConstruction() { BoundingBox expected = new BoundingBox(-1, -1, -1, 1, 2, 3); assertThat(expected.getMin(), is(new Vector(-1, -1, -1))); assertThat(expected.getMax(), is(new Vector(1, 2, 3))); - assertThat(expected.getCenter(), is(new Vector(0.0D, 0.5D, 1.0D))); - assertThat(expected.getWidthX(), is(2.0D)); - assertThat(expected.getHeight(), is(3.0D)); - assertThat(expected.getWidthZ(), is(4.0D)); - assertThat(expected.getVolume(), is(24.0D)); + assertThat(expected.getCenter(), is(new Vector(0.0, 0.5, 1.0))); + assertThat(expected.getWidthX(), is(2.0)); + assertThat(expected.getHeight(), is(3.0)); + assertThat(expected.getWidthZ(), is(4.0)); + assertThat(expected.getVolume(), is(24.0)); assertThat(BoundingBox.of(new Vector(-1, -1, -1), new Vector(1, 2, 3)), is(expected)); assertThat(BoundingBox.of(new Vector(1, 2, 3), new Vector(-1, -1, -1)), is(expected)); assertThat(BoundingBox.of(new Location(null, -1, -1, -1), new Location(null, 1, 2, 3)), is(expected)); - assertThat(BoundingBox.of(new Vector(0.0D, 0.5D, 1.0D), 1.0D, 1.5D, 2.0D), is(expected)); - assertThat(BoundingBox.of(new Location(null, 0.0D, 0.5D, 1.0D), 1.0D, 1.5D, 2.0D), is(expected)); + assertThat(BoundingBox.of(new Vector(0.0, 0.5, 1.0), 1.0, 1.5, 2.0), is(expected)); + assertThat(BoundingBox.of(new Location(null, 0.0, 0.5, 1.0), 1.0, 1.5, 2.0), is(expected)); } @Test public void testContains() { BoundingBox aabb = new BoundingBox(-1, -1, -1, 1, 2, 3); - assertThat(aabb.contains(-0.5D, 0.0D, 0.5D), is(true)); - assertThat(aabb.contains(-1.0D, -1.0D, -1.0D), is(true)); - assertThat(aabb.contains(1.0D, 2.0D, 3.0D), is(false)); - assertThat(aabb.contains(-1.0D, 1.0D, 4.0D), is(false)); - assertThat(aabb.contains(new Vector(-0.5D, 0.0D, 0.5D)), is(true)); + assertThat(aabb.contains(-0.5, 0.0, 0.5), is(true)); + assertThat(aabb.contains(-1.0, -1.0, -1.0), is(true)); + assertThat(aabb.contains(1.0, 2.0, 3.0), is(false)); + assertThat(aabb.contains(-1.0, 1.0, 4.0), is(false)); + assertThat(aabb.contains(new Vector(-0.5, 0.0, 0.5)), is(true)); - assertThat(aabb.contains(new BoundingBox(-0.5D, -0.5D, -0.5D, 0.5D, 1.0D, 2.0D)), is(true)); + assertThat(aabb.contains(new BoundingBox(-0.5, -0.5, -0.5, 0.5, 1.0, 2.0)), is(true)); assertThat(aabb.contains(aabb), is(true)); assertThat(aabb.contains(new BoundingBox(-1, -1, -1, 1, 1, 3)), is(true)); assertThat(aabb.contains(new BoundingBox(-2, -1, -1, 1, 2, 3)), is(false)); - assertThat(aabb.contains(new Vector(-0.5D, -0.5D, -0.5D), new Vector(0.5D, 1.0D, 2.0D)), is(true)); + assertThat(aabb.contains(new Vector(-0.5, -0.5, -0.5), new Vector(0.5, 1.0, 2.0)), is(true)); } @Test @@ -50,20 +50,20 @@ public void testOverlaps() { BoundingBox aabb = new BoundingBox(-1, -1, -1, 1, 2, 3); assertThat(aabb.contains(aabb), is(true)); assertThat(aabb.overlaps(new BoundingBox(-2, -2, -2, 0, 0, 0)), is(true)); - assertThat(aabb.overlaps(new BoundingBox(0.5D, 1.5D, 2.5D, 1, 2, 3)), is(true)); - assertThat(aabb.overlaps(new BoundingBox(0.5D, 1.5D, 2.5D, 2, 3, 4)), is(true)); + assertThat(aabb.overlaps(new BoundingBox(0.5, 1.5, 2.5, 1, 2, 3)), is(true)); + assertThat(aabb.overlaps(new BoundingBox(0.5, 1.5, 2.5, 2, 3, 4)), is(true)); assertThat(aabb.overlaps(new BoundingBox(-2, -2, -2, -1, -1, -1)), is(false)); assertThat(aabb.overlaps(new BoundingBox(1, 2, 3, 2, 3, 4)), is(false)); - assertThat(aabb.overlaps(new Vector(0.5D, 1.5D, 2.5D), new Vector(1, 2, 3)), is(true)); + assertThat(aabb.overlaps(new Vector(0.5, 1.5, 2.5), new Vector(1, 2, 3)), is(true)); } @Test public void testDegenerate() { BoundingBox aabb = new BoundingBox(0, 0, 0, 0, 0, 0); - assertThat(aabb.getWidthX(), is(0.0D)); - assertThat(aabb.getHeight(), is(0.0D)); - assertThat(aabb.getWidthZ(), is(0.0D)); - assertThat(aabb.getVolume(), is(0.0D)); + assertThat(aabb.getWidthX(), is(0.0)); + assertThat(aabb.getHeight(), is(0.0)); + assertThat(aabb.getWidthZ(), is(0.0)); + assertThat(aabb.getVolume(), is(0.0)); } @Test @@ -100,35 +100,35 @@ public void testExpansion() { assertThat(aabb.clone().expand(-1, -2, -3, -0.5D, -0.5, -3), is(new BoundingBox(1, 1.5D, 1, 1.5D, 1.5D, 1))); assertThat(aabb.clone().expand(1, 2, 3), is(new BoundingBox(-1, -2, -3, 3, 4, 5))); - assertThat(aabb.clone().expand(-0.1, -0.5, -2), is(new BoundingBox(0.1D, 0.5D, 1, 1.9D, 1.5D, 1))); + assertThat(aabb.clone().expand(-0.1, -0.5, -2), is(new BoundingBox(0.1, 0.5, 1, 1.9, 1.5, 1))); assertThat(aabb.clone().expand(new Vector(1, 2, 3)), is(new BoundingBox(-1, -2, -3, 3, 4, 5))); assertThat(aabb.clone().expand(1), is(new BoundingBox(-1, -1, -1, 3, 3, 3))); - assertThat(aabb.clone().expand(-0.5D), is(new BoundingBox(0.5D, 0.5D, 0.5D, 1.5D, 1.5D, 1.5D))); - - assertThat(aabb.clone().expand(1, 0, 0, 0.5D), is(new BoundingBox(0, 0, 0, 2.5D, 2, 2))); - assertThat(aabb.clone().expand(1, 0, 0, -0.5D), is(new BoundingBox(0, 0, 0, 1.5D, 2, 2))); - assertThat(aabb.clone().expand(-1, 0, 0, 0.5D), is(new BoundingBox(-0.5D, 0, 0, 2, 2, 2))); - assertThat(aabb.clone().expand(-1, 0, 0, -0.5D), is(new BoundingBox(0.5D, 0, 0, 2, 2, 2))); - - assertThat(aabb.clone().expand(0, 1, 0, 0.5D), is(new BoundingBox(0, 0, 0, 2, 2.5D, 2))); - assertThat(aabb.clone().expand(0, 1, 0, -0.5D), is(new BoundingBox(0, 0, 0, 2, 1.5D, 2))); - assertThat(aabb.clone().expand(0, -1, 0, 0.5D), is(new BoundingBox(0, -0.5D, 0, 2, 2, 2))); - assertThat(aabb.clone().expand(0, -1, 0, -0.5D), is(new BoundingBox(0, 0.5D, 0, 2, 2, 2))); - - assertThat(aabb.clone().expand(0, 0, 1, 0.5D), is(new BoundingBox(0, 0, 0, 2, 2, 2.5D))); - assertThat(aabb.clone().expand(0, 0, 1, -0.5D), is(new BoundingBox(0, 0, 0, 2, 2, 1.5D))); - assertThat(aabb.clone().expand(0, 0, -1, 0.5D), is(new BoundingBox(0, 0, -0.5D, 2, 2, 2))); - assertThat(aabb.clone().expand(0, 0, -1, -0.5D), is(new BoundingBox(0, 0, 0.5D, 2, 2, 2))); - - assertThat(aabb.clone().expand(new Vector(1, 0, 0), 0.5D), is(new BoundingBox(0, 0, 0, 2.5D, 2, 2))); - assertThat(aabb.clone().expand(BlockFace.EAST, 0.5D), is(new BoundingBox(0, 0, 0, 2.5D, 2, 2))); - assertThat(aabb.clone().expand(BlockFace.NORTH_NORTH_WEST, 1.0D), is(aabb.clone().expand(BlockFace.NORTH_NORTH_WEST.getDirection(), 1.0D))); - assertThat(aabb.clone().expand(BlockFace.SELF, 1.0D), is(aabb)); - - BoundingBox expanded = aabb.clone().expand(BlockFace.NORTH_WEST, 1.0D); - assertThat(expanded.getWidthX(), is(closeTo(aabb.getWidthX() + Math.sqrt(0.5D), delta))); - assertThat(expanded.getWidthZ(), is(closeTo(aabb.getWidthZ() + Math.sqrt(0.5D), delta))); + assertThat(aabb.clone().expand(-0.5D), is(new BoundingBox(0.5, 0.5, 0.5, 1.5, 1.5, 1.5))); + + assertThat(aabb.clone().expand(1, 0, 0, 0.5), is(new BoundingBox(0, 0, 0, 2.5, 2, 2))); + assertThat(aabb.clone().expand(1, 0, 0, -0.5), is(new BoundingBox(0, 0, 0, 1.5, 2, 2))); + assertThat(aabb.clone().expand(-1, 0, 0, 0.5), is(new BoundingBox(-0.5, 0, 0, 2, 2, 2))); + assertThat(aabb.clone().expand(-1, 0, 0, -0.5), is(new BoundingBox(0.5, 0, 0, 2, 2, 2))); + + assertThat(aabb.clone().expand(0, 1, 0, 0.5), is(new BoundingBox(0, 0, 0, 2, 2.5, 2))); + assertThat(aabb.clone().expand(0, 1, 0, -0.5), is(new BoundingBox(0, 0, 0, 2, 1.5, 2))); + assertThat(aabb.clone().expand(0, -1, 0, 0.5), is(new BoundingBox(0, -0.5, 0, 2, 2, 2))); + assertThat(aabb.clone().expand(0, -1, 0, -0.5), is(new BoundingBox(0, 0.5, 0, 2, 2, 2))); + + assertThat(aabb.clone().expand(0, 0, 1, 0.5), is(new BoundingBox(0, 0, 0, 2, 2, 2.5))); + assertThat(aabb.clone().expand(0, 0, 1, -0.5), is(new BoundingBox(0, 0, 0, 2, 2, 1.5))); + assertThat(aabb.clone().expand(0, 0, -1, 0.5), is(new BoundingBox(0, 0, -0.5, 2, 2, 2))); + assertThat(aabb.clone().expand(0, 0, -1, -0.5), is(new BoundingBox(0, 0, 0.5, 2, 2, 2))); + + assertThat(aabb.clone().expand(new Vector(1, 0, 0), 0.5), is(new BoundingBox(0, 0, 0, 2.5, 2, 2))); + assertThat(aabb.clone().expand(BlockFace.EAST, 0.5), is(new BoundingBox(0, 0, 0, 2.5, 2, 2))); + assertThat(aabb.clone().expand(BlockFace.NORTH_NORTH_WEST, 1.0), is(aabb.clone().expand(BlockFace.NORTH_NORTH_WEST.getDirection(), 1.0))); + assertThat(aabb.clone().expand(BlockFace.SELF, 1.0), is(aabb)); + + BoundingBox expanded = aabb.clone().expand(BlockFace.NORTH_WEST, 1.0); + assertThat(expanded.getWidthX(), is(closeTo(aabb.getWidthX() + Math.sqrt(0.5), delta))); + assertThat(expanded.getWidthZ(), is(closeTo(aabb.getWidthZ() + Math.sqrt(0.5), delta))); assertThat(expanded.getHeight(), is(aabb.getHeight())); assertThat(aabb.clone().expandDirectional(1, 2, 3), is(new BoundingBox(0, 0, 0, 3, 4, 5))); @@ -176,7 +176,7 @@ public void testRayTrace() { assertThat(aabb.rayTrace(new Vector(0, 0, -3), new Vector(1, 0, 1), 10), is(nullValue())); assertThat(aabb.rayTrace(new Vector(0, 0, -2), new Vector(1, 0, 2), 10), - is(new RayTraceResult(new Vector(0.5D, 0, -1), BlockFace.NORTH))); + is(new RayTraceResult(new Vector(0.5, 0, -1), BlockFace.NORTH))); // corner/edge hits yield unspecified block face: assertThat(aabb.rayTrace(new Vector(2, 2, 2), new Vector(-1, -1, -1), 10), diff --git a/paper-generator/build.gradle.kts b/paper-generator/build.gradle.kts index d305a18dc35e..887de4322c85 100644 --- a/paper-generator/build.gradle.kts +++ b/paper-generator/build.gradle.kts @@ -24,10 +24,10 @@ dependencies { isTransitive = false // paper-api already have everything } implementation("info.picocli:picocli:4.7.7") - implementation("io.github.classgraph:classgraph:4.8.179") + implementation("io.github.classgraph:classgraph:4.8.184") implementation("org.jetbrains:annotations:26.0.2") - testImplementation("org.junit.jupiter:junit-jupiter:5.12.2") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") + testImplementation("org.junit.jupiter:junit-jupiter:6.0.3") + testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.0.3") serverRuntimeClasspath(project(":paper-server", "runtimeConfiguration")) } diff --git a/paper-generator/src/main/java/io/papermc/generator/Main.java b/paper-generator/src/main/java/io/papermc/generator/Main.java index 3a1ab1537111..41dd028d2d0e 100644 --- a/paper-generator/src/main/java/io/papermc/generator/Main.java +++ b/paper-generator/src/main/java/io/papermc/generator/Main.java @@ -85,10 +85,10 @@ public static CompletableFuture bootStrap(boolean withTags) { LayeredRegistryAccess layers = RegistryLayer.createRegistryAccess(); List> pendingTags = TagLoader.loadTagsForExistingRegistries(resourceManager, layers.getLayer(RegistryLayer.STATIC)); List> worldGenLayer = TagLoader.buildUpdatedLookups(layers.getAccessForLoading(RegistryLayer.WORLDGEN), pendingTags); - RegistryAccess.Frozen frozenWorldgenRegistries = RegistryDataLoader.load(resourceManager, worldGenLayer, RegistryDataLoader.WORLDGEN_REGISTRIES); + RegistryAccess.Frozen frozenWorldgenRegistries = RegistryDataLoader.load(resourceManager, worldGenLayer, RegistryDataLoader.WORLDGEN_REGISTRIES, Util.backgroundExecutor()).join(); layers = layers.replaceFrom(RegistryLayer.WORLDGEN, frozenWorldgenRegistries); List> staticAndWorldgenLookups = Stream.concat(worldGenLayer.stream(), frozenWorldgenRegistries.listRegistries()).toList(); - RegistryAccess.Frozen dimensionRegistries = RegistryDataLoader.load(resourceManager, staticAndWorldgenLookups, RegistryDataLoader.DIMENSION_REGISTRIES); + RegistryAccess.Frozen dimensionRegistries = RegistryDataLoader.load(resourceManager, staticAndWorldgenLookups, RegistryDataLoader.DIMENSION_REGISTRIES, Util.backgroundExecutor()).join(); layers = layers.replaceFrom(RegistryLayer.DIMENSIONS, dimensionRegistries); REGISTRY_ACCESS = layers.compositeAccess().freeze(); if (withTags) { @@ -106,7 +106,7 @@ public static CompletableFuture bootStrap(boolean withTags) { resourceManager.close(); } }).thenAccept(resources -> { - resources.updateStaticRegistryTags(); + resources.updateComponentsAndStaticRegistryTags(); EXPERIMENTAL_TAGS = ExperimentalCollector.collectTags(resourceManager); }); } else { diff --git a/paper-generator/src/main/java/io/papermc/generator/Rewriters.java b/paper-generator/src/main/java/io/papermc/generator/Rewriters.java index dee77d43ba9a..c880dad1aee2 100644 --- a/paper-generator/src/main/java/io/papermc/generator/Rewriters.java +++ b/paper-generator/src/main/java/io/papermc/generator/Rewriters.java @@ -26,6 +26,7 @@ import io.papermc.paper.datacomponent.item.SwingAnimation; import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation; import io.papermc.paper.dialog.Dialog; +import io.papermc.paper.item.MapPostProcessing; import io.papermc.paper.world.WeatheringCopperState; import io.papermc.typewriter.preset.EnumCloneRewriter; import io.papermc.typewriter.preset.model.EnumValue; @@ -160,6 +161,7 @@ protected EnumValue.Builder rewriteEnumValue(ParticleStatus status) { )) .register("ItemUseAnimation", ItemUseAnimation.class, new EnumCloneRewriter<>(net.minecraft.world.item.ItemUseAnimation.class)) .register("SwingAnimationType", SwingAnimation.Animation.class, new EnumCloneRewriter<>(SwingAnimationType.class)) + .register("MapPostProcessing", MapPostProcessing.class, new EnumCloneRewriter<>(net.minecraft.world.item.component.MapPostProcessing.class)) .register("ItemRarity", ItemRarity.class, new EnumCloneRewriter<>(Rarity.class) { @Override protected EnumValue.Builder rewriteEnumValue(Rarity rarity) { @@ -213,6 +215,7 @@ protected String rewriteFieldType(Holder.Reference(Registries.WOLF_VARIANT, "getVariant")) .register("WolfSoundVariant", Wolf.SoundVariant.class, new RegistryFieldRewriter<>(Registries.WOLF_SOUND_VARIANT, "getSoundVariant")) .register("CatType", Cat.Type.class, new RegistryFieldRewriter<>(Registries.CAT_VARIANT, "getType")) + .register("CatSoundVariant", Cat.SoundVariant.class, new RegistryFieldRewriter<>(Registries.CAT_SOUND_VARIANT, "getSoundVariant")) .register("FrogVariant", Frog.Variant.class, new RegistryFieldRewriter<>(Registries.FROG_VARIANT, "getVariant")) .register("PatternType", PatternType.class, new RegistryFieldRewriter<>(Registries.BANNER_PATTERN, "getType")) .register("Biome", Biome.class, new RegistryFieldRewriter<>(Registries.BIOME, "getBiome")) @@ -221,8 +224,11 @@ protected String rewriteFieldType(Holder.Reference(Registries.SOUND_EVENT, "getSound")) .register("Art", Art.class, new RegistryFieldRewriter<>(Registries.PAINTING_VARIANT, "getArt")) .register("ChickenVariant", Chicken.Variant.class, new RegistryFieldRewriter<>(Registries.CHICKEN_VARIANT, "getVariant")) + .register("ChickenSoundVariant", Chicken.SoundVariant.class, new RegistryFieldRewriter<>(Registries.CHICKEN_SOUND_VARIANT, "getSoundVariant")) .register("CowVariant", Cow.Variant.class, new RegistryFieldRewriter<>(Registries.COW_VARIANT, "getVariant")) + .register("CowSoundVariant", Cow.SoundVariant.class, new RegistryFieldRewriter<>(Registries.COW_SOUND_VARIANT, "getSoundVariant")) .register("PigVariant", Pig.Variant.class, new RegistryFieldRewriter<>(Registries.PIG_VARIANT, "getVariant")) + .register("PigSoundVariant", Pig.SoundVariant.class, new RegistryFieldRewriter<>(Registries.PIG_SOUND_VARIANT, "getSoundVariant")) .register("ZombieNautilusVariant", ZombieNautilus.Variant.class, new RegistryFieldRewriter<>(Registries.ZOMBIE_NAUTILUS_VARIANT, "getVariant")) .register("Dialog", Dialog.class, new RegistryFieldRewriter<>(Registries.DIALOG, "getDialog")) .register("MemoryKey", MemoryKey.class, new MemoryKeyRewriter()) @@ -236,7 +242,7 @@ protected String rewriteFieldType(Holder.Reference generators) { } }); - // todo remove once entity type is a registry + // todo remove once entity type and potion are both a registry generators.add(new GeneratedTagKeyType(RegistryEntries.byRegistryKey(Registries.ENTITY_TYPE), PAPER_REGISTRY_PACKAGE + ".keys.tags")); + generators.add(new GeneratedTagKeyType(RegistryEntries.byRegistryKey(Registries.POTION), PAPER_REGISTRY_PACKAGE + ".keys.tags")); } public static void bootstrap(PatternSourceSetRewriter apiSourceSet, PatternSourceSetRewriter serverSourceSet) { diff --git a/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java b/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java index 0a9f1e9a68f7..fe34adadee56 100644 --- a/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java +++ b/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java @@ -43,12 +43,16 @@ import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.memory.MemoryModuleType; -import net.minecraft.world.entity.animal.feline.CatVariants; +import net.minecraft.world.entity.animal.chicken.ChickenSoundVariants; import net.minecraft.world.entity.animal.chicken.ChickenVariants; +import net.minecraft.world.entity.animal.cow.CowSoundVariants; import net.minecraft.world.entity.animal.cow.CowVariants; -import net.minecraft.world.entity.animal.pig.PigVariants; -import net.minecraft.world.entity.animal.nautilus.ZombieNautilusVariants; +import net.minecraft.world.entity.animal.feline.CatSoundVariants; +import net.minecraft.world.entity.animal.feline.CatVariants; import net.minecraft.world.entity.animal.frog.FrogVariants; +import net.minecraft.world.entity.animal.nautilus.ZombieNautilusVariants; +import net.minecraft.world.entity.animal.pig.PigSoundVariants; +import net.minecraft.world.entity.animal.pig.PigVariants; import net.minecraft.world.entity.animal.wolf.WolfSoundVariants; import net.minecraft.world.entity.animal.wolf.WolfVariants; import net.minecraft.world.entity.decoration.painting.PaintingVariants; @@ -153,7 +157,8 @@ private static RegistryEntry inconsistentEntry(ResourceKey> REGISTRY_CLASS_NAME_BASED_ON_API = Set.of( BlockType.class, - ItemType.class + ItemType.class, + PotionType.class ); public static final List> BUILT_IN = List.of( @@ -187,10 +192,14 @@ private static RegistryEntry inconsistentEntry(ResourceKey> reference) { .orElseThrow(() -> new IllegalStateException("Could not find entity class for " + reference.key().identifier())) .resolve(runtime); Preconditions.checkArgument(org.bukkit.entity.Entity.class.isAssignableFrom(resolvedClass.knownClass()), "Generic type must be an entity"); - return this.importCollector.getShortName(this.classNamedView.findFirst(CLASS_RENAMES.getOrDefault(className, className)).resolve(runtime)); + return this.importCollector.getShortName(resolvedClass); } } diff --git a/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/trial/PoseRewriter.java b/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/trial/PoseRewriter.java index 42dc3f8b0cc4..2288a15627ea 100644 --- a/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/trial/PoseRewriter.java +++ b/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/trial/PoseRewriter.java @@ -51,7 +51,7 @@ private Map parseConstantJavadocs(String content }) .skipClosure(TokenType.LPAREN, TokenType.RPAREN, true, TokenTaskBuilder::asOptional) // (*)? .skipClosure(TokenType.LSCOPE, TokenType.RSCOPE, true, TokenTaskBuilder::asOptional) // {*}? - .map(END_VALUE_MARKERS::contains, $ -> { // ;|, + .map(END_VALUE_MARKERS::contains, _ -> { // ;|, // this part will fail for the last entry for enum without end (,;) if (constant.isComplete()) { map.put(constant.name(), constant.javadocs()); diff --git a/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/trial/VillagerProfessionRewriter.java b/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/trial/VillagerProfessionRewriter.java index e23548e36c9c..5425b0d21325 100644 --- a/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/trial/VillagerProfessionRewriter.java +++ b/paper-generator/src/main/java/io/papermc/generator/rewriter/types/simple/trial/VillagerProfessionRewriter.java @@ -49,7 +49,7 @@ private Map parseConstantJavadocs(String content }) .skip(TokenType.IDENTIFIER) .skipClosure(TokenType.LPAREN, TokenType.RPAREN, true) - .map(TokenType.SECO, $ -> { + .map(TokenType.SECO, _ -> { if (constant.isComplete()) { map.put(constant.name(), constant.javadocs()); } @@ -71,7 +71,7 @@ private Map parseConstantJavadocs(String content }) .skipClosure(TokenType.LPAREN, TokenType.RPAREN, true) .skipClosure(TokenType.LSCOPE, TokenType.RSCOPE, true) - .map(endMarkers::contains, $ -> { + .map(endMarkers::contains, _ -> { // this part will probably fail for the last entry for enum without end (,;) if (constant.isComplete()) { map.put(constant.name(), constant.javadocs()); diff --git a/paper-generator/src/main/java/io/papermc/generator/utils/BlockStateMapping.java b/paper-generator/src/main/java/io/papermc/generator/utils/BlockStateMapping.java index b8787b0939ac..65f77112b593 100644 --- a/paper-generator/src/main/java/io/papermc/generator/utils/BlockStateMapping.java +++ b/paper-generator/src/main/java/io/papermc/generator/utils/BlockStateMapping.java @@ -130,7 +130,6 @@ public record BlockData(String implName, @Nullable Class "$project-$version-$build.jar" } } } diff --git a/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch index d1782ae71933..0d6d80473524 100644 --- a/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch +++ b/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch @@ -19,7 +19,7 @@ See https://github.com/Tuinity/Moonrise diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java new file mode 100644 -index 0000000000000000000000000000000000000000..1b8193587814225c2ef2c5d9e667436eb50ff6c5 +index 0000000000000000000000000000000000000000..fff7a796a61542213bdaa417d44e25cd05f7e774 --- /dev/null +++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java @@ -0,0 +1,273 @@ @@ -107,7 +107,7 @@ index 0000000000000000000000000000000000000000..1b8193587814225c2ef2c5d9e667436e + + for (int i = 0; i < TOTAL_MAP_TYPES; ++i) { + // use 0 for default, will be updated by tickPlayer -+ (newTrackers[i] = new TrackedPlayer(player, MAP_TYPES[i])).add(chunk.x, chunk.z, 0); ++ (newTrackers[i] = new TrackedPlayer(player, MAP_TYPES[i])).add(chunk.x(), chunk.z(), 0); + } + + // update view distances @@ -143,12 +143,12 @@ index 0000000000000000000000000000000000000000..1b8193587814225c2ef2c5d9e667436e + + final ChunkPos chunk = player.chunkPosition(); + -+ players[NearbyMapType.GENERAL.ordinal()].update(chunk.x, chunk.z, GENERAL_AREA_VIEW_DISTANCE); -+ players[NearbyMapType.GENERAL_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_SMALL_VIEW_DISTANCE); -+ players[NearbyMapType.GENERAL_REALLY_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_REALLY_SMALL_VIEW_DISTANCE); -+ players[NearbyMapType.TICK_VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, PlatformHooks.get().getTickViewDistance(player)); -+ players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, PlatformHooks.get().getViewDistance(player)); -+ players[NearbyMapType.SPAWN_RANGE.ordinal()].update(chunk.x, chunk.z, ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); // Moonrise - chunk tick iteration ++ players[NearbyMapType.GENERAL.ordinal()].update(chunk.x(), chunk.z(), GENERAL_AREA_VIEW_DISTANCE); ++ players[NearbyMapType.GENERAL_SMALL.ordinal()].update(chunk.x(), chunk.z(), GENERAL_SMALL_VIEW_DISTANCE); ++ players[NearbyMapType.GENERAL_REALLY_SMALL.ordinal()].update(chunk.x(), chunk.z(), GENERAL_REALLY_SMALL_VIEW_DISTANCE); ++ players[NearbyMapType.TICK_VIEW_DISTANCE.ordinal()].update(chunk.x(), chunk.z(), PlatformHooks.get().getTickViewDistance(player)); ++ players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x(), chunk.z(), PlatformHooks.get().getViewDistance(player)); ++ players[NearbyMapType.SPAWN_RANGE.ordinal()].update(chunk.x(), chunk.z(), ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); // Moonrise - chunk tick iteration + } + + public TrackedChunk getChunk(final ChunkPos pos) { @@ -279,7 +279,7 @@ index 0000000000000000000000000000000000000000..1b8193587814225c2ef2c5d9e667436e + + final TrackedChunk chunk = NearbyPlayers.this.byChunk.get(chunkKey); + if (chunk == null) { -+ throw new IllegalStateException("Chunk should exist at " + new ChunkPos(chunkKey)); ++ throw new IllegalStateException("Chunk should exist at " + ChunkPos.unpack(chunkKey)); + } + + final NearbyMapType type = this.type; @@ -297,7 +297,7 @@ index 0000000000000000000000000000000000000000..1b8193587814225c2ef2c5d9e667436e + } +} diff --git a/ca/spottedleaf/moonrise/paper/PaperHooks.java b/ca/spottedleaf/moonrise/paper/PaperHooks.java -index 2a5069d11f47d11726f08b597988f8a58546824b..bf051b6443edd32ecdbd177e02f40965477638de 100644 +index 292898e3f7563949288fcafb8893e80cb4191828..d69358d60146e47b7f4669c43afc6c97cbde276f 100644 --- a/ca/spottedleaf/moonrise/paper/PaperHooks.java +++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java @@ -13,6 +13,7 @@ import net.minecraft.server.level.ChunkHolder; @@ -335,7 +335,7 @@ index 2a5069d11f47d11726f08b597988f8a58546824b..bf051b6443edd32ecdbd177e02f40965 } diff --git a/ca/spottedleaf/moonrise/paper/util/BaseChunkSystemHooks.java b/ca/spottedleaf/moonrise/paper/util/BaseChunkSystemHooks.java -index ce81e9f4f1e80be3b4f0cbdd95609990f9006534..3e7b616cbd196f56393719e84c7118ab47308337 100644 +index ce81e9f4f1e80be3b4f0cbdd95609990f9006534..4baf46256e90a87d684e57ff623076a8e9395f31 100644 --- a/ca/spottedleaf/moonrise/paper/util/BaseChunkSystemHooks.java +++ b/ca/spottedleaf/moonrise/paper/util/BaseChunkSystemHooks.java @@ -1,38 +1,23 @@ @@ -591,7 +591,7 @@ index ce81e9f4f1e80be3b4f0cbdd95609990f9006534..3e7b616cbd196f56393719e84c7118ab public void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder) { - + ((ChunkSystemServerChunkCache)((ServerLevel)chunk.getLevel()).getChunkSource()) -+ .moonrise$setFullChunk(chunk.getPos().x, chunk.getPos().z, chunk); ++ .moonrise$setFullChunk(chunk.getPos().x(), chunk.getPos().z(), chunk); } @Override @@ -612,7 +612,7 @@ index ce81e9f4f1e80be3b4f0cbdd95609990f9006534..3e7b616cbd196f56393719e84c7118ab public void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder) { - + ((ChunkSystemServerChunkCache)((ServerLevel)chunk.getLevel()).getChunkSource()) -+ .moonrise$setFullChunk(chunk.getPos().x, chunk.getPos().z, null); ++ .moonrise$setFullChunk(chunk.getPos().x(), chunk.getPos().z(), null); } @Override @@ -750,41 +750,37 @@ index 0000000000000000000000000000000000000000..89e75b454695e174c5619104eeb15eb9 +} diff --git a/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/PropertyAccessStateHolder.java b/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/PropertyAccessStateHolder.java new file mode 100644 -index 0000000000000000000000000000000000000000..01da52b9e8a786824f199a057b62ce0431ecbc43 +index 0000000000000000000000000000000000000000..5c9f02e82a9ce3511e710d8d00fc03bb6ca41968 --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/PropertyAccessStateHolder.java -@@ -0,0 +1,7 @@ +@@ -0,0 +1,11 @@ +package ca.spottedleaf.moonrise.patches.blockstate_propertyaccess; + -+public interface PropertyAccessStateHolder { ++import java.util.Collection; ++ ++public interface PropertyAccessStateHolder { + + public long moonrise$getTableIndex(); + ++ public void moonrise$init(final Collection states); ++ +} diff --git a/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/util/ZeroCollidingReferenceStateTable.java b/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/util/ZeroCollidingReferenceStateTable.java new file mode 100644 -index 0000000000000000000000000000000000000000..866f38eb0f379ffbe2888023a7d1c290f521a231 +index 0000000000000000000000000000000000000000..ce4ce04d42fcdfada56d1cec0d1a1a2f3ef02ca4 --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/util/ZeroCollidingReferenceStateTable.java -@@ -0,0 +1,230 @@ +@@ -0,0 +1,166 @@ +package ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.util; + +import ca.spottedleaf.concurrentutil.util.IntegerUtil; +import ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccess; +import ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccessStateHolder; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -+import it.unimi.dsi.fastutil.objects.AbstractObjectSet; -+import it.unimi.dsi.fastutil.objects.AbstractReference2ObjectMap; -+import it.unimi.dsi.fastutil.objects.ObjectIterator; -+import it.unimi.dsi.fastutil.objects.ObjectSet; -+import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; -+import java.util.ArrayList; ++import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; -+import java.util.Iterator; -+import java.util.List; -+import java.util.Map; +import net.minecraft.world.level.block.state.StateHolder; +import net.minecraft.world.level.block.state.properties.Property; + @@ -794,15 +790,15 @@ index 0000000000000000000000000000000000000000..866f38eb0f379ffbe2888023a7d1c290 + private S[] lookup; + private final Collection> properties; + -+ public ZeroCollidingReferenceStateTable(final Collection> properties) { -+ this.propertyToIndexer = new Int2ObjectOpenHashMap<>(properties.size()); -+ this.properties = new ReferenceArrayList<>(properties); ++ public ZeroCollidingReferenceStateTable(final Property[] properties) { ++ this.propertyToIndexer = new Int2ObjectOpenHashMap<>(properties.length); ++ this.properties = ReferenceArrayList.wrap(properties.clone()); + -+ final List> sortedProperties = new ArrayList<>(properties); ++ final Property[] sortedProperties = properties.clone(); + + // important that each table sees the same property order given the same _set_ of properties, + // as each table will calculate the index for the block state -+ sortedProperties.sort((final Property p1, final Property p2) -> { ++ Arrays.sort(sortedProperties, (final Property p1, final Property p2) -> { + return Integer.compare( + ((PropertyAccess)p1).moonrise$getId(), + ((PropertyAccess)p2).moonrise$getId() @@ -831,16 +827,16 @@ index 0000000000000000000000000000000000000000..866f38eb0f379ffbe2888023a7d1c290 + return this.propertyToIndexer.containsKey(((PropertyAccess)property).moonrise$getId()); + } + -+ public long getIndex(final StateHolder stateHolder) { ++ public long getIndex(final StateHolder stateHolder, final Property[] keys, final Comparable[] values) { + long ret = 0L; + -+ for (final Map.Entry, Comparable> entry : stateHolder.getValues().entrySet()) { -+ final Property property = entry.getKey(); -+ final Comparable value = entry.getValue(); ++ for (int i = 0; i < keys.length; ++i) { ++ final Property property = keys[i]; ++ final Comparable value = values[i]; + + final Indexer indexer = this.propertyToIndexer.get(((PropertyAccess)property).moonrise$getId()); + -+ ret += (((PropertyAccess)property).moonrise$getIdFor(value)) * indexer.multiple; ++ ret += ((long)((PropertyAccess)property).moonrise$getIdFor(value)) * (long)indexer.multiple; + } + + return ret; @@ -850,15 +846,14 @@ index 0000000000000000000000000000000000000000..866f38eb0f379ffbe2888023a7d1c290 + return this.lookup != null; + } + -+ public void loadInTable(final Map, Comparable>, S> universe) { ++ public void loadInTable(final Collection states) { + if (this.lookup != null) { + throw new IllegalStateException(); + } + -+ this.lookup = (S[])new StateHolder[universe.size()]; ++ this.lookup = (S[])new StateHolder[states.size()]; + -+ for (final Map.Entry, Comparable>, S> entry : universe.entrySet()) { -+ final S value = entry.getValue(); ++ for (final S value : states) { + if (value == null) { + continue; + } @@ -930,72 +925,17 @@ index 0000000000000000000000000000000000000000..866f38eb0f379ffbe2888023a7d1c290 + return this.lookup[(int)newIndex]; + } + -+ public Collection> getProperties() { -+ return Collections.unmodifiableCollection(this.properties); ++ public boolean isSingletonState() { ++ return this.properties.isEmpty(); + } + -+ public Map, Comparable> getMapView(final long stateIndex) { -+ return new MapView(stateIndex); ++ public Collection> getProperties() { ++ return Collections.unmodifiableCollection(this.properties); + } + + private static final record Indexer( + int totalValues, int multiple, long multipleDivMagic, long modMagic + ) {} -+ -+ private class MapView extends AbstractReference2ObjectMap, Comparable> { -+ private final long stateIndex; -+ private EntrySet entrySet; -+ -+ MapView(final long stateIndex) { -+ this.stateIndex = stateIndex; -+ } -+ -+ @Override -+ public boolean containsKey(final Object key) { -+ return key instanceof Property prop && ZeroCollidingReferenceStateTable.this.hasProperty(prop); -+ } -+ -+ @Override -+ public int size() { -+ return ZeroCollidingReferenceStateTable.this.properties.size(); -+ } -+ -+ @Override -+ public ObjectSet, Comparable>> reference2ObjectEntrySet() { -+ if (this.entrySet == null) -+ this.entrySet = new EntrySet(); -+ return this.entrySet; -+ } -+ -+ @Override -+ public Comparable get(final Object key) { -+ return key instanceof Property prop ? ZeroCollidingReferenceStateTable.this.get(this.stateIndex, prop) : null; -+ } -+ -+ class EntrySet extends AbstractObjectSet, Comparable>> { -+ @Override -+ public ObjectIterator, Comparable>> iterator() { -+ final Iterator> propIterator = ZeroCollidingReferenceStateTable.this.properties.iterator(); -+ return new ObjectIterator<>() { -+ @Override -+ public boolean hasNext() { -+ return propIterator.hasNext(); -+ } -+ -+ @Override -+ public Entry, Comparable> next() { -+ Property prop = propIterator.next(); -+ return new AbstractReference2ObjectMap.BasicEntry<>(prop, ZeroCollidingReferenceStateTable.this.get(MapView.this.stateIndex, prop)); -+ } -+ }; -+ } -+ -+ @Override -+ public int size() { -+ return ZeroCollidingReferenceStateTable.this.properties.size(); -+ } -+ } -+ } +} diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystemConverters.java b/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystemConverters.java new file mode 100644 @@ -1043,7 +983,7 @@ index 0000000000000000000000000000000000000000..02d596647ab78afb056eefe14ffa0ef2 +} diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/MoonriseChunkLoadCounter.java b/ca/spottedleaf/moonrise/patches/chunk_system/MoonriseChunkLoadCounter.java new file mode 100644 -index 0000000000000000000000000000000000000000..f77ba1b6aaedf6c8c1e0c49ff2779eb42b8eb90d +index 0000000000000000000000000000000000000000..3c3c325fd8066120c652f26ad4b26743fdbadbce --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/chunk_system/MoonriseChunkLoadCounter.java @@ -0,0 +1,64 @@ @@ -1079,10 +1019,10 @@ index 0000000000000000000000000000000000000000..f77ba1b6aaedf6c8c1e0c49ff2779eb4 + this.expected = (chunkRadius * 2 + 1) * (chunkRadius * 2 + 1); + final CompletableFuture ret = new CompletableFuture<>(); + ((ChunkSystemServerLevel) level).moonrise$loadChunksAsync( -+ chunkPos.x - chunkRadius, -+ chunkPos.x + chunkRadius, -+ chunkPos.z - chunkRadius, -+ chunkPos.z + chunkRadius, ++ chunkPos.x() - chunkRadius, ++ chunkPos.x() + chunkRadius, ++ chunkPos.z() - chunkRadius, ++ chunkPos.z() + chunkRadius, + status, + priority, + (chunks) -> { @@ -1201,10 +1141,10 @@ index 0000000000000000000000000000000000000000..a814512fcfb85312474ae2c2c2144384 +} diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java new file mode 100644 -index 0000000000000000000000000000000000000000..344e155420de9148cd402f626af11d4c523cacff +index 0000000000000000000000000000000000000000..27eeb842e6b22bd12b582379eaaa76b622dfca5e --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java -@@ -0,0 +1,1489 @@ +@@ -0,0 +1,1488 @@ +package ca.spottedleaf.moonrise.patches.chunk_system.io; + +import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; @@ -1213,8 +1153,7 @@ index 0000000000000000000000000000000000000000..344e155420de9148cd402f626af11d4c +import ca.spottedleaf.concurrentutil.executor.Cancellable; +import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; +import ca.spottedleaf.concurrentutil.executor.queue.AreaDependentQueue; -+import ca.spottedleaf.concurrentutil.function.BiLong1Function; -+import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; ++import ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2ReferenceHashTable; +import ca.spottedleaf.concurrentutil.util.ConcurrentUtil; +import ca.spottedleaf.concurrentutil.util.Priority; +import ca.spottedleaf.moonrise.common.util.CoordinateUtils; @@ -1979,7 +1918,7 @@ index 0000000000000000000000000000000000000000..344e155420de9148cd402f626af11d4c + final ImmediateCallbackCompletion callbackInfo = new ImmediateCallbackCompletion(); + + final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ); -+ final BiLong1Function compute = (final long keyInMap, final ChunkIOTask running) -> { ++ final ConcurrentChainedLong2ReferenceHashTable.BiLongObjectObjectFunction compute = (final long keyInMap, final ChunkIOTask running) -> { + if (running == null) { + // not scheduled + @@ -2627,7 +2566,7 @@ index 0000000000000000000000000000000000000000..344e155420de9148cd402f626af11d4c + + public final RegionFileType type; + private final PrioritisedExecutor compressionExecutor; -+ private final ConcurrentLong2ReferenceChainedHashTable chunkTasks = new ConcurrentLong2ReferenceChainedHashTable<>(); ++ private final ConcurrentChainedLong2ReferenceHashTable chunkTasks = new ConcurrentChainedLong2ReferenceHashTable<>(); + private final AreaDependentQueue regionIoQueue; + + private final AtomicLong inProgressTasks = new AtomicLong(); @@ -2696,20 +2635,18 @@ index 0000000000000000000000000000000000000000..344e155420de9148cd402f626af11d4c +} diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/ChunkDataController.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/ChunkDataController.java new file mode 100644 -index 0000000000000000000000000000000000000000..633e85bf7824fec10c81b2a64756a7ef756cd481 +index 0000000000000000000000000000000000000000..a3250959137c559ddfc78833b50f0aa51b2639a7 --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/ChunkDataController.java -@@ -0,0 +1,48 @@ +@@ -0,0 +1,45 @@ +package ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller; + +import ca.spottedleaf.moonrise.patches.chunk_system.io.ChunkSystemRegionFileStorage; +import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; -+import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemChunkMap; +import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; +import ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemSimpleRegionStorage; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; -+import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.storage.RegionFileStorage; +import java.io.IOException; + @@ -2734,7 +2671,6 @@ index 0000000000000000000000000000000000000000..633e85bf7824fec10c81b2a64756a7ef + + @Override + public void finishWrite(final int chunkX, final int chunkZ, final WriteData writeData) throws IOException { -+ ((ChunkSystemChunkMap)this.world.getChunkSource().chunkMap).moonrise$writeFinishCallback(new ChunkPos(chunkX, chunkZ)); + ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$finishWrite(chunkX, chunkZ, writeData); + } + @@ -2878,22 +2814,6 @@ index 0000000000000000000000000000000000000000..bd0d782852f9cfe5bc0b5339ecf4d82c + return ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$finishRead(chunkX, chunkZ, readData); + } +} -diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemChunkMap.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemChunkMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..47a4d3376d08dde94a39254bec21473ff27f53e6 ---- /dev/null -+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemChunkMap.java -@@ -0,0 +1,10 @@ -+package ca.spottedleaf.moonrise.patches.chunk_system.level; -+ -+import net.minecraft.world.level.ChunkPos; -+import java.io.IOException; -+ -+public interface ChunkSystemChunkMap { -+ -+ public void moonrise$writeFinishCallback(final ChunkPos pos) throws IOException; -+ -+} diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemLevel.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemLevel.java new file mode 100644 index 0000000000000000000000000000000000000000..5d4d650186b18eb00782429d53d861564d8e4ba9 @@ -3155,7 +3075,7 @@ index 0000000000000000000000000000000000000000..59cbbf65c9df5c680b803f6dd6436a35 +} diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java new file mode 100644 -index 0000000000000000000000000000000000000000..c2363cfa5e93942fe837efd9f39478698f6d1a98 +index 0000000000000000000000000000000000000000..0cfdbdee091f43ca011bc68f7479eb7b84801b09 --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java @@ -0,0 +1,591 @@ @@ -3278,7 +3198,7 @@ index 0000000000000000000000000000000000000000..c2363cfa5e93942fe837efd9f3947869 + + final ListTag entitiesTag = new ListTag(); + try (final ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(ChunkAccess.problemPath(chunkPos), LOGGER)) { -+ for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x, chunkPos.z, entities)) { ++ for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x(), chunkPos.z(), entities)) { + final TagValueOutput savedEntity = TagValueOutput.createWithContext( + scopedCollector.forChild(entity.problemPath()), entity.registryAccess() + ); @@ -3752,14 +3672,14 @@ index 0000000000000000000000000000000000000000..c2363cfa5e93942fe837efd9f3947869 +} diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java new file mode 100644 -index 0000000000000000000000000000000000000000..2d24d03bbdb5ee0d862cbfff2219f58afffafe12 +index 0000000000000000000000000000000000000000..56e71bf01f22579c294c6b33450bd282e082f6ae --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java @@ -0,0 +1,1006 @@ +package ca.spottedleaf.moonrise.patches.chunk_system.level.entity; + -+import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; +import ca.spottedleaf.concurrentutil.map.SWMRLong2ObjectHashTable; ++import ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2ReferenceHashTable; +import ca.spottedleaf.moonrise.common.list.EntityList; +import ca.spottedleaf.moonrise.common.util.CoordinateUtils; +import ca.spottedleaf.moonrise.common.util.WorldUtil; @@ -3806,7 +3726,7 @@ index 0000000000000000000000000000000000000000..2d24d03bbdb5ee0d862cbfff2219f58a + + protected final LevelCallback worldCallback; + -+ protected final ConcurrentLong2ReferenceChainedHashTable entityById = new ConcurrentLong2ReferenceChainedHashTable<>(); ++ protected final ConcurrentChainedLong2ReferenceHashTable entityById = new ConcurrentChainedLong2ReferenceHashTable<>(); + protected final ConcurrentHashMap entityByUUID = new ConcurrentHashMap<>(); + protected final EntityList accessibleEntities = new EntityList(); + @@ -5481,17 +5401,17 @@ index 0000000000000000000000000000000000000000..003a857e70ead858e8437e3c1bfaf22f +} diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java new file mode 100644 -index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f5589396f4e4e9 +index 0000000000000000000000000000000000000000..0743659c9f1122088adad4992b93649efc9ba873 --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java -@@ -0,0 +1,1093 @@ +@@ -0,0 +1,1104 @@ +package ca.spottedleaf.moonrise.patches.chunk_system.player; + +import ca.spottedleaf.concurrentutil.util.ConcurrentUtil; +import ca.spottedleaf.concurrentutil.util.Priority; +import ca.spottedleaf.moonrise.common.PlatformHooks; -+import ca.spottedleaf.moonrise.common.misc.AllocatingRateLimiter; +import ca.spottedleaf.moonrise.common.misc.SingleUserAreaMap; ++import ca.spottedleaf.moonrise.common.misc.StaggeredRateLimiter; +import ca.spottedleaf.moonrise.common.util.CoordinateUtils; +import ca.spottedleaf.moonrise.common.util.MoonriseConstants; +import ca.spottedleaf.moonrise.common.util.TickThread; @@ -5853,13 +5773,24 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + } + + // rate limiting -+ private static final long ALLOCATION_GRANULARITY = TimeUnit.SECONDS.toNanos(1L); -+ private final AllocatingRateLimiter chunkSendLimiter = new AllocatingRateLimiter(ALLOCATION_GRANULARITY); -+ private final AllocatingRateLimiter chunkLoadTicketLimiter = new AllocatingRateLimiter(ALLOCATION_GRANULARITY); -+ private final AllocatingRateLimiter chunkGenerateTicketLimiter = new AllocatingRateLimiter(ALLOCATION_GRANULARITY); ++ private static final double INITIAL_ALLOCATION_FACTOR = 0.0; ++ ++ private static StaggeredRateLimiter createChunkLimiter() { ++ // allow bursts over small intervals but keep it in check over the long interval ++ return StaggeredRateLimiter.builder() ++ .add(100L, TimeUnit.MILLISECONDS, 4.0) ++ .add(500L, TimeUnit.MILLISECONDS, 2.0) ++ .add(1_000L, TimeUnit.MILLISECONDS, 1.25) ++ .add(3_000L, TimeUnit.SECONDS, 1.0) ++ .build(); ++ } ++ ++ private final StaggeredRateLimiter chunkSendLimiter = createChunkLimiter(); ++ private final StaggeredRateLimiter chunkLoadTicketLimiter = createChunkLimiter(); ++ private final StaggeredRateLimiter chunkGenerateTicketLimiter = createChunkLimiter(); + + // queues -+ private final LongComparator CLOSEST_MANHATTAN_DIST = (final long c1, final long c2) -> { ++ private final LongComparator queueComparator = (final long c1, final long c2) -> { + final int c1x = CoordinateUtils.getChunkX(c1); + final int c1z = CoordinateUtils.getChunkZ(c1); + @@ -5869,17 +5800,24 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + final int centerX = PlayerChunkLoaderData.this.lastChunkX; + final int centerZ = PlayerChunkLoaderData.this.lastChunkZ; + ++ // note: VD < 2^15, so we shouldn't worry about overflow ++ final int diff1X = c1x - centerX; ++ final int diff1Z = c1z - centerZ; ++ ++ final int diff2X = c2x - centerX; ++ final int diff2Z = c2z - centerZ; ++ + return Integer.compare( -+ Math.abs(c1x - centerX) + Math.abs(c1z - centerZ), -+ Math.abs(c2x - centerX) + Math.abs(c2z - centerZ) ++ (diff1X * diff1X) + (diff1Z * diff1Z), ++ (diff2X * diff2X) + (diff2Z * diff2Z) + ); + }; -+ private final LongHeapPriorityQueue sendQueue = new LongHeapPriorityQueue(CLOSEST_MANHATTAN_DIST); -+ private final LongHeapPriorityQueue tickingQueue = new LongHeapPriorityQueue(CLOSEST_MANHATTAN_DIST); -+ private final LongHeapPriorityQueue generatingQueue = new LongHeapPriorityQueue(CLOSEST_MANHATTAN_DIST); -+ private final LongHeapPriorityQueue genQueue = new LongHeapPriorityQueue(CLOSEST_MANHATTAN_DIST); -+ private final LongHeapPriorityQueue loadingQueue = new LongHeapPriorityQueue(CLOSEST_MANHATTAN_DIST); -+ private final LongHeapPriorityQueue loadQueue = new LongHeapPriorityQueue(CLOSEST_MANHATTAN_DIST); ++ private final LongHeapPriorityQueue sendQueue = new LongHeapPriorityQueue(this.queueComparator); ++ private final LongHeapPriorityQueue tickingQueue = new LongHeapPriorityQueue(this.queueComparator); ++ private final LongHeapPriorityQueue generatingQueue = new LongHeapPriorityQueue(this.queueComparator); ++ private final LongHeapPriorityQueue genQueue = new LongHeapPriorityQueue(this.queueComparator); ++ private final LongHeapPriorityQueue loadingQueue = new LongHeapPriorityQueue(this.queueComparator); ++ private final LongHeapPriorityQueue loadQueue = new LongHeapPriorityQueue(this.queueComparator); + + private volatile boolean removed; + @@ -5930,7 +5868,7 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ))); + // Paper start - PlayerChunkUnloadEvent + if (io.papermc.paper.event.packet.PlayerChunkUnloadEvent.getHandlerList().getRegisteredListeners().length > 0) { -+ new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(player.getBukkitEntity().getWorld().getChunkAt(new ChunkPos(chunkX, chunkZ).longKey), player.getBukkitEntity()).callEvent(); ++ new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(player.getBukkitEntity().getWorld().getChunkAt(new ChunkPos(chunkX, chunkZ).longKey()), player.getBukkitEntity()).callEvent(); + } + // Paper end - PlayerChunkUnloadEvent + } @@ -6138,9 +6076,9 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + final double genRate = this.getMaxChunkGenRate(); + final double sendRate = this.getMaxChunkSendRate(); + -+ this.chunkLoadTicketLimiter.tickAllocation(time, loadRate, loadRate); -+ this.chunkGenerateTicketLimiter.tickAllocation(time, genRate, genRate); -+ this.chunkSendLimiter.tickAllocation(time, sendRate, sendRate); ++ this.chunkLoadTicketLimiter.tick(time, loadRate, INITIAL_ALLOCATION_FACTOR); ++ this.chunkGenerateTicketLimiter.tick(time, genRate, INITIAL_ALLOCATION_FACTOR); ++ this.chunkSendLimiter.tick(time, sendRate, INITIAL_ALLOCATION_FACTOR); + + // try to progress chunk loads + while (!this.loadingQueue.isEmpty()) { @@ -6168,7 +6106,7 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + + // try to push more chunk loads + final long maxLoads = Math.max(0L, Math.min(MAX_RATE, Math.min(this.loadQueue.size(), this.getMaxChunkLoads()))); -+ final int maxLoadsThisTick = (int)this.chunkLoadTicketLimiter.takeAllocation(time, loadRate, maxLoads); ++ final int maxLoadsThisTick = (int)this.chunkLoadTicketLimiter.takeAllocation(maxLoads); + if (maxLoadsThisTick > 0) { + final LongArrayList chunks = new LongArrayList(maxLoadsThisTick); + for (int i = 0; i < maxLoadsThisTick; ++i) { @@ -6243,8 +6181,7 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + + // try to push more chunk generations + final long maxGens = Math.max(0L, Math.min(MAX_RATE, Math.min(this.genQueue.size(), this.getMaxChunkGenerates()))); -+ // preview the allocations, as we may not actually utilise all of them -+ final long maxGensThisTick = this.chunkGenerateTicketLimiter.previewAllocation(time, genRate, maxGens); ++ final long maxGensThisTick = this.chunkGenerateTicketLimiter.takeAllocation(maxGens); + long ratedGensThisTick = 0L; + while (!this.genQueue.isEmpty()) { + final long chunkKey = this.genQueue.firstLong(); @@ -6275,7 +6212,7 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + this.generatingQueue.enqueue(chunkKey); + } + // take the allocations we actually used -+ this.chunkGenerateTicketLimiter.takeAllocation(time, genRate, ratedGensThisTick); ++ this.chunkGenerateTicketLimiter.returnUnused(maxGensThisTick - ratedGensThisTick); + + // try to pull ticking chunks + while (!this.tickingQueue.isEmpty()) { @@ -6305,10 +6242,10 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + } + + // try to pull sending chunks -+ final long maxSends = Math.max(0L, Math.min(MAX_RATE, Integer.MAX_VALUE)); // note: no logic to track concurrent sends -+ final int maxSendsThisTick = Math.min((int)this.chunkSendLimiter.takeAllocation(time, sendRate, maxSends), this.sendQueue.size()); ++ final long maxSends = Math.max(0L, Math.min(MAX_RATE, Math.min(this.sendQueue.size(), Integer.MAX_VALUE))); // note: no logic to track concurrent sends ++ final long maxSendsThisTick = this.chunkSendLimiter.takeAllocation(maxSends); + // we do not return sends that we took from the allocation back because we want to limit the max send rate, not target it -+ for (int i = 0; i < maxSendsThisTick; ++i) { ++ for (long i = 0; i < maxSendsThisTick; ++i) { + final long pendingSend = this.sendQueue.firstLong(); + final int pendingSendX = CoordinateUtils.getChunkX(pendingSend); + final int pendingSendZ = CoordinateUtils.getChunkZ(pendingSend); @@ -6346,8 +6283,8 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + } + final ViewDistances playerDistances = ((ChunkSystemServerPlayer)this.player).moonrise$getViewDistanceHolder().getViewDistances(); + final ViewDistances worldDistances = ((ChunkSystemServerLevel)this.world).moonrise$getViewDistanceHolder().getViewDistances(); -+ final int chunkX = this.player.chunkPosition().x; -+ final int chunkZ = this.player.chunkPosition().z; ++ final int chunkX = this.player.chunkPosition().x(); ++ final int chunkZ = this.player.chunkPosition().z(); + + final int tickViewDistance = getTickDistance( + playerDistances.tickViewDistance, worldDistances.tickViewDistance, @@ -6371,12 +6308,6 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + // update chunk center + this.player.connection.send(this.updateClientChunkCenter(chunkX, chunkZ)); + -+ // reset limiters, they will start at a zero allocation -+ final long time = System.nanoTime(); -+ this.chunkLoadTicketLimiter.reset(time); -+ this.chunkGenerateTicketLimiter.reset(time); -+ this.chunkSendLimiter.reset(time); -+ + // now we can update + this.update(); + } @@ -6414,8 +6345,8 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + + final ChunkPos playerPos = this.player.chunkPosition(); + final boolean canGenerateChunks = this.canPlayerGenerateChunks(); -+ final int currentChunkX = playerPos.x; -+ final int currentChunkZ = playerPos.z; ++ final int currentChunkX = playerPos.x(); ++ final int currentChunkZ = playerPos.z(); + + final int prevChunkX = this.lastChunkX; + final int prevChunkZ = this.lastChunkZ; @@ -6469,7 +6400,7 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 + this.canGenerateChunks = canGenerateChunks; + + // +1 since we need to load chunks +1 around the load view distance... -+ final long[] toIterate = ParallelSearchRadiusIteration.getSearchIteration(loadViewDistance + 1); ++ final long[] toIterate = ParallelSearchRadiusIteration.getEuclideanIteration(loadViewDistance + 1); + // the iteration order is by increasing manhattan distance - so, we do NOT need to + // sort anything in the queue! + for (final long deltaChunk : toIterate) { @@ -6580,13 +6511,13 @@ index 0000000000000000000000000000000000000000..7129c5e1920008ac54f3a8ac83f55893 +} diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java b/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java new file mode 100644 -index 0000000000000000000000000000000000000000..7eafc5b7cba23d8dec92ecc1050afe3fd8c9e309 +index 0000000000000000000000000000000000000000..7dda31a98de2ddf0ca7a520ae48025fc96754011 --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/chunk_system/queue/ChunkUnloadQueue.java @@ -0,0 +1,144 @@ +package ca.spottedleaf.moonrise.patches.chunk_system.queue; + -+import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; ++import ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2ReferenceHashTable; +import ca.spottedleaf.moonrise.common.util.CoordinateUtils; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; @@ -6602,7 +6533,7 @@ index 0000000000000000000000000000000000000000..7eafc5b7cba23d8dec92ecc1050afe3f + + public final int coordinateShift; + private final AtomicLong orderGenerator = new AtomicLong(); -+ private final ConcurrentLong2ReferenceChainedHashTable unloadSections = new ConcurrentLong2ReferenceChainedHashTable<>(); ++ private final ConcurrentChainedLong2ReferenceHashTable unloadSections = new ConcurrentChainedLong2ReferenceHashTable<>(); + + /* + * Note: write operations do not occur in parallel for any given section. @@ -6618,8 +6549,8 @@ index 0000000000000000000000000000000000000000..7eafc5b7cba23d8dec92ecc1050afe3f + public List retrieveForAllRegions() { + final List ret = new ArrayList<>(); + -+ for (final Iterator> iterator = this.unloadSections.entryIterator(); iterator.hasNext();) { -+ final ConcurrentLong2ReferenceChainedHashTable.TableEntry entry = iterator.next(); ++ for (final Iterator> iterator = this.unloadSections.entryIterator(); iterator.hasNext();) { ++ final ConcurrentChainedLong2ReferenceHashTable.TableEntry entry = iterator.next(); + final long key = entry.getKey(); + final UnloadSection section = entry.getValue(); + final int sectionX = CoordinateUtils.getChunkX(key); @@ -6728,10 +6659,9 @@ index 0000000000000000000000000000000000000000..7eafc5b7cba23d8dec92ecc1050afe3f + } + } +} -\ No newline at end of file diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java new file mode 100644 -index 0000000000000000000000000000000000000000..41b4c5a87b5153f845421a7df8df04c9231253a8 +index 0000000000000000000000000000000000000000..05d374f679919d082064e8f2bb363a419bc9648e --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java @@ -0,0 +1,1568 @@ @@ -6739,8 +6669,8 @@ index 0000000000000000000000000000000000000000..41b4c5a87b5153f845421a7df8df04c9 + +import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; +import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock; -+import ca.spottedleaf.concurrentutil.map.ConcurrentLong2LongChainedHashTable; -+import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; ++import ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2LongHashTable; ++import ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2ReferenceHashTable; +import ca.spottedleaf.concurrentutil.util.Priority; +import ca.spottedleaf.moonrise.common.PlatformHooks; +import ca.spottedleaf.moonrise.common.util.CoordinateUtils; @@ -6812,11 +6742,11 @@ index 0000000000000000000000000000000000000000..41b4c5a87b5153f845421a7df8df04c9 + private static final long NO_TIMEOUT_MARKER = Long.MIN_VALUE; + public final ReentrantAreaLock ticketLockArea; + -+ private final ConcurrentLong2ReferenceChainedHashTable tickets = new ConcurrentLong2ReferenceChainedHashTable<>(); -+ private final ConcurrentLong2ReferenceChainedHashTable sectionToChunkToExpireCount = new ConcurrentLong2ReferenceChainedHashTable<>(); ++ private final ConcurrentChainedLong2ReferenceHashTable tickets = new ConcurrentChainedLong2ReferenceHashTable<>(); ++ private final ConcurrentChainedLong2ReferenceHashTable sectionToChunkToExpireCount = new ConcurrentChainedLong2ReferenceHashTable<>(); + final ChunkUnloadQueue unloadQueue; + -+ private final ConcurrentLong2ReferenceChainedHashTable chunkHolders = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(16384, 0.25f); ++ private final ConcurrentChainedLong2ReferenceHashTable chunkHolders = ConcurrentChainedLong2ReferenceHashTable.createWithCapacity(16384, 0.25f); + private final ServerLevel world; + private final ChunkTaskScheduler taskScheduler; + private long currentTick; @@ -6845,7 +6775,7 @@ index 0000000000000000000000000000000000000000..41b4c5a87b5153f845421a7df8df04c9 + }); + + // mapping of counter id -> (mapping of pos->count) -+ private final ConcurrentLong2ReferenceChainedHashTable ticketCounters = new ConcurrentLong2ReferenceChainedHashTable<>(); ++ private final ConcurrentChainedLong2ReferenceHashTable ticketCounters = new ConcurrentChainedLong2ReferenceHashTable<>(); + + public ChunkHolderManager(final ServerLevel world, final ChunkTaskScheduler taskScheduler) { + this.world = world; @@ -7388,7 +7318,7 @@ index 0000000000000000000000000000000000000000..41b4c5a87b5153f845421a7df8df04c9 + private void addTicketCounter(final TicketType type, final long pos) { + for (final long counterType : ((ChunkSystemTicketType)(Object)type).moonrise$getCounterTypes()) { + this.ticketCounters.computeIfAbsent(counterType, (final long counterId) -> { -+ return new ConcurrentLong2LongChainedHashTable(); ++ return new ConcurrentChainedLong2LongHashTable(); + }).addTo(pos, 1L, 1L); + } + } @@ -7399,7 +7329,7 @@ index 0000000000000000000000000000000000000000..41b4c5a87b5153f845421a7df8df04c9 + } + } + -+ public ConcurrentLong2LongChainedHashTable getTicketCounters(final long counterType) { ++ public ConcurrentChainedLong2LongHashTable getTicketCounters(final long counterType) { + return this.ticketCounters.get(counterType); + } + @@ -8267,9 +8197,9 @@ index 0000000000000000000000000000000000000000..41b4c5a87b5153f845421a7df8df04c9 + final JsonArray allTicketsJson = new JsonArray(); + ret.add("tickets", allTicketsJson); + -+ for (final Iterator> iterator = this.tickets.entryIterator(); ++ for (final Iterator> iterator = this.tickets.entryIterator(); + iterator.hasNext();) { -+ final ConcurrentLong2ReferenceChainedHashTable.TableEntry coordinateTickets = iterator.next(); ++ final ConcurrentChainedLong2ReferenceHashTable.TableEntry coordinateTickets = iterator.next(); + final long coordinate = coordinateTickets.getKey(); + final TicketSet tickets = coordinateTickets.getValue(); + @@ -11568,7 +11498,7 @@ index 0000000000000000000000000000000000000000..6b468c621b74449a6218391f6477cf63 +} diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java new file mode 100644 -index 0000000000000000000000000000000000000000..342d41bd32ea5e0ca40da677d2d5d6b87b77a4a9 +index 0000000000000000000000000000000000000000..3922616c82a9a38fb51038cd4f4c10d7921b649a --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ThreadedTicketLevelPropagator.java @@ -0,0 +1,1247 @@ @@ -11576,7 +11506,7 @@ index 0000000000000000000000000000000000000000..342d41bd32ea5e0ca40da677d2d5d6b8 + +import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; +import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock; -+import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; ++import ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2ReferenceHashTable; +import ca.spottedleaf.concurrentutil.util.ConcurrentUtil; +import ca.spottedleaf.moonrise.common.util.CoordinateUtils; +import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkProgressionTask; @@ -11608,11 +11538,11 @@ index 0000000000000000000000000000000000000000..342d41bd32ea5e0ca40da677d2d5d6b8 + } + + private final UpdateQueue updateQueue; -+ private final ConcurrentLong2ReferenceChainedHashTable

    sections; ++ private final ConcurrentChainedLong2ReferenceHashTable
    sections; + + public ThreadedTicketLevelPropagator() { + this.updateQueue = new UpdateQueue(); -+ this.sections = new ConcurrentLong2ReferenceChainedHashTable<>(); ++ this.sections = new ConcurrentChainedLong2ReferenceHashTable<>(); + } + + // must hold ticket lock for: @@ -14877,16 +14807,17 @@ index 0000000000000000000000000000000000000000..ce3bb903c9ccb7efa0f004cf79b291dc +} diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/util/ParallelSearchRadiusIteration.java b/ca/spottedleaf/moonrise/patches/chunk_system/util/ParallelSearchRadiusIteration.java new file mode 100644 -index 0000000000000000000000000000000000000000..93fd23027c00cef76562098306737272fda1350a +index 0000000000000000000000000000000000000000..28d796a3fddebf0bb25ba759969aadecc5a6a2de --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/chunk_system/util/ParallelSearchRadiusIteration.java -@@ -0,0 +1,321 @@ +@@ -0,0 +1,360 @@ +package ca.spottedleaf.moonrise.patches.chunk_system.util; + +import ca.spottedleaf.moonrise.common.util.CoordinateUtils; +import ca.spottedleaf.moonrise.common.util.MoonriseConstants; +import it.unimi.dsi.fastutil.HashCommon; +import it.unimi.dsi.fastutil.longs.LongArrayList; ++import it.unimi.dsi.fastutil.longs.LongComparator; +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; @@ -14904,11 +14835,49 @@ index 0000000000000000000000000000000000000000..93fd23027c00cef76562098306737272 + SEARCH_RADIUS_ITERATION_LIST[i] = generateBFSOrder(i); + } + } ++ private static final long[][] EUCLIDEAN_RADIUS_ITERATION_LIST = new long[SEARCH_RADIUS_ITERATION_LIST.length][]; ++ static { ++ // we could iterate all the coordinates, but we already have them sort of sorted in SEARCH_RADIUS_ITERATION_LIST ++ final LongComparator comparator = (final long l1, final long l2) -> { ++ final int c1x = CoordinateUtils.getChunkX(l1); ++ final int c1z = CoordinateUtils.getChunkZ(l1); ++ ++ final int c2x = CoordinateUtils.getChunkX(l2); ++ final int c2z = CoordinateUtils.getChunkZ(l2); ++ ++ final int centerX = 0; ++ final int centerZ = 0; ++ ++ // note: VD < 2^15, so we shouldn't worry about overflow ++ final int diff1X = c1x - centerX; ++ final int diff1Z = c1z - centerZ; ++ ++ final int diff2X = c2x - centerX; ++ final int diff2Z = c2z - centerZ; ++ ++ return Integer.compare( ++ (diff1X * diff1X) + (diff1Z * diff1Z), ++ (diff2X * diff2X) + (diff2Z * diff2Z) ++ ); ++ }; ++ ++ for (int i = 0; i < EUCLIDEAN_RADIUS_ITERATION_LIST.length; ++i) { ++ final LongArrayList tmp = LongArrayList.wrap(SEARCH_RADIUS_ITERATION_LIST[i].clone()); ++ ++ tmp.sort(comparator); ++ ++ EUCLIDEAN_RADIUS_ITERATION_LIST[i] = tmp.elements(); ++ } ++ } + + public static long[] getSearchIteration(final int radius) { + return SEARCH_RADIUS_ITERATION_LIST[radius]; + } + ++ public static long[] getEuclideanIteration(final int radius) { ++ return SEARCH_RADIUS_ITERATION_LIST[radius]; ++ } ++ + private static class CustomLongArray extends LongArrayList { + + public CustomLongArray() { @@ -18147,6 +18116,810 @@ index 0000000000000000000000000000000000000000..540c14a6d2c216cd3ef2a9c4056e1571 + public BlockState moonrise$getBlock(final int x, final int y, final int z); + +} +diff --git a/ca/spottedleaf/moonrise/patches/poi_lookup/PoiAccess.java b/ca/spottedleaf/moonrise/patches/poi_lookup/PoiAccess.java +new file mode 100644 +index 0000000000000000000000000000000000000000..85d80b3952a76b0ca7f67401a659cf35dd55b082 +--- /dev/null ++++ b/ca/spottedleaf/moonrise/patches/poi_lookup/PoiAccess.java +@@ -0,0 +1,798 @@ ++package ca.spottedleaf.moonrise.patches.poi_lookup; ++ ++import ca.spottedleaf.moonrise.common.util.CoordinateUtils; ++import ca.spottedleaf.moonrise.common.util.WorldUtil; ++import com.mojang.datafixers.util.Pair; ++import it.unimi.dsi.fastutil.doubles.Double2ObjectMap; ++import it.unimi.dsi.fastutil.doubles.Double2ObjectRBTreeMap; ++import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; ++import it.unimi.dsi.fastutil.longs.LongOpenHashSet; ++import java.util.function.BiPredicate; ++import net.minecraft.core.BlockPos; ++import net.minecraft.core.Holder; ++import net.minecraft.util.Mth; ++import net.minecraft.world.entity.ai.village.poi.PoiManager; ++import net.minecraft.world.entity.ai.village.poi.PoiRecord; ++import net.minecraft.world.entity.ai.village.poi.PoiSection; ++import net.minecraft.world.entity.ai.village.poi.PoiType; ++import java.util.ArrayList; ++import java.util.HashSet; ++import java.util.Iterator; ++import java.util.List; ++import java.util.Map; ++import java.util.Optional; ++import java.util.Set; ++import java.util.function.Predicate; ++ ++/** ++ * Provides optimised access to POI data. All returned values will be identical to vanilla. ++ */ ++public final class PoiAccess { ++ ++ public static final boolean LOAD_FOR_SEARCHING = true; ++ ++ public static int compareDistances(final double d1, final double d2) { ++ // we assume that the values are reasonably finite ++ ++ // we want: ++ // -1 if d1 - d2 < 0 (d1 < d2) ++ // 0 if d1 == d2 ++ // 1 if d1 - d2 > 0 (d1 > d2) ++ return (int)Math.signum(d1 - d2); ++ } ++ ++ private static long dist(final BlockPos p1, final BlockPos p2) { ++ final long dx = (long)p1.getX() - (long)p2.getX(); ++ final long dy = (long)p1.getY() - (long)p2.getY(); ++ final long dz = (long)p1.getZ() - (long)p2.getZ(); ++ ++ return dx*dx + dy*dy + dz*dz; ++ } ++ ++ public static int compareDistances(final BlockPos center, final BlockPos p1, final BlockPos p2) { ++ final long d1 = dist(p1, center); ++ final long d2 = dist(p2, center); ++ ++ // note: d1 >= 0 and d2 >= 0 ++ return Long.compareUnsigned(d1, d2); ++ } ++ ++ protected static double clamp(final double val, final double min, final double max) { ++ return (val < min ? min : (val > max ? max : val)); ++ } ++ ++ protected static double getSmallestDistanceSquared(final double boxMinX, final double boxMinY, final double boxMinZ, ++ final double boxMaxX, final double boxMaxY, final double boxMaxZ, ++ ++ final double circleX, final double circleY, final double circleZ) { ++ // is the circle center inside the box? ++ if (circleX >= boxMinX && circleX <= boxMaxX && circleY >= boxMinY && circleY <= boxMaxY && circleZ >= boxMinZ && circleZ <= boxMaxZ) { ++ return 0.0; ++ } ++ ++ final double boxWidthX = (boxMaxX - boxMinX) / 2.0; ++ final double boxWidthY = (boxMaxY - boxMinY) / 2.0; ++ final double boxWidthZ = (boxMaxZ - boxMinZ) / 2.0; ++ ++ final double boxCenterX = (boxMinX + boxMaxX) / 2.0; ++ final double boxCenterY = (boxMinY + boxMaxY) / 2.0; ++ final double boxCenterZ = (boxMinZ + boxMaxZ) / 2.0; ++ ++ double centerDiffX = circleX - boxCenterX; ++ double centerDiffY = circleY - boxCenterY; ++ double centerDiffZ = circleZ - boxCenterZ; ++ ++ centerDiffX = circleX - (clamp(centerDiffX, -boxWidthX, boxWidthX) + boxCenterX); ++ centerDiffY = circleY - (clamp(centerDiffY, -boxWidthY, boxWidthY) + boxCenterY); ++ centerDiffZ = circleZ - (clamp(centerDiffZ, -boxWidthZ, boxWidthZ) + boxCenterZ); ++ ++ return (centerDiffX * centerDiffX) + (centerDiffY * centerDiffY) + (centerDiffZ * centerDiffZ); ++ } ++ ++ public static void findClosestPoiDataRecords(final PoiManager poiStorage, ++ final Predicate> villagePlaceType, ++ // position predicate must not modify chunk POI ++ final BiPredicate, BlockPos> predicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistanceSquared, ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final List ret) { ++ final Predicate occupancyFilter = occupancy.getTest(); ++ ++ final List closestRecords = new ArrayList<>(); ++ double closestDistanceSquared = maxDistanceSquared; ++ ++ final int lowerX = Mth.floor(sourcePosition.getX() - range) >> 4; ++ final int lowerY = WorldUtil.getMinSection(poiStorage.levelHeightAccessor); ++ final int lowerZ = Mth.floor(sourcePosition.getZ() - range) >> 4; ++ final int upperX = Mth.floor(sourcePosition.getX() + range) >> 4; ++ final int upperY = WorldUtil.getMaxSection(poiStorage.levelHeightAccessor); ++ final int upperZ = Mth.floor(sourcePosition.getZ() + range) >> 4; ++ ++ final int centerX = sourcePosition.getX() >> 4; ++ final int centerY = Mth.clamp(sourcePosition.getY() >> 4, lowerY, upperY); ++ final int centerZ = sourcePosition.getZ() >> 4; ++ final long centerKey = CoordinateUtils.getChunkSectionKey(centerX, centerY, centerZ); ++ ++ final LongArrayFIFOQueue queue = new LongArrayFIFOQueue(); ++ final LongOpenHashSet seen = new LongOpenHashSet(); ++ seen.add(centerKey); ++ queue.enqueue(centerKey); ++ ++ while (!queue.isEmpty()) { ++ final long key = queue.dequeueLong(); ++ final int sectionX = CoordinateUtils.getChunkSectionX(key); ++ final int sectionY = CoordinateUtils.getChunkSectionY(key); ++ final int sectionZ = CoordinateUtils.getChunkSectionZ(key); ++ ++ if (sectionX < lowerX || sectionX > upperX || sectionY < lowerY || sectionY > upperY || sectionZ < lowerZ || sectionZ > upperZ) { ++ // out of bound chunk ++ continue; ++ } ++ ++ final double sectionDistanceSquared = getSmallestDistanceSquared( ++ (double)(sectionX << 4), ++ (double)(sectionY << 4), ++ (double)(sectionZ << 4), ++ (double)((sectionX << 4) | 15), ++ (double)((sectionY << 4) | 15), ++ (double)((sectionZ << 4) | 15), ++ (double)sourcePosition.getX(), (double)sourcePosition.getY(), (double)sourcePosition.getZ() ++ ); ++ if (sectionDistanceSquared > closestDistanceSquared) { ++ continue; ++ } ++ ++ // queue all neighbours ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ for (int dy = -1; dy <= 1; ++dy) { ++ // -1 and 1 have the 1st bit set. so just add up the first bits, and it will tell us how many ++ // values are set. we only care about cardinal neighbours, so, we only care if one value is set ++ if ((dx & 1) + (dy & 1) + (dz & 1) != 1) { ++ continue; ++ } ++ ++ final int neighbourX = sectionX + dx; ++ final int neighbourY = sectionY + dy; ++ final int neighbourZ = sectionZ + dz; ++ ++ final long neighbourKey = CoordinateUtils.getChunkSectionKey(neighbourX, neighbourY, neighbourZ); ++ if (seen.add(neighbourKey)) { ++ queue.enqueue(neighbourKey); ++ } ++ } ++ } ++ } ++ ++ final Optional poiSectionOptional = load ? poiStorage.getOrLoad(key) : poiStorage.get(key); ++ ++ if (poiSectionOptional == null || !poiSectionOptional.isPresent()) { ++ continue; ++ } ++ ++ final PoiSection poiSection = poiSectionOptional.get(); ++ ++ final Map, Set> sectionData = poiSection.byType; ++ if (sectionData.isEmpty()) { ++ continue; ++ } ++ ++ // now we search the section data ++ for (final Map.Entry, Set> entry : sectionData.entrySet()) { ++ if (!villagePlaceType.test(entry.getKey())) { ++ // filter out by poi type ++ continue; ++ } ++ ++ // now we can look at the poi data ++ for (final PoiRecord poiData : entry.getValue()) { ++ if (!occupancyFilter.test(poiData)) { ++ // filter by occupancy ++ continue; ++ } ++ ++ final BlockPos poiPosition = poiData.getPos(); ++ ++ if (Math.abs(poiPosition.getX() - sourcePosition.getX()) > range ++ || Math.abs(poiPosition.getZ() - sourcePosition.getZ()) > range) { ++ // out of range for square radius ++ continue; ++ } ++ ++ // it's important that it's poiPosition.distSqr(source) : the value actually is different IF the values are swapped! ++ final double dataRange = poiPosition.distSqr(sourcePosition); ++ ++ if (dataRange > closestDistanceSquared) { ++ // out of range for distance check ++ continue; ++ } ++ ++ if (predicate != null && !predicate.test(poiData.getPoiType(), poiPosition)) { ++ // filter by position ++ continue; ++ } ++ ++ if (dataRange < closestDistanceSquared) { ++ closestRecords.clear(); ++ closestDistanceSquared = dataRange; ++ } ++ closestRecords.add(poiData); ++ } ++ } ++ } ++ ++ // uh oh! we might have multiple records that match the distance sorting! ++ // we need to re-order our results by the way vanilla would have iterated over them. ++ closestRecords.sort((record1, record2) -> { ++ // vanilla iterates the same way we do for data inside sections, so we know the ordering inside a section ++ // is fine and should be preserved (this sort is stable so we're good there) ++ // but they iterate sections by x then by z (like the following) ++ // for (int x = -dx; x <= dx; ++x) ++ // for (int z = -dz; z <= dz; ++z) ++ // .... ++ // so we need to reorder such that records with lower chunk z, then lower chunk x come first ++ final BlockPos pos1 = record1.getPos(); ++ final BlockPos pos2 = record2.getPos(); ++ ++ final int cx1 = pos1.getX() >> 4; ++ final int cz1 = pos1.getZ() >> 4; ++ ++ final int cx2 = pos2.getX() >> 4; ++ final int cz2 = pos2.getZ() >> 4; ++ ++ if (cz2 != cz1) { ++ // want smaller z ++ return Integer.compare(cz1, cz2); ++ } ++ ++ if (cx2 != cx1) { ++ // want smaller x ++ return Integer.compare(cx1, cx2); ++ } ++ ++ // same chunk ++ // once vanilla has the chunk, it will iterate from all of the chunk sections starting from smaller y ++ // so now we just compare section y, wanting smaller y ++ ++ return Integer.compare(pos1.getY() >> 4, pos2.getY() >> 4); ++ }); ++ ++ // now we match perfectly what vanilla would have outputted, without having to search the whole radius (hopefully). ++ ret.addAll(closestRecords); ++ } ++ ++ // finds the closest poi entry pos. ++ public static BlockPos findNearestPoiPosition(final PoiManager poiStorage, ++ final Predicate> villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistanceSquared, ++ final PoiManager.Occupancy occupancy, ++ final boolean load) { ++ final PoiRecord ret = findNearestPoiRecord( ++ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistanceSquared, occupancy, load ++ ); ++ return ret == null ? null : ret.getPos(); ++ } ++ ++ // finds the closest `max` poi entry positions. ++ public static void findNearestPoiPositions(final PoiManager poiStorage, ++ final Predicate> villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistanceSquared, ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final int max, ++ final List, BlockPos>> ret) { ++ final Set positions = new HashSet<>(); ++ // pos predicate is last thing that runs before adding to ret. ++ final Predicate newPredicate = (final BlockPos pos) -> { ++ if (positionPredicate != null && !positionPredicate.test(pos)) { ++ return false; ++ } ++ return positions.add(pos.immutable()); ++ }; ++ ++ final List toConvert = new ArrayList<>(); ++ findNearestPoiRecords( ++ poiStorage, villagePlaceType, newPredicate, sourcePosition, range, maxDistanceSquared, occupancy, load, max, toConvert ++ ); ++ ++ for (final PoiRecord record : toConvert) { ++ ret.add(Pair.of(record.getPoiType(), record.getPos())); ++ } ++ } ++ ++ // finds the closest poi entry. ++ public static PoiRecord findNearestPoiRecord(final PoiManager poiStorage, ++ final Predicate> villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistanceSquared, ++ final PoiManager.Occupancy occupancy, ++ final boolean load) { ++ final List ret = new ArrayList<>(); ++ findNearestPoiRecords( ++ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistanceSquared, occupancy, load, ++ 1, ret ++ ); ++ return ret.isEmpty() ? null : ret.get(0); ++ } ++ ++ // finds the closest `max` poi entries. ++ public static void findNearestPoiRecords(final PoiManager poiStorage, ++ final Predicate> villagePlaceType, ++ // position predicate must not modify chunk POI ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistanceSquared, ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final int max, ++ final List ret) { ++ final Predicate occupancyFilter = occupancy.getTest(); ++ ++ final Double2ObjectRBTreeMap> closestRecords = new Double2ObjectRBTreeMap<>(); ++ int totalRecords = 0; ++ double furthestDistanceSquared = maxDistanceSquared; ++ ++ final int lowerX = Mth.floor(sourcePosition.getX() - range) >> 4; ++ final int lowerY = WorldUtil.getMinSection(poiStorage.levelHeightAccessor); ++ final int lowerZ = Mth.floor(sourcePosition.getZ() - range) >> 4; ++ final int upperX = Mth.floor(sourcePosition.getX() + range) >> 4; ++ final int upperY = WorldUtil.getMaxSection(poiStorage.levelHeightAccessor); ++ final int upperZ = Mth.floor(sourcePosition.getZ() + range) >> 4; ++ ++ final int centerX = sourcePosition.getX() >> 4; ++ final int centerY = Mth.clamp(sourcePosition.getY() >> 4, lowerY, upperY); ++ final int centerZ = sourcePosition.getZ() >> 4; ++ final long centerKey = CoordinateUtils.getChunkSectionKey(centerX, centerY, centerZ); ++ ++ final LongArrayFIFOQueue queue = new LongArrayFIFOQueue(); ++ final LongOpenHashSet seen = new LongOpenHashSet(); ++ seen.add(centerKey); ++ queue.enqueue(centerKey); ++ ++ while (!queue.isEmpty()) { ++ final long key = queue.dequeueLong(); ++ final int sectionX = CoordinateUtils.getChunkSectionX(key); ++ final int sectionY = CoordinateUtils.getChunkSectionY(key); ++ final int sectionZ = CoordinateUtils.getChunkSectionZ(key); ++ ++ if (sectionX < lowerX || sectionX > upperX || sectionY < lowerY || sectionY > upperY || sectionZ < lowerZ || sectionZ > upperZ) { ++ // out of bound chunk ++ continue; ++ } ++ ++ final double sectionDistanceSquared = getSmallestDistanceSquared( ++ (double)(sectionX << 4), ++ (double)(sectionY << 4), ++ (double)(sectionZ << 4), ++ (double)((sectionX << 4) | 15), ++ (double)((sectionY << 4) | 15), ++ (double)((sectionZ << 4) | 15), ++ (double)sourcePosition.getX(), (double)sourcePosition.getY(), (double)sourcePosition.getZ() ++ ); ++ ++ if (sectionDistanceSquared > (totalRecords >= max ? furthestDistanceSquared : maxDistanceSquared)) { ++ continue; ++ } ++ ++ // queue all neighbours ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ for (int dy = -1; dy <= 1; ++dy) { ++ // -1 and 1 have the 1st bit set. so just add up the first bits, and it will tell us how many ++ // values are set. we only care about cardinal neighbours, so, we only care if one value is set ++ if ((dx & 1) + (dy & 1) + (dz & 1) != 1) { ++ continue; ++ } ++ ++ final int neighbourX = sectionX + dx; ++ final int neighbourY = sectionY + dy; ++ final int neighbourZ = sectionZ + dz; ++ ++ final long neighbourKey = CoordinateUtils.getChunkSectionKey(neighbourX, neighbourY, neighbourZ); ++ if (seen.add(neighbourKey)) { ++ queue.enqueue(neighbourKey); ++ } ++ } ++ } ++ } ++ ++ final Optional poiSectionOptional = load ? poiStorage.getOrLoad(key) : poiStorage.get(key); ++ ++ if (poiSectionOptional == null || !poiSectionOptional.isPresent()) { ++ continue; ++ } ++ ++ final PoiSection poiSection = poiSectionOptional.get(); ++ ++ final Map, Set> sectionData = poiSection.byType; ++ if (sectionData.isEmpty()) { ++ continue; ++ } ++ ++ // now we search the section data ++ for (final Map.Entry, Set> entry : sectionData.entrySet()) { ++ if (!villagePlaceType.test(entry.getKey())) { ++ // filter out by poi type ++ continue; ++ } ++ ++ // now we can look at the poi data ++ for (final PoiRecord poiData : entry.getValue()) { ++ if (!occupancyFilter.test(poiData)) { ++ // filter by occupancy ++ continue; ++ } ++ ++ final BlockPos poiPosition = poiData.getPos(); ++ ++ if (Math.abs(poiPosition.getX() - sourcePosition.getX()) > range ++ || Math.abs(poiPosition.getZ() - sourcePosition.getZ()) > range) { ++ // out of range for square radius ++ continue; ++ } ++ ++ // it's important that it's poiPosition.distSqr(source) : the value actually is different IF the values are swapped! ++ final double dataRange = poiPosition.distSqr(sourcePosition); ++ ++ if (dataRange > maxDistanceSquared) { ++ // out of range for distance check ++ continue; ++ } ++ ++ if (dataRange > furthestDistanceSquared && totalRecords >= max) { ++ // out of range for distance check ++ continue; ++ } ++ ++ if (positionPredicate != null && !positionPredicate.test(poiPosition)) { ++ // filter by position ++ continue; ++ } ++ ++ if (dataRange > furthestDistanceSquared) { ++ // we know totalRecords < max, so this entry is now our furthest ++ furthestDistanceSquared = dataRange; ++ } ++ ++ closestRecords.computeIfAbsent(dataRange, (final double unused) -> { ++ return new ArrayList<>(); ++ }).add(poiData); ++ ++ if (++totalRecords >= max) { ++ if (closestRecords.size() >= 2) { ++ int entriesInClosest = 0; ++ final Iterator>> iterator = closestRecords.double2ObjectEntrySet().iterator(); ++ double nextFurthestDistanceSquared = 0.0; ++ ++ for (int i = 0, len = closestRecords.size() - 1; i < len; ++i) { ++ final Double2ObjectMap.Entry> recordEntry = iterator.next(); ++ entriesInClosest += recordEntry.getValue().size(); ++ nextFurthestDistanceSquared = recordEntry.getDoubleKey(); ++ } ++ ++ if (entriesInClosest >= max) { ++ // the last set of entries at range wont even be considered for sure... nuke em ++ final Double2ObjectMap.Entry> recordEntry = iterator.next(); ++ totalRecords -= recordEntry.getValue().size(); ++ iterator.remove(); ++ ++ furthestDistanceSquared = nextFurthestDistanceSquared; ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ final List closestRecordsUnsorted = new ArrayList<>(); ++ ++ // we're done here, so now just flatten the map and sort it. ++ ++ for (final List records : closestRecords.values()) { ++ closestRecordsUnsorted.addAll(records); ++ } ++ ++ // uh oh! we might have multiple records that match the distance sorting! ++ // we need to re-order our results by the way vanilla would have iterated over them. ++ closestRecordsUnsorted.sort((record1, record2) -> { ++ // vanilla iterates the same way we do for data inside sections, so we know the ordering inside a section ++ // is fine and should be preserved (this sort is stable so we're good there) ++ // but they iterate sections by x then by z (like the following) ++ // for (int x = -dx; x <= dx; ++x) ++ // for (int z = -dz; z <= dz; ++z) ++ // .... ++ // so we need to reorder such that records with lower chunk z, then lower chunk x come first ++ final BlockPos pos1 = record1.getPos(); ++ final BlockPos pos2 = record2.getPos(); ++ ++ final int cx1 = pos1.getX() >> 4; ++ final int cz1 = pos1.getZ() >> 4; ++ ++ final int cx2 = pos2.getX() >> 4; ++ final int cz2 = pos2.getZ() >> 4; ++ ++ if (cz2 != cz1) { ++ // want smaller z ++ return Integer.compare(cz1, cz2); ++ } ++ ++ if (cx2 != cx1) { ++ // want smaller x ++ return Integer.compare(cx1, cx2); ++ } ++ ++ // same chunk ++ // once vanilla has the chunk, it will iterate from all of the chunk sections starting from smaller y ++ // so now we just compare section y, wanting smaller section y ++ ++ return Integer.compare(pos1.getY() >> 4, pos2.getY() >> 4); ++ }); ++ ++ // trim out any entries exceeding our maximum ++ for (int i = closestRecordsUnsorted.size() - 1; i >= max; --i) { ++ closestRecordsUnsorted.remove(i); ++ } ++ ++ // now we match perfectly what vanilla would have outputted, without having to search the whole radius (hopefully). ++ ret.addAll(closestRecordsUnsorted); ++ } ++ ++ public static BlockPos findAnyPoiPosition(final PoiManager poiStorage, ++ final Predicate> villagePlaceType, ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final PoiManager.Occupancy occupancy, ++ final boolean load) { ++ final PoiRecord ret = findAnyPoiRecord( ++ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, (double)((long)range * (long)range), occupancy, load ++ ); ++ ++ return ret == null ? null : ret.getPos(); ++ } ++ ++ public static void findAnyPoiPositions(final PoiManager poiStorage, ++ final Predicate> villagePlaceType, ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final int max, ++ final List, BlockPos>> ret) { ++ final Set positions = new HashSet<>(); ++ // pos predicate is last thing that runs before adding to ret. ++ final Predicate newPredicate = (final BlockPos pos) -> { ++ if (positionPredicate != null && !positionPredicate.test(pos)) { ++ return false; ++ } ++ return positions.add(pos.immutable()); ++ }; ++ ++ final List toConvert = new ArrayList<>(); ++ findAnyPoiRecords( ++ poiStorage, villagePlaceType, newPredicate, sourcePosition, range, (double)((long)range * (long)range), occupancy, load, max, toConvert ++ ); ++ ++ for (final PoiRecord record : toConvert) { ++ ret.add(Pair.of(record.getPoiType(), record.getPos())); ++ } ++ } ++ ++ public static PoiRecord findAnyPoiRecord(final PoiManager poiStorage, ++ final Predicate> villagePlaceType, ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistanceSqr, ++ final PoiManager.Occupancy occupancy, ++ final boolean load) { ++ final List ret = new ArrayList<>(); ++ findAnyPoiRecords(poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistanceSqr, occupancy, load, 1, ret); ++ return ret.isEmpty() ? null : ret.get(0); ++ } ++ ++ public static void findAnyPoiRecords(final PoiManager poiStorage, ++ final Predicate> villagePlaceType, ++ final Predicate positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistanceSqr, ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final int max, ++ final List ret) { ++ // the biggest issue with the original mojang implementation is that they chain so many streams together ++ // the amount of streams chained just rolls performance, even if nothing is iterated over ++ final Predicate occupancyFilter = occupancy.getTest(); ++ ++ int added = 0; ++ ++ // First up, we need to iterate the chunks ++ // all the values here are in chunk sections ++ final int lowerX = Mth.floor(sourcePosition.getX() - range) >> 4; ++ final int lowerY = Math.max(WorldUtil.getMinSection(poiStorage.levelHeightAccessor), Mth.floor(sourcePosition.getY() - range) >> 4); ++ final int lowerZ = Mth.floor(sourcePosition.getZ() - range) >> 4; ++ final int upperX = Mth.floor(sourcePosition.getX() + range) >> 4; ++ final int upperY = Math.min(WorldUtil.getMaxSection(poiStorage.levelHeightAccessor), Mth.floor(sourcePosition.getY() + range) >> 4); ++ final int upperZ = Mth.floor(sourcePosition.getZ() + range) >> 4; ++ ++ // Vanilla iterates by x until max is reached then increases z ++ // vanilla also searches by increasing Y section value ++ for (int currZ = lowerZ; currZ <= upperZ; ++currZ) { ++ for (int currX = lowerX; currX <= upperX; ++currX) { ++ for (int currY = lowerY; currY <= upperY; ++currY) { // vanilla searches the entire chunk because they're actually stupid. just search the sections we need ++ final Optional poiSectionOptional = load ? poiStorage.getOrLoad(CoordinateUtils.getChunkSectionKey(currX, currY, currZ)) : ++ poiStorage.get(CoordinateUtils.getChunkSectionKey(currX, currY, currZ)); ++ final PoiSection poiSection = poiSectionOptional == null ? null : poiSectionOptional.orElse(null); ++ if (poiSection == null) { ++ continue; ++ } ++ ++ final Map, Set> sectionData = poiSection.byType; ++ if (sectionData.isEmpty()) { ++ continue; ++ } ++ ++ // now we search the section data ++ for (final Map.Entry, Set> entry : sectionData.entrySet()) { ++ if (!villagePlaceType.test(entry.getKey())) { ++ // filter out by poi type ++ continue; ++ } ++ ++ // now we can look at the poi data ++ for (final PoiRecord poiData : entry.getValue()) { ++ if (!occupancyFilter.test(poiData)) { ++ // filter by occupancy ++ continue; ++ } ++ ++ final BlockPos poiPosition = poiData.getPos(); ++ ++ if (Math.abs(poiPosition.getX() - sourcePosition.getX()) > range ++ || Math.abs(poiPosition.getZ() - sourcePosition.getZ()) > range) { ++ // out of range for square radius ++ continue; ++ } ++ ++ if (poiPosition.distSqr(sourcePosition) > maxDistanceSqr) { ++ // out of range for distance check ++ continue; ++ } ++ ++ if (positionPredicate != null && !positionPredicate.test(poiPosition)) { ++ // filter by position ++ continue; ++ } ++ ++ // found one! ++ ret.add(poiData); ++ if (++added >= max) { ++ return; ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ public static PoiRecord findAnyPoiRecord(final PoiManager poiStorage, ++ final Predicate> villagePlaceType, ++ final BiPredicate, BlockPos> positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistanceSqr, ++ final PoiManager.Occupancy occupancy, ++ final boolean load) { ++ final List ret = new ArrayList<>(); ++ findAnyPoiRecords(poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistanceSqr, occupancy, load, 1, ret); ++ return ret.isEmpty() ? null : ret.get(0); ++ } ++ ++ public static void findAnyPoiRecords(final PoiManager poiStorage, ++ final Predicate> villagePlaceType, ++ final BiPredicate, BlockPos> positionPredicate, ++ final BlockPos sourcePosition, ++ final int range, // distance on x y z axis ++ final double maxDistanceSqr, ++ final PoiManager.Occupancy occupancy, ++ final boolean load, ++ final int max, ++ final List ret) { ++ // the biggest issue with the original mojang implementation is that they chain so many streams together ++ // the amount of streams chained just rolls performance, even if nothing is iterated over ++ final Predicate occupancyFilter = occupancy.getTest(); ++ ++ int added = 0; ++ ++ // First up, we need to iterate the chunks ++ // all the values here are in chunk sections ++ final int lowerX = Mth.floor(sourcePosition.getX() - range) >> 4; ++ final int lowerY = Math.max(WorldUtil.getMinSection(poiStorage.levelHeightAccessor), Mth.floor(sourcePosition.getY() - range) >> 4); ++ final int lowerZ = Mth.floor(sourcePosition.getZ() - range) >> 4; ++ final int upperX = Mth.floor(sourcePosition.getX() + range) >> 4; ++ final int upperY = Math.min(WorldUtil.getMaxSection(poiStorage.levelHeightAccessor), Mth.floor(sourcePosition.getY() + range) >> 4); ++ final int upperZ = Mth.floor(sourcePosition.getZ() + range) >> 4; ++ ++ // Vanilla iterates by x until max is reached then increases z ++ // vanilla also searches by increasing Y section value ++ for (int currZ = lowerZ; currZ <= upperZ; ++currZ) { ++ for (int currX = lowerX; currX <= upperX; ++currX) { ++ for (int currY = lowerY; currY <= upperY; ++currY) { // vanilla searches the entire chunk because they're actually stupid. just search the sections we need ++ final Optional poiSectionOptional = load ? poiStorage.getOrLoad(CoordinateUtils.getChunkSectionKey(currX, currY, currZ)) : ++ poiStorage.get(CoordinateUtils.getChunkSectionKey(currX, currY, currZ)); ++ final PoiSection poiSection = poiSectionOptional == null ? null : poiSectionOptional.orElse(null); ++ if (poiSection == null) { ++ continue; ++ } ++ ++ final Map, Set> sectionData = poiSection.byType; ++ if (sectionData.isEmpty()) { ++ continue; ++ } ++ ++ // now we search the section data ++ for (final Map.Entry, Set> entry : sectionData.entrySet()) { ++ if (!villagePlaceType.test(entry.getKey())) { ++ // filter out by poi type ++ continue; ++ } ++ ++ // now we can look at the poi data ++ for (final PoiRecord poiData : entry.getValue()) { ++ if (!occupancyFilter.test(poiData)) { ++ // filter by occupancy ++ continue; ++ } ++ ++ final BlockPos poiPosition = poiData.getPos(); ++ ++ if (Math.abs(poiPosition.getX() - sourcePosition.getX()) > range ++ || Math.abs(poiPosition.getZ() - sourcePosition.getZ()) > range) { ++ // out of range for square radius ++ continue; ++ } ++ ++ if (poiPosition.distSqr(sourcePosition) > maxDistanceSqr) { ++ // out of range for distance check ++ continue; ++ } ++ ++ if (positionPredicate != null && !positionPredicate.test(poiData.getPoiType(), poiPosition)) { ++ // filter by position ++ continue; ++ } ++ ++ // found one! ++ ret.add(poiData); ++ if (++added >= max) { ++ return; ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ private PoiAccess() { ++ throw new RuntimeException(); ++ } ++} diff --git a/ca/spottedleaf/moonrise/patches/starlight/blockstate/StarlightAbstractBlockState.java b/ca/spottedleaf/moonrise/patches/starlight/blockstate/StarlightAbstractBlockState.java new file mode 100644 index 0000000000000000000000000000000000000000..8e6d79b7c10ef25f5478b72c53c555423d615a2f @@ -18186,7 +18959,7 @@ index 0000000000000000000000000000000000000000..ed80017c8f257b981d626a37ffc5480d +} diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/BlockStarLightEngine.java b/ca/spottedleaf/moonrise/patches/starlight/light/BlockStarLightEngine.java new file mode 100644 -index 0000000000000000000000000000000000000000..b623fb6974f5c4875a1ddc09053e72e6e67dda17 +index 0000000000000000000000000000000000000000..7e343de2fe4348290d7275494ee14142e64b9243 --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/starlight/light/BlockStarLightEngine.java @@ -0,0 +1,280 @@ @@ -18327,7 +19100,7 @@ index 0000000000000000000000000000000000000000..b623fb6974f5c4875a1ddc09053e72e6 + return level; + } + -+ final int opacity = Math.max(1, centerState.getLightBlock()); ++ final int opacity = Math.max(1, centerState.getLightDampening()); + if (opacity >= 15) { + return level; + } @@ -18390,8 +19163,8 @@ index 0000000000000000000000000000000000000000..b623fb6974f5c4875a1ddc09053e72e6 + protected List getSources(final LightChunkGetter lightAccess, final ChunkAccess chunk) { + final List sources = new ArrayList<>(); + -+ final int offX = chunk.getPos().x << 4; -+ final int offZ = chunk.getPos().z << 4; ++ final int offX = chunk.getPos().x() << 4; ++ final int offZ = chunk.getPos().z() << 4; + + final PlatformHooks platformHooks = PlatformHooks.get(); + @@ -18919,7 +19692,7 @@ index 0000000000000000000000000000000000000000..5c7b3804cdbcb0a873a0d195325c2658 +} diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/SkyStarLightEngine.java b/ca/spottedleaf/moonrise/patches/starlight/light/SkyStarLightEngine.java new file mode 100644 -index 0000000000000000000000000000000000000000..cdb679fb6e3d342efc776329124209d1f433cc8f +index 0000000000000000000000000000000000000000..d3d1831ff4fabd87d4b18da0b28a0d75d82dfbc7 --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/starlight/light/SkyStarLightEngine.java @@ -0,0 +1,689 @@ @@ -19172,8 +19945,8 @@ index 0000000000000000000000000000000000000000..cdb679fb6e3d342efc776329124209d1 + final int toSection) { + Arrays.fill(this.nullPropagationCheckCache, false); + this.rewriteNibbleCacheForSkylight(chunk); -+ final int chunkX = chunk.getPos().x; -+ final int chunkZ = chunk.getPos().z; ++ final int chunkX = chunk.getPos().x(); ++ final int chunkZ = chunk.getPos().z(); + for (int y = toSection; y >= fromSection; --y) { + this.checkNullSection(chunkX, y, chunkZ, true); + } @@ -19185,8 +19958,8 @@ index 0000000000000000000000000000000000000000..cdb679fb6e3d342efc776329124209d1 + protected void checkChunkEdges(final LightChunkGetter lightAccess, final ChunkAccess chunk, final ShortCollection sections) { + Arrays.fill(this.nullPropagationCheckCache, false); + this.rewriteNibbleCacheForSkylight(chunk); -+ final int chunkX = chunk.getPos().x; -+ final int chunkZ = chunk.getPos().z; ++ final int chunkX = chunk.getPos().x(); ++ final int chunkZ = chunk.getPos().z(); + for (final ShortIterator iterator = sections.iterator(); iterator.hasNext();) { + final int y = (int)iterator.nextShort(); + this.checkNullSection(chunkX, y, chunkZ, true); @@ -19236,7 +20009,7 @@ index 0000000000000000000000000000000000000000..cdb679fb6e3d342efc776329124209d1 + final BlockState centerState = this.getBlockState(worldX, worldY, worldZ); + + final BlockState conditionallyOpaqueState; -+ final int opacity = Math.max(1, centerState.getLightBlock()); ++ final int opacity = Math.max(1, centerState.getLightDampening()); + if (((StarlightAbstractBlockState)centerState).starlight$isConditionallyFullOpaque()) { + conditionallyOpaqueState = centerState; + } else { @@ -19289,8 +20062,8 @@ index 0000000000000000000000000000000000000000..cdb679fb6e3d342efc776329124209d1 + Arrays.fill(this.nullPropagationCheckCache, false); + + final BlockGetter world = lightAccess.getLevel(); -+ final int chunkX = atChunk.getPos().x; -+ final int chunkZ = atChunk.getPos().z; ++ final int chunkX = atChunk.getPos().x(); ++ final int chunkZ = atChunk.getPos().z(); + final int heightMapOffset = chunkX * -16 + (chunkZ * (-16 * 16)); + + // setup heightmap for changes @@ -19382,8 +20155,8 @@ index 0000000000000000000000000000000000000000..cdb679fb6e3d342efc776329124209d1 + + final BlockGetter world = lightAccess.getLevel(); + final ChunkPos chunkPos = chunk.getPos(); -+ final int chunkX = chunkPos.x; -+ final int chunkZ = chunkPos.z; ++ final int chunkX = chunkPos.x(); ++ final int chunkZ = chunkPos.z(); + + final LevelChunkSection[] sections = chunk.getSections(); + @@ -19459,10 +20232,10 @@ index 0000000000000000000000000000000000000000..cdb679fb6e3d342efc776329124209d1 + + if (highestNonEmptySection >= this.minSection) { + // fill out our other sources -+ final int minX = chunkPos.x << 4; -+ final int maxX = chunkPos.x << 4 | 15; -+ final int minZ = chunkPos.z << 4; -+ final int maxZ = chunkPos.z << 4 | 15; ++ final int minX = chunkPos.x() << 4; ++ final int maxX = chunkPos.x() << 4 | 15; ++ final int minZ = chunkPos.z() << 4; ++ final int maxZ = chunkPos.z() << 4 | 15; + final int startY = highestNonEmptySection << 4 | 15; + for (int currZ = minZ; currZ <= maxZ; ++currZ) { + for (int currX = minX; currX <= maxX; ++currX) { @@ -19574,7 +20347,7 @@ index 0000000000000000000000000000000000000000..cdb679fb6e3d342efc776329124209d1 + flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; + } + -+ final int opacity = current.getLightBlock(); ++ final int opacity = current.getLightDampening(); + if (opacity > 0) { + // let the queued value (if any) handle it from here. + break; @@ -19614,7 +20387,7 @@ index 0000000000000000000000000000000000000000..cdb679fb6e3d342efc776329124209d1 +} diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightEngine.java b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightEngine.java new file mode 100644 -index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc4620ce3672 +index 0000000000000000000000000000000000000000..d5eb47f26039220456d271d2f6d00be645e06616 --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightEngine.java @@ -0,0 +1,1450 @@ @@ -20192,8 +20965,8 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 + + protected void checkChunkEdges(final LightChunkGetter lightAccess, final ChunkAccess chunk, final ShortCollection sections) { + final ChunkPos chunkPos = chunk.getPos(); -+ final int chunkX = chunkPos.x; -+ final int chunkZ = chunkPos.z; ++ final int chunkX = chunkPos.x(); ++ final int chunkZ = chunkPos.z(); + + for (final ShortIterator iterator = sections.iterator(); iterator.hasNext();) { + this.checkChunkEdge(lightAccess, chunk, chunkX, iterator.nextShort(), chunkZ); @@ -20209,8 +20982,8 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 + // This does not resolve skylight source problems. + protected void checkChunkEdges(final LightChunkGetter lightAccess, final ChunkAccess chunk, final int fromSection, final int toSection) { + final ChunkPos chunkPos = chunk.getPos(); -+ final int chunkX = chunkPos.x; -+ final int chunkZ = chunkPos.z; ++ final int chunkX = chunkPos.x(); ++ final int chunkZ = chunkPos.z(); + + for (int currSectionY = toSection; currSectionY >= fromSection; --currSectionY) { + this.checkChunkEdge(lightAccess, chunk, chunkX, currSectionY, chunkZ); @@ -20222,8 +20995,8 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 + // pulls light from neighbours, and adds them into the increase queue. does not actually propagate. + protected final void propagateNeighbourLevels(final LightChunkGetter lightAccess, final ChunkAccess chunk, final int fromSection, final int toSection) { + final ChunkPos chunkPos = chunk.getPos(); -+ final int chunkX = chunkPos.x; -+ final int chunkZ = chunkPos.z; ++ final int chunkX = chunkPos.x(); ++ final int chunkZ = chunkPos.z(); + + for (int currSectionY = toSection; currSectionY >= fromSection; --currSectionY) { + final SWMRNibbleArray currNibble = this.getNibbleFromCache(chunkX, currSectionY, chunkZ); @@ -20318,8 +21091,8 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 + } + + public final void forceHandleEmptySectionChanges(final LightChunkGetter lightAccess, final ChunkAccess chunk, final Boolean[] emptinessChanges) { -+ final int chunkX = chunk.getPos().x; -+ final int chunkZ = chunk.getPos().z; ++ final int chunkX = chunk.getPos().x(); ++ final int chunkZ = chunk.getPos().z(); + this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true, true); + try { + // force current chunk into cache @@ -20368,8 +21141,8 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 + protected final boolean[] handleEmptySectionChanges(final LightChunkGetter lightAccess, final ChunkAccess chunk, + final Boolean[] emptinessChanges, final boolean unlit) { + final Level world = (Level)lightAccess.getLevel(); -+ final int chunkX = chunk.getPos().x; -+ final int chunkZ = chunk.getPos().z; ++ final int chunkX = chunk.getPos().x(); ++ final int chunkZ = chunk.getPos().z(); + + boolean[] chunkEmptinessMap = this.getEmptinessMap(chunkX, chunkZ); + boolean[] ret = null; @@ -20519,8 +21292,8 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 + protected abstract void lightChunk(final LightChunkGetter lightAccess, final ChunkAccess chunk, final boolean needsEdgeChecks); + + public final void light(final LightChunkGetter lightAccess, final ChunkAccess chunk, final Boolean[] emptySections) { -+ final int chunkX = chunk.getPos().x; -+ final int chunkZ = chunk.getPos().z; ++ final int chunkX = chunk.getPos().x(); ++ final int chunkZ = chunk.getPos().z(); + this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true, true); + + try { @@ -20569,8 +21342,8 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 + int lightCalls = 0; + + for (final ChunkPos chunkPos : chunks) { -+ final int chunkX = chunkPos.x; -+ final int chunkZ = chunkPos.z; ++ final int chunkX = chunkPos.x(); ++ final int chunkZ = chunkPos.z(); + final ChunkAccess chunk = (ChunkAccess)lightAccess.getChunkForLighting(chunkX, chunkZ); + if (chunk == null || !this.canUseChunk(chunk)) { + throw new IllegalStateException(); @@ -20791,7 +21564,7 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 + flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; + } + -+ final int opacity = blockState.getLightBlock(); ++ final int opacity = blockState.getLightDampening(); + final int targetLevel = propagatedLightLevel - Math.max(1, opacity); + if (targetLevel <= currentLevel) { + continue; @@ -20850,7 +21623,7 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 + flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; + } + -+ final int opacity = blockState.getLightBlock(); ++ final int opacity = blockState.getLightDampening(); + final int targetLevel = propagatedLightLevel - Math.max(1, opacity); + if (targetLevel <= currentLevel) { + continue; @@ -20934,7 +21707,7 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 + flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; + } + -+ final int opacity = blockState.getLightBlock(); ++ final int opacity = blockState.getLightDampening(); + final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity)); + if (lightLevel > targetLevel) { + // it looks like another source propagated here, so re-propagate it @@ -21017,7 +21790,7 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 + flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; + } + -+ final int opacity = blockState.getLightBlock(); ++ final int opacity = blockState.getLightDampening(); + final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity)); + if (lightLevel > targetLevel) { + // it looks like another source propagated here, so re-propagate it @@ -21070,7 +21843,7 @@ index 0000000000000000000000000000000000000000..c6222bfb75706052df41d821f2fcfc46 +} diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java new file mode 100644 -index 0000000000000000000000000000000000000000..7faf226b753a03e03b0dec9bbfcc94a6c3f5bc0f +index 0000000000000000000000000000000000000000..ef896fd3cad9ad8b67f5048fab2cfe28559bdca5 --- /dev/null +++ b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java @@ -0,0 +1,936 @@ @@ -21078,7 +21851,7 @@ index 0000000000000000000000000000000000000000..7faf226b753a03e03b0dec9bbfcc94a6 + +import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue; +import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; -+import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; ++import ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2ReferenceHashTable; +import ca.spottedleaf.concurrentutil.util.Priority; +import ca.spottedleaf.moonrise.common.util.CoordinateUtils; +import ca.spottedleaf.moonrise.common.util.WorldUtil; @@ -21821,7 +22594,7 @@ index 0000000000000000000000000000000000000000..7faf226b753a03e03b0dec9bbfcc94a6 + + public static final class ServerLightQueue extends LightQueue { + -+ private final ConcurrentLong2ReferenceChainedHashTable chunkTasks = new ConcurrentLong2ReferenceChainedHashTable<>(); ++ private final ConcurrentChainedLong2ReferenceHashTable chunkTasks = new ConcurrentChainedLong2ReferenceHashTable<>(); + + public ServerLightQueue(final StarLightInterface lightInterface) { + super(lightInterface); @@ -22268,7 +23041,7 @@ index 0000000000000000000000000000000000000000..95154636727366adfb4fb67e7db8b09f + private SaveUtil() {} +} diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java -index 61c526263cfbd1871f6b8548ed4431a76ee95a31..485413cb6b20c4bd20fbc29a0db43c051993fe79 100644 +index 1cd5901c8e4eb4114ab347a393fc37095c8642d2..fadce1c051e6c7bc0c3d041c0cc8ecd7e1ae43e0 100644 --- a/io/papermc/paper/FeatureHooks.java +++ b/io/papermc/paper/FeatureHooks.java @@ -1,6 +1,9 @@ @@ -22281,12 +23054,12 @@ index 61c526263cfbd1871f6b8548ed4431a76ee95a31..485413cb6b20c4bd20fbc29a0db43c05 import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSets; -@@ -32,13 +35,16 @@ public final class FeatureHooks { +@@ -31,13 +34,16 @@ public final class FeatureHooks { // this includes non-accessible entities - public static Iterable getAllEntities(final net.minecraft.server.level.ServerLevel world) { -- return ((net.minecraft.world.level.entity.LevelEntityGetterAdapter)world.getEntities()).sectionStorage.getAllEntities(); -+ return ((ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup)world.getEntities()).getAllMapped(); // Paper - rewrite chunk system + public static Iterable getAllEntities(final net.minecraft.server.level.ServerLevel level) { +- return ((net.minecraft.world.level.entity.LevelEntityGetterAdapter)level.getEntities()).sectionStorage.getAllEntities(); ++ return ((ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup)level.getEntities()).getAllMapped(); // Paper - rewrite chunk system } public static void setPlayerChunkUnloadDelay(final long ticks) { @@ -22299,12 +23072,12 @@ index 61c526263cfbd1871f6b8548ed4431a76ee95a31..485413cb6b20c4bd20fbc29a0db43c05 } public static LevelChunkSection createSection(final PalettedContainerFactory palettedContainerFactory, final Level level, final ChunkPos chunkPos, final int chunkSection) { -@@ -59,111 +65,58 @@ public final class FeatureHooks { +@@ -58,117 +64,59 @@ public final class FeatureHooks { } public static Set getSentChunkKeys(final ServerPlayer player) { - final LongSet keys = new LongOpenHashSet(); -- player.getChunkTrackingView().forEach(pos -> keys.add(pos.longKey)); +- player.getChunkTrackingView().forEach(pos -> keys.add(pos.pack())); - return LongSets.unmodifiable(keys); + return LongSets.unmodifiable(player.moonrise$getChunkLoader().getSentChunksRaw().clone()); // Paper - rewrite chunk system } @@ -22319,7 +23092,7 @@ index 61c526263cfbd1871f6b8548ed4431a76ee95a31..485413cb6b20c4bd20fbc29a0db43c05 + final ObjectSet chunks = new ObjectOpenHashSet<>(rawChunkKeys.size()); final World world = player.level().getWorld(); - player.getChunkTrackingView().forEach(pos -> { -- final org.bukkit.Chunk chunk = world.getChunkAt(pos.longKey); +- final org.bukkit.Chunk chunk = world.getChunkAt(pos.pack()); - chunks.add(chunk); - }); + final LongIterator iter = player.moonrise$getChunkLoader().getSentChunksRaw().longIterator(); @@ -22331,15 +23104,21 @@ index 61c526263cfbd1871f6b8548ed4431a76ee95a31..485413cb6b20c4bd20fbc29a0db43c05 } public static boolean isChunkSent(final ServerPlayer player, final long chunkKey) { -- return player.getChunkTrackingView().contains(new ChunkPos(chunkKey)); +- return player.getChunkTrackingView().contains(ChunkPos.unpack(chunkKey)); + // Paper start - rewrite chunk system + return player.moonrise$getChunkLoader() != null && player.moonrise$getChunkLoader().getSentChunksRaw().contains(chunkKey); + // Paper end - rewrite chunk system } - public static boolean isSpiderCollidingWithWorldBorder(final Spider spider) { -- return true; // ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isCollidingWithBorder(spider.level().getWorldBorder(), spider.getBoundingBox().inflate(ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) -+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isCollidingWithBorder(spider.level().getWorldBorder(), spider.getBoundingBox().inflate(ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)); // Paper - rewrite collision system + public static boolean isCollidingWithWorldBorder(final Entity entity) { + final net.minecraft.world.level.border.WorldBorder border = entity.level().getWorldBorder(); +- // Inflate by +EPSILON as collision will just barely place us outside border +- return net.minecraft.world.phys.shapes.Shapes.joinIsNotEmpty( +- border.getCollisionShape(), +- net.minecraft.world.phys.shapes.Shapes.create(entity.getBoundingBox().inflate(net.minecraft.util.Util.COLLISION_EPSILON)), net.minecraft.world.phys.shapes.BooleanOp.AND +- ) && border.isInsideCloseToBorder(entity, entity.getBoundingBox()); +- // return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isCollidingWithBorder(border, entity.getBoundingBox().inflate(ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) && border.isInsideCloseToBorder(entity, entity.getBoundingBox()) ++ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isCollidingWithBorder(border, entity.getBoundingBox().inflate(ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) && border.isInsideCloseToBorder(entity, entity.getBoundingBox()); // Paper - rewrite collision system } public static void dumpAllChunkLoadInfo(net.minecraft.server.MinecraftServer server, boolean isLongTimeout) { @@ -22349,38 +23128,38 @@ index 61c526263cfbd1871f6b8548ed4431a76ee95a31..485413cb6b20c4bd20fbc29a0db43c05 private static void dumpEntity(final Entity entity) { } - public static org.bukkit.entity.Entity[] getChunkEntities(net.minecraft.server.level.ServerLevel world, int chunkX, int chunkZ) { -- world.getChunk(chunkX, chunkZ); // ensure full loaded + public static org.bukkit.entity.Entity[] getChunkEntities(net.minecraft.server.level.ServerLevel level, int chunkX, int chunkZ) { +- level.getChunk(chunkX, chunkZ); // ensure full loaded - -- net.minecraft.world.level.entity.PersistentEntitySectionManager entityManager = world.entityManager; -- long pair = ChunkPos.asLong(chunkX, chunkZ); +- net.minecraft.world.level.entity.PersistentEntitySectionManager entityManager = level.entityManager; +- long chunkKey = ChunkPos.pack(chunkX, chunkZ); - -- if (entityManager.areEntitiesLoaded(pair)) { +- if (entityManager.areEntitiesLoaded(chunkKey)) { - return entityManager.getEntities(new ChunkPos(chunkX, chunkZ)).stream() - .map(net.minecraft.world.entity.Entity::getBukkitEntity) - .filter(java.util.Objects::nonNull).toArray(org.bukkit.entity.Entity[]::new); - } - -- entityManager.ensureChunkQueuedForLoad(pair); // Start entity loading +- entityManager.ensureChunkQueuedForLoad(chunkKey); // Start entity loading - - // SPIGOT-6772: Use entity mailbox and re-schedule entities if they get unloaded - net.minecraft.util.thread.ConsecutiveExecutor mailbox = ((net.minecraft.world.level.chunk.storage.EntityStorage) entityManager.permanentStorage).entityDeserializerQueue; - java.util.function.BooleanSupplier supplier = () -> { - // only execute inbox if our entities are not present -- if (entityManager.areEntitiesLoaded(pair)) { +- if (entityManager.areEntitiesLoaded(chunkKey)) { - return true; - } - -- if (!entityManager.isPending(pair)) { +- if (!entityManager.isPending(chunkKey)) { - // Our entities got unloaded, this should normally not happen. -- entityManager.ensureChunkQueuedForLoad(pair); // Re-start entity loading +- entityManager.ensureChunkQueuedForLoad(chunkKey); // Re-start entity loading - } - - // tick loading inbox, which loads the created entities to the world - // (if present) - entityManager.tick(); - // check if our entities are loaded -- return entityManager.areEntitiesLoaded(pair); +- return entityManager.areEntitiesLoaded(chunkKey); - }; - - // now we wait until the entities are loaded, @@ -22397,13 +23176,13 @@ index 61c526263cfbd1871f6b8548ed4431a76ee95a31..485413cb6b20c4bd20fbc29a0db43c05 - return entityManager.getEntities(new ChunkPos(chunkX, chunkZ)).stream() - .map(net.minecraft.world.entity.Entity::getBukkitEntity) - .filter(java.util.Objects::nonNull).toArray(org.bukkit.entity.Entity[]::new); -+ return world.getChunkEntities(chunkX, chunkZ); // Paper - rewrite chunk system ++ return level.getChunkEntities(chunkX, chunkZ); // Paper - rewrite chunk system } - public static java.util.Collection getPluginChunkTickets(net.minecraft.server.level.ServerLevel world, + public static java.util.Collection getPluginChunkTickets(net.minecraft.server.level.ServerLevel level, int x, int z) { -- net.minecraft.server.level.DistanceManager chunkDistanceManager = world.getChunkSource().chunkMap.distanceManager; -- List tickets = chunkDistanceManager.ticketStorage.tickets.get(ChunkPos.asLong(x, z)); +- net.minecraft.server.level.DistanceManager chunkDistanceManager = level.getChunkSource().chunkMap.distanceManager; +- List tickets = chunkDistanceManager.ticketStorage.tickets.get(ChunkPos.pack(x, z)); - - if (tickets == null) { - return java.util.Collections.emptyList(); @@ -22417,12 +23196,12 @@ index 61c526263cfbd1871f6b8548ed4431a76ee95a31..485413cb6b20c4bd20fbc29a0db43c05 - } - - return ret.build(); -+ return world.moonrise$getChunkTaskScheduler().chunkHolderManager.getPluginChunkTickets(x, z); // Paper - rewrite chunk system ++ return level.moonrise$getChunkTaskScheduler().chunkHolderManager.getPluginChunkTickets(x, z); // Paper - rewrite chunk system } - public static Map> getPluginChunkTickets(net.minecraft.server.level.ServerLevel world) { + public static Map> getPluginChunkTickets(net.minecraft.server.level.ServerLevel level) { Map> ret = new HashMap<>(); - net.minecraft.server.level.DistanceManager chunkDistanceManager = world.getChunkSource().chunkMap.distanceManager; + net.minecraft.server.level.DistanceManager chunkDistanceManager = level.getChunkSource().chunkMap.distanceManager; - for (it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry> chunkTickets : chunkDistanceManager.ticketStorage.tickets.long2ObjectEntrySet()) { + for (it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry> chunkTickets : chunkDistanceManager.moonrise$getChunkHolderManager().getTicketsCopy().long2ObjectEntrySet()) { // Paper - rewrite chunk system @@ -22432,41 +23211,41 @@ index 61c526263cfbd1871f6b8548ed4431a76ee95a31..485413cb6b20c4bd20fbc29a0db43c05 org.bukkit.Chunk chunk = null; for (net.minecraft.server.level.Ticket ticket : tickets) { -@@ -183,15 +136,15 @@ public final class FeatureHooks { +@@ -188,15 +136,15 @@ public final class FeatureHooks { } - public static int getViewDistance(net.minecraft.server.level.ServerLevel world) { -- return world.getChunkSource().chunkMap.serverViewDistance; -+ return world.moonrise$getPlayerChunkLoader().getAPIViewDistance(); // Paper - rewrite chunk system + public static int getViewDistance(net.minecraft.server.level.ServerLevel level) { +- return level.getChunkSource().chunkMap.serverViewDistance; ++ return level.moonrise$getPlayerChunkLoader().getAPIViewDistance(); // Paper - rewrite chunk system } - public static int getSimulationDistance(net.minecraft.server.level.ServerLevel world) { -- return world.getChunkSource().chunkMap.getDistanceManager().simulationDistance; -+ return world.moonrise$getPlayerChunkLoader().getAPITickDistance(); // Paper - rewrite chunk system + public static int getSimulationDistance(net.minecraft.server.level.ServerLevel level) { +- return level.getChunkSource().chunkMap.getDistanceManager().simulationDistance; ++ return level.moonrise$getPlayerChunkLoader().getAPITickDistance(); // Paper - rewrite chunk system } - public static int getSendViewDistance(net.minecraft.server.level.ServerLevel world) { -- return getViewDistance(world); -+ return world.moonrise$getPlayerChunkLoader().getAPISendViewDistance(); // Paper - rewrite chunk system + public static int getSendViewDistance(net.minecraft.server.level.ServerLevel level) { +- return getViewDistance(level); ++ return level.moonrise$getPlayerChunkLoader().getAPISendViewDistance(); // Paper - rewrite chunk system } - public static void setViewDistance(net.minecraft.server.level.ServerLevel world, int distance) { -@@ -209,35 +162,31 @@ public final class FeatureHooks { + public static void setViewDistance(net.minecraft.server.level.ServerLevel level, int distance) { +@@ -214,35 +162,31 @@ public final class FeatureHooks { } - public static void setSendViewDistance(net.minecraft.server.level.ServerLevel world, int distance) { + public static void setSendViewDistance(net.minecraft.server.level.ServerLevel level, int distance) { - throw new UnsupportedOperationException("Not implemented yet"); -+ world.chunkSource.setSendViewDistance(distance); // Paper - rewrite chunk system ++ level.chunkSource.setSendViewDistance(distance); // Paper - rewrite chunk system } - public static void tickEntityManager(net.minecraft.server.level.ServerLevel world) { -- world.entityManager.tick(); + public static void tickEntityManager(net.minecraft.server.level.ServerLevel level) { +- level.entityManager.tick(); + // Paper - rewrite chunk system } - public static void closeEntityManager(net.minecraft.server.level.ServerLevel world, boolean save) { + public static void closeEntityManager(net.minecraft.server.level.ServerLevel level, boolean save) { - try { -- world.entityManager.close(save); +- level.entityManager.close(save); - } catch (final java.io.IOException exception) { - throw new RuntimeException("Failed to close entity manager", exception); - } @@ -22781,7 +23560,7 @@ index 0000000000000000000000000000000000000000..2dca7afbd93cfbb8686f336fcd3b45dd +} diff --git a/io/papermc/paper/command/subcommands/FixLightCommand.java b/io/papermc/paper/command/subcommands/FixLightCommand.java new file mode 100644 -index 0000000000000000000000000000000000000000..85950a1aa732ab8c01ad28bec9e0de140e1a172e +index 0000000000000000000000000000000000000000..c7303da057111baae585143979e7b1ffba0aad86 --- /dev/null +++ b/io/papermc/paper/command/subcommands/FixLightCommand.java @@ -0,0 +1,116 @@ @@ -22868,7 +23647,7 @@ index 0000000000000000000000000000000000000000..85950a1aa732ab8c01ad28bec9e0de14 + for (java.util.Iterator iterator = chunks.iterator(); iterator.hasNext(); ) { + final ChunkPos chunkPos = iterator.next(); + -+ final @Nullable ChunkAccess chunk = (ChunkAccess) world.getChunkSource().getChunkForLighting(chunkPos.x, chunkPos.z); ++ final @Nullable ChunkAccess chunk = (ChunkAccess) world.getChunkSource().getChunkForLighting(chunkPos.x(), chunkPos.z()); + if (chunk == null || !chunk.isLightCorrect() || !chunk.getPersistedStatus().isOrAfter(net.minecraft.world.level.chunk.status.ChunkStatus.LIGHT)) { + // cannot relight this chunk + iterator.remove(); @@ -22918,7 +23697,7 @@ index 0000000000000000000000000000000000000000..8424cf9d4617b4732d44cc460d25b044 + +} diff --git a/net/minecraft/core/Direction.java b/net/minecraft/core/Direction.java -index 3a5aad2acd2a7ac1f0fc3c33f1705c5d737cb10c..9926682586fda91017e3e1ab079296a8c4a88879 100644 +index 3f516ffd7a76f1d17daaa865f7446803b8217bcc..240fb1346b721e4aaf8517b01fa205760cee4bbd 100644 --- a/net/minecraft/core/Direction.java +++ b/net/minecraft/core/Direction.java @@ -29,7 +29,7 @@ import org.joml.Vector3f; @@ -22930,7 +23709,7 @@ index 3a5aad2acd2a7ac1f0fc3c33f1705c5d737cb10c..9926682586fda91017e3e1ab079296a8 DOWN(0, 1, -1, "down", Direction.AxisDirection.NEGATIVE, Direction.Axis.Y, new Vec3i(0, -1, 0)), UP(1, 0, -1, "up", Direction.AxisDirection.POSITIVE, Direction.Axis.Y, new Vec3i(0, 1, 0)), NORTH(2, 3, 2, "north", Direction.AxisDirection.NEGATIVE, Direction.Axis.Z, new Vec3i(0, 0, -1)), -@@ -70,6 +70,46 @@ public enum Direction implements StringRepresentable { +@@ -68,6 +68,46 @@ public enum Direction implements StringRepresentable { private final int adjY; private final int adjZ; // Paper end - Perf: Inline shift direction fields @@ -22977,17 +23756,17 @@ index 3a5aad2acd2a7ac1f0fc3c33f1705c5d737cb10c..9926682586fda91017e3e1ab079296a8 private Direction( final int data3d, -@@ -155,14 +195,13 @@ public enum Direction implements StringRepresentable { +@@ -153,14 +193,13 @@ public enum Direction implements StringRepresentable { } public Quaternionf getRotation() { - return switch (this) { -- case DOWN -> new Quaternionf().rotationX((float) Math.PI); +- case DOWN -> new Quaternionf().rotationX(Mth.PI); - case UP -> new Quaternionf(); -- case NORTH -> new Quaternionf().rotationXYZ((float) (Math.PI / 2), 0.0F, (float) Math.PI); -- case SOUTH -> new Quaternionf().rotationX((float) (Math.PI / 2)); -- case WEST -> new Quaternionf().rotationXYZ((float) (Math.PI / 2), 0.0F, (float) (Math.PI / 2)); -- case EAST -> new Quaternionf().rotationXYZ((float) (Math.PI / 2), 0.0F, (float) (-Math.PI / 2)); +- case NORTH -> new Quaternionf().rotationXYZ(Mth.PI / 2.0F, 0.0F, Mth.PI); +- case SOUTH -> new Quaternionf().rotationX(Mth.PI / 2.0F); +- case WEST -> new Quaternionf().rotationXYZ(Mth.PI / 2.0F, 0.0F, Mth.PI / 2.0F); +- case EAST -> new Quaternionf().rotationXYZ(Mth.PI / 2.0F, 0.0F, -Mth.PI / 2.0F); - }; + // Paper start - optimise collisions + try { @@ -22999,7 +23778,7 @@ index 3a5aad2acd2a7ac1f0fc3c33f1705c5d737cb10c..9926682586fda91017e3e1ab079296a8 } public int get3DDataValue() { -@@ -186,7 +225,7 @@ public enum Direction implements StringRepresentable { +@@ -184,7 +223,7 @@ public enum Direction implements StringRepresentable { } public Direction getOpposite() { @@ -23007,8 +23786,8 @@ index 3a5aad2acd2a7ac1f0fc3c33f1705c5d737cb10c..9926682586fda91017e3e1ab079296a8 + return this.opposite; // Paper - optimise collisions } - public Direction getClockWise(Direction.Axis axis) { -@@ -629,4 +668,17 @@ public enum Direction implements StringRepresentable { + public Direction getClockWise(final Direction.Axis axis) { +@@ -627,4 +666,17 @@ public enum Direction implements StringRepresentable { return this.faces.length; } } @@ -23027,10 +23806,10 @@ index 3a5aad2acd2a7ac1f0fc3c33f1705c5d737cb10c..9926682586fda91017e3e1ab079296a8 + // Paper end - optimise collisions } diff --git a/net/minecraft/core/MappedRegistry.java b/net/minecraft/core/MappedRegistry.java -index fd09226af661a6970062ee11d38f6e69464fb74b..ad1558693564ebd18f5e16b85ae552d2af95efef 100644 +index 3c1490ac7c259da04031db2f170e0c0a5f512191..470d7c770ae9d045b97e2df145cfe3cf9d101deb 100644 --- a/net/minecraft/core/MappedRegistry.java +++ b/net/minecraft/core/MappedRegistry.java -@@ -56,6 +56,19 @@ public class MappedRegistry implements WritableRegistry { +@@ -58,6 +58,19 @@ public class MappedRegistry implements WritableRegistry { return this.getTags(); } @@ -23047,46 +23826,34 @@ index fd09226af661a6970062ee11d38f6e69464fb74b..ad1558693564ebd18f5e16b85ae552d2 + } + // Paper end - fluid method optimisations + - public MappedRegistry(ResourceKey> key, Lifecycle registryLifecycle) { - this(key, registryLifecycle, false); + public MappedRegistry(final ResourceKey> key, final Lifecycle lifecycle) { + this(key, lifecycle, false); } -@@ -121,6 +134,7 @@ public class MappedRegistry implements WritableRegistry { +@@ -123,6 +136,7 @@ public class MappedRegistry implements WritableRegistry { this.registrationInfos.put(key, registrationInfo); this.registryLifecycle = this.registryLifecycle.add(registrationInfo.lifecycle()); this.temporaryUnfrozenMap.put(key.identifier(), value); // Paper - support pre-filling in registry mod API + this.injectFluidRegister(key, value); // Paper - fluid method optimisations - return reference; + return holder; } } -diff --git a/net/minecraft/server/Main.java b/net/minecraft/server/Main.java -index 3e8870cf4eade33894c6b2f7d3d42a5607645118..9216e85e74bb0121c72665f155b07cf9a37f430a 100644 ---- a/net/minecraft/server/Main.java -+++ b/net/minecraft/server/Main.java -@@ -299,6 +299,7 @@ public class Main { - - levelStorageAccess.saveDataTag(frozen, worldData); - */ -+ Class.forName(net.minecraft.world.entity.npc.villager.VillagerTrades.class.getName()); // Paper - load this sync so it won't fail later async - final DedicatedServer dedicatedServer = MinecraftServer.spin( - thread1 -> { - DedicatedServer dedicatedServer1 = new DedicatedServer( diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 97a6c426654fe2e103ed46bb295e098a9c9be526..8588b380f9b5375cdb2515a01e46a5731ad3ab33 100644 +index 9566334f019ff3c9469f06c784d122b5d35f3e24..e11862c7e0617acfb36212cfe349bdade3a5164b 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -185,7 +185,7 @@ import net.minecraft.world.scores.ScoreboardSaveData; +@@ -191,7 +191,7 @@ import net.minecraft.world.scores.ScoreboardSaveData; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; --public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements ServerInfo, CommandSource, ChunkIOErrorReporter { -+public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements ServerInfo, CommandSource, ChunkIOErrorReporter, ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer { // Paper - rewrite chunk system +-public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements CommandSource, ServerInfo, ChunkIOErrorReporter { ++public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements CommandSource, ServerInfo, ChunkIOErrorReporter, ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer { // Paper - rewrite chunk system private static MinecraftServer SERVER; // Paper public static final Logger LOGGER = LogUtils.getLogger(); public static final net.kyori.adventure.text.logger.slf4j.ComponentLogger COMPONENT_LOGGER = net.kyori.adventure.text.logger.slf4j.ComponentLogger.logger(LOGGER.getName()); // Paper -@@ -397,6 +397,93 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop level.getChunkSource().chunkMap.hasWork())) { -+ while (false && this.levels.values().stream().anyMatch(level -> level.getChunkSource().chunkMap.hasWork())) { // Paper - rewrite chunk system +- while (this.levels.values().stream().anyMatch(l -> l.getChunkSource().chunkMap.hasWork())) { ++ while (false && this.levels.values().stream().anyMatch(l -> l.getChunkSource().chunkMap.hasWork())) { // Paper - rewrite chunk system this.nextTickTimeNanos = Util.getNanos() + TimeUtil.NANOSECONDS_PER_MILLISECOND; - for (ServerLevel serverLevelx : this.getAllLevels()) { -@@ -956,18 +1048,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop false : this::haveTime); @@ -23313,8 +24078,8 @@ index 97a6c426654fe2e103ed46bb295e098a9c9be526..8588b380f9b5375cdb2515a01e46a573 + // Paper end - rewrite chunk system this.tickFrame.end(); this.recordEndOfTick(); // Paper - improve tick loop - profilerFiller.pop(); -@@ -2557,6 +2676,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop dependency) { + public void addSendDependency(final CompletableFuture sync) { - if (this.sendSync.isDone()) { -- this.sendSync = dependency; +- this.sendSync = sync; - } else { -- this.sendSync = this.sendSync.thenCombine((CompletionStage)dependency, (object, object1) -> null); +- this.sendSync = this.sendSync.thenCombine((CompletionStage)sync, (a, b) -> null); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @@ -23583,25 +24348,25 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 } @Override - protected void addSaveDependency(CompletableFuture dependency) { + protected void addSaveDependency(final CompletableFuture sync) { - if (this.saveSync.isDone()) { -- this.saveSync = dependency; +- this.saveSync = sync; - } else { -- this.saveSync = this.saveSync.thenCombine((CompletionStage)dependency, (object, object1) -> null); +- this.saveSync = this.saveSync.thenCombine((CompletionStage)sync, (a, b) -> null); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public boolean blockChanged(BlockPos pos) { -- LevelChunk tickingChunk = this.getTickingChunk(); -+ LevelChunk tickingChunk = this.playersSentChunkTo.size() == 0 ? null : this.getChunkToSend(); // Paper - rewrite chunk system - if (tickingChunk == null) { + public boolean blockChanged(final BlockPos pos) { +- LevelChunk chunk = this.getTickingChunk(); ++ LevelChunk chunk = this.playersSentChunkTo.size() == 0 ? null : this.getChunkToSend(); // Paper - rewrite chunk system + if (chunk == null) { return false; } else { @@ -158,7 +246,7 @@ public class ChunkHolder extends GenerationChunkHolder { return false; } else { - chunkIfPresent.markUnsaved(); + chunk.markUnsaved(); - LevelChunk tickingChunk = this.getTickingChunk(); + LevelChunk tickingChunk = this.playersSentChunkTo.size() == 0 ? null : this.getChunkToSend(); // Paper - rewrite chunk system if (tickingChunk == null) { @@ -23611,10 +24376,10 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 if (this.hasChangesToBroadcast()) { Level level = chunk.getLevel(); if (!this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty()) { -- List players = this.playerProvider.getPlayers(this.pos, true); -+ List players = this.moonrise$getPlayers(true); // Paper - rewrite chunk system - if (!players.isEmpty()) { - ClientboundLightUpdatePacket clientboundLightUpdatePacket = new ClientboundLightUpdatePacket( +- List borderPlayers = this.playerProvider.getPlayers(this.pos, true); ++ List borderPlayers = this.moonrise$getPlayers(true); // Paper - rewrite chunk system + if (!borderPlayers.isEmpty()) { + ClientboundLightUpdatePacket lightPacket = new ClientboundLightUpdatePacket( chunk.getPos(), this.lightEngine, this.skyChangedLightSectionFilter, this.blockChangedLightSectionFilter @@ -201,7 +289,7 @@ public class ChunkHolder extends GenerationChunkHolder { } @@ -23623,9 +24388,9 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 - List players = this.playerProvider.getPlayers(this.pos, false); + List players = this.moonrise$getPlayers(false); // Paper - rewrite chunk system - for (int i = 0; i < this.changedBlocksPerSection.length; i++) { - ShortSet set = this.changedBlocksPerSection[i]; -@@ -256,193 +344,50 @@ public class ChunkHolder extends GenerationChunkHolder { + for (int sectionIndex = 0; sectionIndex < this.changedBlocksPerSection.length; sectionIndex++) { + ShortSet changedBlocks = this.changedBlocksPerSection[sectionIndex]; +@@ -252,193 +340,50 @@ public class ChunkHolder extends GenerationChunkHolder { @Override public int getTicketLevel() { @@ -23639,30 +24404,30 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void setQueueLevel(int queueLevel) { + private void setQueueLevel(final int queueLevel) { - this.queueLevel = queueLevel; + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public void setTicketLevel(int level) { -- this.ticketLevel = level; + public void setTicketLevel(final int ticketLevel) { +- this.ticketLevel = ticketLevel; + // Paper - rewrite chunk system } private void scheduleFullChunkPromotion( - ChunkMap chunkMap, CompletableFuture> future, Executor executor, FullChunkStatus fullChunkStatus + final ChunkMap scheduler, final CompletableFuture> task, final Executor mainThreadExecutor, final FullChunkStatus status ) { - this.pendingFullStateConfirmation.cancel(false); -- CompletableFuture completableFuture = new CompletableFuture<>(); -- completableFuture.thenRunAsync(() -> chunkMap.onFullChunkStatusChange(this.pos, fullChunkStatus), executor); -- this.pendingFullStateConfirmation = completableFuture; -- future.thenAccept(chunkResult -> chunkResult.ifSuccess(levelChunk -> completableFuture.complete(null))); +- CompletableFuture confirmation = new CompletableFuture<>(); +- confirmation.thenRunAsync(() -> scheduler.onFullChunkStatusChange(this.pos, status), mainThreadExecutor); +- this.pendingFullStateConfirmation = confirmation; +- task.thenAccept(r -> r.ifSuccess(l -> confirmation.complete(null))); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void demoteFullChunk(ChunkMap chunkMap, FullChunkStatus fullChunkStatus) { + private void demoteFullChunk(final ChunkMap scheduler, final FullChunkStatus status) { - this.pendingFullStateConfirmation.cancel(false); -- chunkMap.onFullChunkStatusChange(this.pos, fullChunkStatus); +- scheduler.onFullChunkStatusChange(this.pos, status); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @@ -23699,16 +24464,16 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 } // CraftBukkit end - protected void updateFutures(ChunkMap chunkMap, Executor executor) { -- FullChunkStatus fullChunkStatus = ChunkLevel.fullStatus(this.oldTicketLevel); -- FullChunkStatus fullChunkStatus1 = ChunkLevel.fullStatus(this.ticketLevel); -- boolean isOrAfter = fullChunkStatus.isOrAfter(FullChunkStatus.FULL); -- boolean isOrAfter1 = fullChunkStatus1.isOrAfter(FullChunkStatus.FULL); -- this.wasAccessibleSinceLastSave |= isOrAfter1; -- if (!isOrAfter && isOrAfter1) { + protected void updateFutures(final ChunkMap scheduler, final Executor mainThreadExecutor) { +- FullChunkStatus oldFullStatus = ChunkLevel.fullStatus(this.oldTicketLevel); +- FullChunkStatus newFullStatus = ChunkLevel.fullStatus(this.ticketLevel); +- boolean wasAccessible = oldFullStatus.isOrAfter(FullChunkStatus.FULL); +- boolean isAccessible = newFullStatus.isOrAfter(FullChunkStatus.FULL); +- this.wasAccessibleSinceLastSave |= isAccessible; +- if (!wasAccessible && isAccessible) { - int expectCreateCount = ++this.fullChunkCreateCount; // Paper -- this.fullChunkFuture = chunkMap.prepareAccessibleChunk(this); -- this.scheduleFullChunkPromotion(chunkMap, this.fullChunkFuture, executor, FullChunkStatus.FULL); +- this.fullChunkFuture = scheduler.prepareAccessibleChunk(this); +- this.scheduleFullChunkPromotion(scheduler, this.fullChunkFuture, mainThreadExecutor, FullChunkStatus.FULL); - // Paper start - cache ticking ready status - this.fullChunkFuture.thenAccept(chunkResult -> { - chunkResult.ifSuccess(chunk -> { @@ -23722,7 +24487,7 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 - this.addSaveDependency(this.fullChunkFuture); - } - -- if (isOrAfter && !isOrAfter1) { +- if (wasAccessible && !isAccessible) { - // Paper start - if (this.isFullChunkReady) { - ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkNotBorder(this.fullChunkFuture.join().orElseThrow(IllegalStateException::new), this); // Paper @@ -23732,11 +24497,11 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 - this.fullChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; - } - -- boolean isOrAfter2 = fullChunkStatus.isOrAfter(FullChunkStatus.BLOCK_TICKING); -- boolean isOrAfter3 = fullChunkStatus1.isOrAfter(FullChunkStatus.BLOCK_TICKING); -- if (!isOrAfter2 && isOrAfter3) { -- this.tickingChunkFuture = chunkMap.prepareTickingChunk(this); -- this.scheduleFullChunkPromotion(chunkMap, this.tickingChunkFuture, executor, FullChunkStatus.BLOCK_TICKING); +- boolean wasTicking = oldFullStatus.isOrAfter(FullChunkStatus.BLOCK_TICKING); +- boolean isTicking = newFullStatus.isOrAfter(FullChunkStatus.BLOCK_TICKING); +- if (!wasTicking && isTicking) { +- this.tickingChunkFuture = scheduler.prepareTickingChunk(this); +- this.scheduleFullChunkPromotion(scheduler, this.tickingChunkFuture, mainThreadExecutor, FullChunkStatus.BLOCK_TICKING); - // Paper start - cache ticking ready status - this.tickingChunkFuture.thenAccept(chunkResult -> { - chunkResult.ifSuccess(chunk -> { @@ -23745,11 +24510,11 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 - ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkTicking(chunk, this); - }); - }); -- // Paper end +- // Paper end - cache ticking ready status - this.addSaveDependency(this.tickingChunkFuture); - } - -- if (isOrAfter2 && !isOrAfter3) { +- if (wasTicking && !isTicking) { - // Paper start - if (this.isTickingReady) { - ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkNotTicking(this.tickingChunkFuture.join().orElseThrow(IllegalStateException::new), this); // Paper @@ -23759,15 +24524,15 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 - this.tickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; - } - -- boolean isOrAfter4 = fullChunkStatus.isOrAfter(FullChunkStatus.ENTITY_TICKING); -- boolean isOrAfter5 = fullChunkStatus1.isOrAfter(FullChunkStatus.ENTITY_TICKING); -- if (!isOrAfter4 && isOrAfter5) { +- boolean wasEntityTicking = oldFullStatus.isOrAfter(FullChunkStatus.ENTITY_TICKING); +- boolean isEntityTicking = newFullStatus.isOrAfter(FullChunkStatus.ENTITY_TICKING); +- if (!wasEntityTicking && isEntityTicking) { - if (this.entityTickingChunkFuture != UNLOADED_LEVEL_CHUNK_FUTURE) { - throw (IllegalStateException)Util.pauseInIde(new IllegalStateException()); - } - -- this.entityTickingChunkFuture = chunkMap.prepareEntityTickingChunk(this); -- this.scheduleFullChunkPromotion(chunkMap, this.entityTickingChunkFuture, executor, FullChunkStatus.ENTITY_TICKING); +- this.entityTickingChunkFuture = scheduler.prepareEntityTickingChunk(this); +- this.scheduleFullChunkPromotion(scheduler, this.entityTickingChunkFuture, mainThreadExecutor, FullChunkStatus.ENTITY_TICKING); - // Paper start - cache ticking ready status - this.entityTickingChunkFuture.thenAccept(chunkResult -> { - chunkResult.ifSuccess(chunk -> { @@ -23779,7 +24544,7 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 - this.addSaveDependency(this.entityTickingChunkFuture); - } - -- if (isOrAfter4 && !isOrAfter5) { +- if (wasEntityTicking && !isEntityTicking) { - // Paper start - if (this.isEntityTickingReady) { - ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkNotEntityTicking(this.entityTickingChunkFuture.join().orElseThrow(IllegalStateException::new), this); @@ -23789,19 +24554,19 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 - this.entityTickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; - } - -- if (!fullChunkStatus1.isOrAfter(fullChunkStatus)) { -- this.demoteFullChunk(chunkMap, fullChunkStatus1); +- if (!newFullStatus.isOrAfter(oldFullStatus)) { +- this.demoteFullChunk(scheduler, newFullStatus); - } - - this.onLevelChange.onLevelChange(this.pos, this::getQueueLevel, this.ticketLevel, this::setQueueLevel); - this.oldTicketLevel = this.ticketLevel; - // CraftBukkit start - // ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins. -- if (!fullChunkStatus.isOrAfter(FullChunkStatus.FULL) && fullChunkStatus1.isOrAfter(FullChunkStatus.FULL)) { +- if (!oldFullStatus.isOrAfter(FullChunkStatus.FULL) && newFullStatus.isOrAfter(FullChunkStatus.FULL)) { - this.getFullChunkFuture().thenAccept((either) -> { - LevelChunk chunk = (LevelChunk) either.orElse(null); - if (chunk != null) { -- chunkMap.callbackExecutor.execute(() -> { +- scheduler.callbackExecutor.execute(() -> { - chunk.loadCallback(); - }); - } @@ -23812,7 +24577,7 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 - }); - - // Run callback right away if the future was already done -- chunkMap.callbackExecutor.run(); +- scheduler.callbackExecutor.run(); - } - // CraftBukkit end + throw new UnsupportedOperationException(); // Paper - rewrite chunk system @@ -23830,7 +24595,7 @@ index e8f81fa17a0470ae9bf3f506fc7fb10fb7810cc7..22123d8b0fd8741c6df4efaec56f3c69 @FunctionalInterface diff --git a/net/minecraft/server/level/ChunkLevel.java b/net/minecraft/server/level/ChunkLevel.java -index 3a6fb3b46d94e23357be8af241340e3de06b7c7c..a5ce3593e92e6771f3c8df23ba1bf4d7872c9304 100644 +index 62290d63f44f4fb4e1eb02fb68ba9c34e051a50a..bbb7ca544df9342089d9630e3aa7e70050d3a947 100644 --- a/net/minecraft/server/level/ChunkLevel.java +++ b/net/minecraft/server/level/ChunkLevel.java @@ -8,7 +8,7 @@ import org.jspecify.annotations.Nullable; @@ -23843,18 +24608,9 @@ index 3a6fb3b46d94e23357be8af241340e3de06b7c7c..a5ce3593e92e6771f3c8df23ba1bf4d7 private static final ChunkStep FULL_CHUNK_STEP = ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.FULL); public static final int RADIUS_AROUND_FULL_CHUNK = FULL_CHUNK_STEP.accumulatedDependencies().getRadius(); diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef439cbde3f5 100644 +index 164920eaedc80f120abf4b6001b195d654217b71..23ae1694c3d5fffdb2277316f66c5fd59157f5ca 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java -@@ -108,7 +108,7 @@ import org.apache.commons.lang3.mutable.MutableBoolean; - import org.jspecify.annotations.Nullable; - import org.slf4j.Logger; - --public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerProvider, GeneratingChunkMap { -+public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerProvider, GeneratingChunkMap, ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemChunkMap { // Paper - rewrite chunk system - private static final ChunkResult> UNLOADED_CHUNK_LIST_RESULT = ChunkResult.error("Unloaded chunks found in range"); - private static final CompletableFuture>> UNLOADED_CHUNK_LIST_FUTURE = CompletableFuture.completedFuture( - UNLOADED_CHUNK_LIST_RESULT @@ -124,10 +124,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP public static final int MIN_VIEW_DISTANCE = 2; public static final int MAX_VIEW_DISTANCE = 32; @@ -23891,7 +24647,7 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback() public final CallbackExecutor callbackExecutor = new CallbackExecutor(); -@@ -175,9 +168,16 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP +@@ -175,7 +168,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP // Paper start public final ChunkHolder getUnloadingChunkHolder(int chunkX, int chunkZ) { @@ -23899,30 +24655,21 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + return null; // Paper - rewrite chunk system } // Paper end -+ // Paper start - rewrite chunk system -+ @Override -+ public final void moonrise$writeFinishCallback(final ChunkPos pos) throws IOException { -+ // see ChunkStorage#write -+ this.markChunkDone(pos); -+ } -+ // Paper end - rewrite chunk system - public ChunkMap( - ServerLevel level, -@@ -224,10 +224,9 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP - ConsecutiveExecutor consecutiveExecutor = new ConsecutiveExecutor(dispatcher, "worldgen"); +@@ -223,9 +216,9 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + ConsecutiveExecutor worldgen = new ConsecutiveExecutor(executor, "worldgen"); this.chunkStatusListener = chunkStatusListener; - ConsecutiveExecutor consecutiveExecutor1 = new ConsecutiveExecutor(dispatcher, "light"); -- this.worldgenTaskDispatcher = new ChunkTaskDispatcher(consecutiveExecutor, dispatcher); -- this.lightTaskDispatcher = new ChunkTaskDispatcher(consecutiveExecutor1, dispatcher); -+ // Paper - rewrite chunk system - this.lightEngine = new ThreadedLevelLightEngine( -- lightChunk, this, this.level.dimensionType().hasSkyLight(), consecutiveExecutor1, this.lightTaskDispatcher -+ lightChunk, this, this.level.dimensionType().hasSkyLight(), consecutiveExecutor1, null // Paper - rewrite chunk system - ); - this.distanceManager = new ChunkMap.DistanceManager(ticketStorage, dispatcher, mainThreadExecutor); + ConsecutiveExecutor light = new ConsecutiveExecutor(executor, "light"); +- this.worldgenTaskDispatcher = new ChunkTaskDispatcher(worldgen, executor); +- this.lightTaskDispatcher = new ChunkTaskDispatcher(light, executor); +- this.lightEngine = new ThreadedLevelLightEngine(chunkGetter, this, this.level.dimensionType().hasSkyLight(), light, this.lightTaskDispatcher); ++ //this.worldgenTaskDispatcher = new ChunkTaskDispatcher(worldgen, executor); // Paper - rewrite chunk system ++ //this.lightTaskDispatcher = new ChunkTaskDispatcher(light, executor); // Paper - rewrite chunk system ++ this.lightEngine = new ThreadedLevelLightEngine(chunkGetter, this, this.level.dimensionType().hasSkyLight(), light, null); // Paper - rewrite chunk system + this.distanceManager = new ChunkMap.DistanceManager(ticketStorage, executor, mainThreadExecutor); this.ticketStorage = ticketStorage; -@@ -241,11 +240,11 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + this.poiManager = new PoiManager( +@@ -238,11 +231,11 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP level ); this.setServerViewDistance(serverViewDistance); @@ -23930,27 +24677,27 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + this.worldGenContext = new WorldGenContext(level, generator, structureManager, this.lightEngine, null, this::setChunkUnsaved); // Paper - rewrite chunk system } - private void setChunkUnsaved(ChunkPos chunkPos) { -- this.chunksToEagerlySave.add(chunkPos.toLong()); + private void setChunkUnsaved(final ChunkPos chunkPos) { +- this.chunksToEagerlySave.add(chunkPos.pack()); + // Paper - rewrite chunk system } // Paper start -@@ -267,23 +266,11 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP +@@ -264,23 +257,11 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } - public boolean isChunkTracked(ServerPlayer player, int x, int z) { -- return player.getChunkTrackingView().contains(x, z) && !player.connection.chunkSender.isPending(ChunkPos.asLong(x, z)); -+ return ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getPlayerChunkLoader().isChunkSent(player, x, z); // Paper - rewrite chunk system + public boolean isChunkTracked(final ServerPlayer player, final int chunkX, final int chunkZ) { +- return player.getChunkTrackingView().contains(chunkX, chunkZ) && !player.connection.chunkSender.isPending(ChunkPos.pack(chunkX, chunkZ)); ++ return ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getPlayerChunkLoader().isChunkSent(player, chunkX, chunkZ); // Paper - rewrite chunk system } - private boolean isChunkOnTrackedBorder(ServerPlayer player, int x, int z) { -- if (!this.isChunkTracked(player, x, z)) { + private boolean isChunkOnTrackedBorder(final ServerPlayer player, final int chunkX, final int chunkZ) { +- if (!this.isChunkTracked(player, chunkX, chunkZ)) { - return false; - } else { -- for (int i = -1; i <= 1; i++) { -- for (int i1 = -1; i1 <= 1; i1++) { -- if ((i != 0 || i1 != 0) && !this.isChunkTracked(player, x + i, z + i1)) { +- for (int dx = -1; dx <= 1; dx++) { +- for (int dz = -1; dz <= 1; dz++) { +- if ((dx != 0 || dz != 0) && !this.isChunkTracked(player, chunkX + dx, chunkZ + dz)) { - return true; - } - } @@ -23958,139 +24705,139 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 - - return false; - } -+ return ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getPlayerChunkLoader().isChunkSent(player, x, z, true); // Paper - rewrite chunk system ++ return ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getPlayerChunkLoader().isChunkSent(player, chunkX, chunkZ, true); // Paper - rewrite chunk system } protected ThreadedLevelLightEngine getLightEngine() { -@@ -291,11 +278,17 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP +@@ -288,11 +269,17 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } - public @Nullable ChunkHolder getUpdatingChunkIfPresent(long chunkPos) { -- return this.updatingChunkMap.get(chunkPos); + public @Nullable ChunkHolder getUpdatingChunkIfPresent(final long key) { +- return this.updatingChunkMap.get(key); + // Paper start - rewrite chunk system -+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder holder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos); ++ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder holder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(key); + return holder == null ? null : holder.vanillaChunkHolder; + // Paper end - rewrite chunk system } - public @Nullable ChunkHolder getVisibleChunkIfPresent(long chunkPos) { -- return this.visibleChunkMap.get(chunkPos); + public @Nullable ChunkHolder getVisibleChunkIfPresent(final long key) { +- return this.visibleChunkMap.get(key); + // Paper start - rewrite chunk system -+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder holder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos); ++ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder holder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(key); + return holder == null ? null : holder.vanillaChunkHolder; + // Paper end - rewrite chunk system } - public @Nullable ChunkStatus getLatestStatus(long chunkPos) { -@@ -304,12 +297,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + public @Nullable ChunkStatus getLatestStatus(final long key) { +@@ -301,12 +288,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } - protected IntSupplier getChunkQueueLevel(long chunkPos) { + protected IntSupplier getChunkQueueLevel(final long pos) { - return () -> { -- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(chunkPos); -- return visibleChunkIfPresent == null +- ChunkHolder chunk = this.getVisibleChunkIfPresent(pos); +- return chunk == null - ? ChunkTaskPriorityQueue.PRIORITY_LEVEL_COUNT - 1 -- : Math.min(visibleChunkIfPresent.getQueueLevel(), ChunkTaskPriorityQueue.PRIORITY_LEVEL_COUNT - 1); +- : Math.min(chunk.getQueueLevel(), ChunkTaskPriorityQueue.PRIORITY_LEVEL_COUNT - 1); - }; + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public String getChunkDebugData(ChunkPos pos) { -@@ -335,47 +323,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP - } - - CompletableFuture>> getChunkRangeFuture(ChunkHolder chunkHolder, int range, IntFunction statusGetter) { + public String getChunkDebugData(final ChunkPos pos) { +@@ -334,47 +316,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + CompletableFuture>> getChunkRangeFuture( + final ChunkHolder centerChunk, final int range, final IntFunction distanceToStatus + ) { - if (range == 0) { -- ChunkStatus chunkStatus = statusGetter.apply(0); -- return chunkHolder.scheduleChunkGenerationTask(chunkStatus, this).thenApply(chunkResult -> chunkResult.map(List::of)); +- ChunkStatus status = distanceToStatus.apply(0); +- return centerChunk.scheduleChunkGenerationTask(status, this).thenApply(r -> r.map(List::of)); - } else { -- int squared = Mth.square(range * 2 + 1); -- List>> list = new ArrayList<>(squared); -- ChunkPos pos = chunkHolder.getPos(); +- int chunkCount = Mth.square(range * 2 + 1); +- List>> deps = new ArrayList<>(chunkCount); +- ChunkPos centerPos = centerChunk.getPos(); - -- for (int i = -range; i <= range; i++) { -- for (int i1 = -range; i1 <= range; i1++) { -- int max = Math.max(Math.abs(i1), Math.abs(i)); -- long packedChunkPos = ChunkPos.asLong(pos.x + i1, pos.z + i); -- ChunkHolder updatingChunkIfPresent = this.getUpdatingChunkIfPresent(packedChunkPos); -- if (updatingChunkIfPresent == null) { +- for (int z = -range; z <= range; z++) { +- for (int x = -range; x <= range; x++) { +- int distance = Math.max(Math.abs(x), Math.abs(z)); +- long chunkNode = ChunkPos.pack(centerPos.x() + x, centerPos.z() + z); +- ChunkHolder chunk = this.getUpdatingChunkIfPresent(chunkNode); +- if (chunk == null) { - return UNLOADED_CHUNK_LIST_FUTURE; - } - -- ChunkStatus chunkStatus1 = statusGetter.apply(max); -- list.add(updatingChunkIfPresent.scheduleChunkGenerationTask(chunkStatus1, this)); +- ChunkStatus depStatus = distanceToStatus.apply(distance); +- deps.add(chunk.scheduleChunkGenerationTask(depStatus, this)); - } - } - -- return Util.sequence(list).thenApply(list1 -> { -- List list2 = new ArrayList<>(list1.size()); +- return Util.sequence(deps).thenApply(chunkResults -> { +- List chunks = new ArrayList<>(chunkResults.size()); - -- for (ChunkResult chunkResult : list1) { +- for (ChunkResult chunkResult : chunkResults) { - if (chunkResult == null) { - throw this.debugFuturesAndCreateReportedException(new IllegalStateException("At least one of the chunk futures were null"), "n/a"); - } - -- ChunkAccess chunkAccess = chunkResult.orElse(null); -- if (chunkAccess == null) { +- ChunkAccess chunkx = chunkResult.orElse(null); +- if (chunkx == null) { - return UNLOADED_CHUNK_LIST_RESULT; - } - -- list2.add(chunkAccess); +- chunks.add(chunkx); - } - -- return ChunkResult.of(list2); +- return ChunkResult.of(chunks); - }); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public ReportedException debugFuturesAndCreateReportedException(IllegalStateException exception, String details) { -@@ -407,92 +355,28 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + public ReportedException debugFuturesAndCreateReportedException(final IllegalStateException exception, final String details) { +@@ -398,92 +340,28 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } - public CompletableFuture> prepareEntityTickingChunk(ChunkHolder chunk) { -- return this.getChunkRangeFuture(chunk, 2, i -> ChunkStatus.FULL) + public CompletableFuture> prepareEntityTickingChunk(final ChunkHolder chunk) { +- return this.getChunkRangeFuture(chunk, 2, distance -> ChunkStatus.FULL) - .thenApply(chunkResult -> chunkResult.map(list -> (LevelChunk)list.get(list.size() / 2))); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - @Nullable ChunkHolder updateChunkScheduling(long chunkPos, int newLevel, @Nullable ChunkHolder holder, int oldLevel) { -- if (!ChunkLevel.isLoaded(oldLevel) && !ChunkLevel.isLoaded(newLevel)) { -- return holder; + private @Nullable ChunkHolder updateChunkScheduling(final long node, final int level, @Nullable ChunkHolder chunk, final int oldLevel) { +- if (!ChunkLevel.isLoaded(oldLevel) && !ChunkLevel.isLoaded(level)) { +- return chunk; - } else { -- if (holder != null) { -- holder.setTicketLevel(newLevel); +- if (chunk != null) { +- chunk.setTicketLevel(level); - } - -- if (holder != null) { -- if (!ChunkLevel.isLoaded(newLevel)) { -- this.toDrop.add(chunkPos); +- if (chunk != null) { +- if (!ChunkLevel.isLoaded(level)) { +- this.toDrop.add(node); - } else { -- this.toDrop.remove(chunkPos); +- this.toDrop.remove(node); - } - } - -- if (ChunkLevel.isLoaded(newLevel) && holder == null) { -- holder = this.pendingUnloads.remove(chunkPos); -- if (holder != null) { -- holder.setTicketLevel(newLevel); +- if (ChunkLevel.isLoaded(level) && chunk == null) { +- chunk = this.pendingUnloads.remove(node); +- if (chunk != null) { +- chunk.setTicketLevel(level); - } else { -- holder = new ChunkHolder(new ChunkPos(chunkPos), newLevel, this.level, this.lightEngine, this::onLevelChange, this); -- ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkHolderCreate(this.level, holder); // Paper +- chunk = new ChunkHolder(ChunkPos.unpack(node), level, this.level, this.lightEngine, this::onLevelChange, this); +- ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkHolderCreate(this.level, chunk); // Paper - } - -- this.updatingChunkMap.put(chunkPos, holder); +- this.updatingChunkMap.put(node, chunk); - this.modified = true; - } - -- return holder; +- return chunk; - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void onLevelChange(ChunkPos chunkPos, IntSupplier queueLevelGetter, int ticketLevel, IntConsumer queueLevelSetter) { -- this.worldgenTaskDispatcher.onLevelChange(chunkPos, queueLevelGetter, ticketLevel, queueLevelSetter); -- this.lightTaskDispatcher.onLevelChange(chunkPos, queueLevelGetter, ticketLevel, queueLevelSetter); + private void onLevelChange(final ChunkPos pos, final IntSupplier oldLevel, final int newLevel, final IntConsumer setQueueLevel) { +- this.worldgenTaskDispatcher.onLevelChange(pos, oldLevel, newLevel, setQueueLevel); +- this.lightTaskDispatcher.onLevelChange(pos, oldLevel, newLevel, setQueueLevel); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @@ -24106,48 +24853,48 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + throw new UnsupportedOperationException("Use ServerChunkCache#close"); // Paper - rewrite chunk system } - protected void saveAllChunks(boolean flush) { -- if (flush) { -- List list = ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.level) // Paper - moonrise + protected void saveAllChunks(final boolean flushStorage) { +- if (flushStorage) { +- List chunksToSave = ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.level) // Paper - moonrise - //.values() // Paper - moonrise - .stream() - .filter(ChunkHolder::wasAccessibleSinceLastSave) - .peek(ChunkHolder::refreshAccessibility) - .toList(); -- MutableBoolean mutableBoolean = new MutableBoolean(); +- MutableBoolean didWork = new MutableBoolean(); - - do { -- mutableBoolean.setFalse(); -- list.stream() -- .map(chunk -> { -- this.mainThreadExecutor.managedBlock(chunk::isReadyForSaving); -- return chunk.getLatestChunk(); +- didWork.setFalse(); +- chunksToSave.stream() +- .map(chunkx -> { +- this.mainThreadExecutor.managedBlock(chunkx::isReadyForSaving); +- return chunkx.getLatestChunk(); - }) -- .filter(chunk -> chunk instanceof ImposterProtoChunk || chunk instanceof LevelChunk) +- .filter(chunkAccess -> chunkAccess instanceof ImposterProtoChunk || chunkAccess instanceof LevelChunk) - .filter(this::save) -- .forEach(chunk -> mutableBoolean.setTrue()); -- } while (mutableBoolean.isTrue()); +- .forEach(c -> didWork.setTrue()); +- } while (didWork.isTrue()); - - this.poiManager.flushAll(); - this.processUnloads(() -> true); - this.synchronize(true).join(); - } else { - this.nextChunkSaveTime.clear(); -- long millis = Util.getMillis(); +- long now = Util.getMillis(); - -- for (ChunkHolder chunkHolder : ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.level)) { // Paper -- this.saveChunkIfNeeded(chunkHolder, millis); +- for (ChunkHolder chunk : ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.level)) { // Paper +- this.saveChunkIfNeeded(chunk, now); - } - } + // Paper start - rewrite chunk system + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.saveAllChunks( -+ flush, false, false, false ++ flushStorage, false, false, false + ); + // Paper end - rewrite chunk system } - protected void tick(BooleanSupplier hasMoreTime) { -@@ -508,129 +392,28 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + protected void tick(final BooleanSupplier haveTime) { +@@ -499,129 +377,28 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } public boolean hasWork() { @@ -24164,82 +24911,82 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void processUnloads(BooleanSupplier hasMoreTime) { -- for (LongIterator longIterator = this.toDrop.iterator(); longIterator.hasNext(); longIterator.remove()) { -- long l = longIterator.nextLong(); -- ChunkHolder chunkHolder = this.updatingChunkMap.get(l); + private void processUnloads(final BooleanSupplier haveTime) { +- for (LongIterator iterator = this.toDrop.iterator(); iterator.hasNext(); iterator.remove()) { +- long pos = iterator.nextLong(); +- ChunkHolder chunkHolder = this.updatingChunkMap.get(pos); - if (chunkHolder != null) { -- this.updatingChunkMap.remove(l); -- this.pendingUnloads.put(l, chunkHolder); +- this.updatingChunkMap.remove(pos); +- this.pendingUnloads.put(pos, chunkHolder); - this.modified = true; -- this.scheduleUnload(l, chunkHolder); +- this.scheduleUnload(pos, chunkHolder); - } - } - -- int max = Math.max(0, this.unloadQueue.size() - 2000); +- int minimalNumberOfChunksToProcess = Math.max(0, this.unloadQueue.size() - 2000); - -- Runnable runnable; -- while ((max > 0 || hasMoreTime.getAsBoolean()) && (runnable = this.unloadQueue.poll()) != null) { -- max--; -- runnable.run(); +- Runnable unloadTask; +- while ((minimalNumberOfChunksToProcess > 0 || haveTime.getAsBoolean()) && (unloadTask = this.unloadQueue.poll()) != null) { +- minimalNumberOfChunksToProcess--; +- unloadTask.run(); - } - -- this.saveChunksEagerly(hasMoreTime); +- this.saveChunksEagerly(haveTime); + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.processUnloads(); // Paper - rewrite chunk system + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.autoSave(); // Paper - rewrite chunk system } - private void saveChunksEagerly(BooleanSupplier hasMoreTime) { -- long millis = Util.getMillis(); -- int i = 0; -- LongIterator longIterator = this.chunksToEagerlySave.iterator(); + private void saveChunksEagerly(final BooleanSupplier haveTime) { +- long now = Util.getMillis(); +- int eagerlySavedCount = 0; +- LongIterator iterator = this.chunksToEagerlySave.iterator(); - -- while (i < 20 && this.activeChunkWrites.get() < 128 && hasMoreTime.getAsBoolean() && longIterator.hasNext()) { -- long l = longIterator.nextLong(); -- ChunkHolder chunkHolder = this.visibleChunkMap.get(l); -- ChunkAccess chunkAccess = chunkHolder != null ? chunkHolder.getLatestChunk() : null; -- if (chunkAccess == null || !chunkAccess.isUnsaved()) { -- longIterator.remove(); -- } else if (this.saveChunkIfNeeded(chunkHolder, millis)) { -- i++; -- longIterator.remove(); +- while (eagerlySavedCount < 20 && this.activeChunkWrites.get() < 128 && haveTime.getAsBoolean() && iterator.hasNext()) { +- long chunkPos = iterator.nextLong(); +- ChunkHolder chunkHolder = this.visibleChunkMap.get(chunkPos); +- ChunkAccess latestChunk = chunkHolder != null ? chunkHolder.getLatestChunk() : null; +- if (latestChunk == null || !latestChunk.isUnsaved()) { +- iterator.remove(); +- } else if (this.saveChunkIfNeeded(chunkHolder, now)) { +- eagerlySavedCount++; +- iterator.remove(); - } - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void scheduleUnload(long chunkPos, ChunkHolder chunkHolder) { + private void scheduleUnload(final long pos, final ChunkHolder chunkHolder) { - CompletableFuture saveSyncFuture = chunkHolder.getSaveSyncFuture(); - saveSyncFuture.thenRunAsync(() -> { -- CompletableFuture saveSyncFuture1 = chunkHolder.getSaveSyncFuture(); -- if (saveSyncFuture1 != saveSyncFuture) { -- this.scheduleUnload(chunkPos, chunkHolder); +- CompletableFuture currentFuture = chunkHolder.getSaveSyncFuture(); +- if (currentFuture != saveSyncFuture) { +- this.scheduleUnload(pos, chunkHolder); - } else { -- ChunkAccess latestChunk = chunkHolder.getLatestChunk(); +- ChunkAccess chunk = chunkHolder.getLatestChunk(); - // Paper start - boolean removed; -- if ((removed = this.pendingUnloads.remove(chunkPos, chunkHolder)) && latestChunk != null) { +- if ((removed = this.pendingUnloads.remove(pos, chunkHolder)) && chunk != null) { - ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkHolderDelete(this.level, chunkHolder); - // Paper end -- if (latestChunk instanceof LevelChunk levelChunk) { +- if (chunk instanceof LevelChunk levelChunk) { - levelChunk.setLoaded(false); - } - -- this.save(latestChunk); -- if (latestChunk instanceof LevelChunk levelChunk) { +- this.save(chunk); +- if (chunk instanceof LevelChunk levelChunk) { - this.level.unload(levelChunk); - } - -- this.lightEngine.updateChunkStatus(latestChunk.getPos()); +- this.lightEngine.updateChunkStatus(chunk.getPos()); - this.lightEngine.tryScheduleUpdate(); -- this.nextChunkSaveTime.remove(latestChunk.getPos().toLong()); +- this.nextChunkSaveTime.remove(chunk.getPos().pack()); - } else if (removed) { // Paper start - ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkHolderDelete(this.level, chunkHolder); - } // Paper end - } -- }, this.unloadQueue::add).whenComplete((_void, error) -> { -- if (error != null) { -- LOGGER.error("Failed to save chunk {}", chunkHolder.getPos(), error); +- }, this.unloadQueue::add).whenComplete((ignored, throwable) -> { +- if (throwable != null) { +- LOGGER.error("Failed to save chunk {}", chunkHolder.getPos(), throwable); - } - }); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system @@ -24256,97 +25003,96 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private CompletableFuture scheduleChunkLoad(ChunkPos chunkPos) { -- CompletableFuture> completableFuture = this.readChunk(chunkPos).thenApplyAsync(optional -> optional.map(tag -> { -- SerializableChunkData serializableChunkData = SerializableChunkData.parse(this.level, this.level.palettedContainerFactory(), tag); -- if (serializableChunkData == null) { -- LOGGER.error("Chunk file at {} is missing level data, skipping", chunkPos); + private CompletableFuture scheduleChunkLoad(final ChunkPos pos) { +- CompletableFuture> chunkDataFuture = this.readChunk(pos).thenApplyAsync(chunkData -> chunkData.map(tag -> { +- SerializableChunkData parsedData = SerializableChunkData.parse(this.level, this.level.palettedContainerFactory(), tag); +- if (parsedData == null) { +- LOGGER.error("Chunk file at {} is missing level data, skipping", pos); - } - -- return serializableChunkData; +- return parsedData; - }), Util.backgroundExecutor().forName("parseChunk")); -- CompletableFuture completableFuture1 = this.poiManager.prefetch(chunkPos); -- return completableFuture.>thenCombine( -- (CompletionStage)completableFuture1, (optional, object) -> optional +- CompletableFuture poiFuture = this.poiManager.prefetch(pos); +- return chunkDataFuture.>thenCombine( +- (CompletionStage)poiFuture, (chunkData, ignored) -> chunkData - ) -- .thenApplyAsync(optional -> { +- .thenApplyAsync(chunkData -> { - Profiler.get().incrementCounter("chunkLoad"); -- if (optional.isPresent()) { -- ChunkAccess chunkAccess = optional.get().read(this.level, this.poiManager, this.storageInfo(), chunkPos); -- this.markPosition(chunkPos, chunkAccess.getPersistedStatus().getChunkType()); -- return chunkAccess; +- if (chunkData.isPresent()) { +- ChunkAccess chunk = chunkData.get().read(this.level, this.poiManager, this.storageInfo(), pos); +- this.markPosition(pos, chunk.getPersistedStatus().getChunkType()); +- return chunk; - } else { -- return this.createEmptyChunk(chunkPos); +- return this.createEmptyChunk(pos); - } - }, this.mainThreadExecutor) -- .exceptionallyAsync(throwable -> this.handleChunkLoadFailure(throwable, chunkPos), this.mainThreadExecutor); +- .exceptionallyAsync(throwable -> this.handleChunkLoadFailure(throwable, pos), this.mainThreadExecutor); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private ChunkAccess handleChunkLoadFailure(Throwable exception, ChunkPos chunkPos) { -@@ -668,230 +451,137 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + private ChunkAccess handleChunkLoadFailure(final Throwable throwable, final ChunkPos pos) { +@@ -659,229 +436,139 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP @Override - public GenerationChunkHolder acquireGeneration(long chunkPos) { -- ChunkHolder chunkHolder = this.updatingChunkMap.get(chunkPos); + public GenerationChunkHolder acquireGeneration(final long chunkNode) { +- ChunkHolder chunkHolder = this.updatingChunkMap.get(chunkNode); - chunkHolder.increaseGenerationRefCount(); - return chunkHolder; + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @Override - public void releaseGeneration(GenerationChunkHolder chunk) { -- chunk.decreaseGenerationRefCount(); + public void releaseGeneration(final GenerationChunkHolder chunkHolder) { +- chunkHolder.decreaseGenerationRefCount(); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @Override - public CompletableFuture applyStep(GenerationChunkHolder chunk, ChunkStep step, StaticCache2D cache) { -- ChunkPos pos = chunk.getPos(); + public CompletableFuture applyStep( + final GenerationChunkHolder chunkHolder, final ChunkStep step, final StaticCache2D cache + ) { +- ChunkPos pos = chunkHolder.getPos(); - if (step.targetStatus() == ChunkStatus.EMPTY) { - return this.scheduleChunkLoad(pos); - } else { - try { -- GenerationChunkHolder generationChunkHolder = cache.get(pos.x, pos.z); -- ChunkAccess chunkIfPresentUnchecked = generationChunkHolder.getChunkIfPresentUnchecked(step.targetStatus().getParent()); -- if (chunkIfPresentUnchecked == null) { +- GenerationChunkHolder holder = cache.get(pos.x(), pos.z()); +- ChunkAccess centerChunk = holder.getChunkIfPresentUnchecked(step.targetStatus().getParent()); +- if (centerChunk == null) { - throw new IllegalStateException("Parent chunk missing"); - } else { -- return step.apply(this.worldGenContext, cache, chunkIfPresentUnchecked); +- return step.apply(this.worldGenContext, cache, centerChunk); - } - } catch (Exception var8) { - var8.getStackTrace(); -- CrashReport crashReport = CrashReport.forThrowable(var8, "Exception generating new chunk"); -- CrashReportCategory crashReportCategory = crashReport.addCategory("Chunk to be generated"); -- crashReportCategory.setDetail("Status being generated", () -> step.targetStatus().getName()); -- crashReportCategory.setDetail("Location", String.format(Locale.ROOT, "%d,%d", pos.x, pos.z)); -- crashReportCategory.setDetail("Position hash", ChunkPos.asLong(pos.x, pos.z)); -- crashReportCategory.setDetail("Generator", this.generator()); -- this.mainThreadExecutor.execute(() -> { -- throw new ReportedException(crashReport); -- }); -- throw new ReportedException(crashReport); +- CrashReport report = CrashReport.forThrowable(var8, "Exception generating new chunk"); +- CrashReportCategory category = report.addCategory("Chunk to be generated"); +- category.setDetail("Status being generated", () -> step.targetStatus().getName()); +- category.setDetail("Location", String.format(Locale.ROOT, "%d,%d", pos.x(), pos.z())); +- category.setDetail("Position hash", ChunkPos.pack(pos.x(), pos.z())); +- category.setDetail("Generator", this.generator()); +- throw new ReportedException(report); - } - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @Override - public ChunkGenerationTask scheduleGenerationTask(ChunkStatus targetStatus, ChunkPos pos) { -- ChunkGenerationTask chunkGenerationTask = ChunkGenerationTask.create(this, targetStatus, pos); -- this.pendingGenerationTasks.add(chunkGenerationTask); -- return chunkGenerationTask; + public ChunkGenerationTask scheduleGenerationTask(final ChunkStatus targetStatus, final ChunkPos pos) { +- ChunkGenerationTask task = ChunkGenerationTask.create(this, targetStatus, pos); +- this.pendingGenerationTasks.add(task); +- return task; + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void runGenerationTask(ChunkGenerationTask task) { -- GenerationChunkHolder center = task.getCenter(); + private void runGenerationTask(final ChunkGenerationTask task) { +- GenerationChunkHolder chunk = task.getCenter(); - this.worldgenTaskDispatcher.submit(() -> { -- CompletableFuture completableFuture = task.runUntilWait(); -- if (completableFuture != null) { -- completableFuture.thenRun(() -> this.runGenerationTask(task)); +- CompletableFuture future = task.runUntilWait(); +- if (future != null) { +- future.thenRun(() -> this.runGenerationTask(task)); - } -- }, center.getPos().toLong(), center::getQueueLevel); +- }, chunk.getPos().pack(), chunk::getQueueLevel); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @@ -24357,17 +25103,17 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public CompletableFuture> prepareTickingChunk(ChunkHolder holder) { -- CompletableFuture>> chunkRangeFuture = this.getChunkRangeFuture(holder, 1, i -> ChunkStatus.FULL); -- return chunkRangeFuture.thenApplyAsync(chunk -> chunk.map(list -> { + public CompletableFuture> prepareTickingChunk(final ChunkHolder chunk) { +- CompletableFuture>> future = this.getChunkRangeFuture(chunk, 1, distance -> ChunkStatus.FULL); +- return future.thenApplyAsync(listResult -> listResult.map(list -> { - LevelChunk levelChunk = (LevelChunk)list.get(list.size() / 2); - levelChunk.postProcessGeneration(this.level); - this.level.startTickingChunk(levelChunk); -- CompletableFuture sendSyncFuture = holder.getSendSyncFuture(); +- CompletableFuture sendSyncFuture = chunk.getSendSyncFuture(); - if (sendSyncFuture.isDone()) { -- this.onChunkReadyToSend(holder, levelChunk); +- this.onChunkReadyToSend(chunk, levelChunk); - } else { -- sendSyncFuture.thenAcceptAsync(object -> this.onChunkReadyToSend(holder, levelChunk), this.mainThreadExecutor); +- sendSyncFuture.thenAcceptAsync(ignored -> this.onChunkReadyToSend(chunk, levelChunk), this.mainThreadExecutor); - } - - return levelChunk; @@ -24375,12 +25121,12 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void onChunkReadyToSend(ChunkHolder chunkHolder, LevelChunk chunk) { -- ChunkPos pos = chunk.getPos(); + private void onChunkReadyToSend(final ChunkHolder chunkHolder, final LevelChunk chunk) { +- ChunkPos chunkPos = chunk.getPos(); - -- for (ServerPlayer serverPlayer : this.playerMap.getAllPlayers()) { -- if (serverPlayer.getChunkTrackingView().contains(pos)) { -- markChunkPendingToSend(serverPlayer, chunk); +- for (ServerPlayer player : this.playerMap.getAllPlayers()) { +- if (player.getChunkTrackingView().contains(chunkPos)) { +- markChunkPendingToSend(player, chunk); - } - } - @@ -24389,15 +25135,15 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public CompletableFuture> prepareAccessibleChunk(ChunkHolder chunk) { + public CompletableFuture> prepareAccessibleChunk(final ChunkHolder chunk) { - return this.getChunkRangeFuture(chunk, 1, ChunkLevel::getStatusAroundFullChunk) - .thenApply(chunkResult -> chunkResult.map(list -> (LevelChunk)list.get(list.size() / 2))); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - Stream allChunksWithAtLeastStatus(ChunkStatus status) { -- int i = ChunkLevel.byStatus(status); -- return this.visibleChunkMap.values().stream().filter(chunkHolder -> chunkHolder.getTicketLevel() <= i); + Stream allChunksWithAtLeastStatus(final ChunkStatus status) { +- int level = ChunkLevel.byStatus(status); +- return this.visibleChunkMap.values().stream().filter(chunk -> chunk.getTicketLevel() <= level); + // Paper start - rewrite chunk system + final int i = ChunkLevel.byStatus(status); + return ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager @@ -24407,26 +25153,26 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + // Paper end - rewrite chunk system } - private boolean saveChunkIfNeeded(ChunkHolder chunk, long gameTime) { + private boolean saveChunkIfNeeded(final ChunkHolder chunk, final long now) { - if (chunk.wasAccessibleSinceLastSave() && chunk.isReadyForSaving()) { -- ChunkAccess latestChunk = chunk.getLatestChunk(); -- if (!(latestChunk instanceof ImposterProtoChunk) && !(latestChunk instanceof LevelChunk)) { +- ChunkAccess chunkAccess = chunk.getLatestChunk(); +- if (!(chunkAccess instanceof ImposterProtoChunk) && !(chunkAccess instanceof LevelChunk)) { - return false; -- } else if (!latestChunk.isUnsaved()) { +- } else if (!chunkAccess.isUnsaved()) { - return false; - } else { -- long packedChunkPos = latestChunk.getPos().toLong(); -- long orDefault = this.nextChunkSaveTime.getOrDefault(packedChunkPos, -1L); -- if (gameTime < orDefault) { +- long chunkPos = chunkAccess.getPos().pack(); +- long nextSaveTime = this.nextChunkSaveTime.getOrDefault(chunkPos, -1L); +- if (now < nextSaveTime) { - return false; - } else { -- boolean flag = this.save(latestChunk); +- boolean saved = this.save(chunkAccess); - chunk.refreshAccessibility(); -- if (flag) { -- this.nextChunkSaveTime.put(packedChunkPos, gameTime + 10000L); +- if (saved) { +- this.nextChunkSaveTime.put(chunkPos, now + 10000L); - } - -- return flag; +- return saved; - } - } - } else { @@ -24435,7 +25181,7 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public boolean save(ChunkAccess chunk) { + public boolean save(final ChunkAccess chunk) { - this.poiManager.flush(chunk.getPos()); - if (!chunk.tryMarkSaved()) { - return false; @@ -24443,30 +25189,30 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 - ChunkPos pos = chunk.getPos(); - - try { -- ChunkStatus persistedStatus = chunk.getPersistedStatus(); -- if (persistedStatus.getChunkType() != ChunkType.LEVELCHUNK) { +- ChunkStatus status = chunk.getPersistedStatus(); +- if (status.getChunkType() != ChunkType.LEVELCHUNK) { - if (this.isExistingChunkFull(pos)) { - return false; - } - -- if (persistedStatus == ChunkStatus.EMPTY && chunk.getAllStarts().values().stream().noneMatch(StructureStart::isValid)) { +- if (status == ChunkStatus.EMPTY && chunk.getAllStarts().values().stream().noneMatch(StructureStart::isValid)) { - return false; - } - } - - Profiler.get().incrementCounter("chunkSave"); - this.activeChunkWrites.incrementAndGet(); -- SerializableChunkData serializableChunkData = SerializableChunkData.copyOf(this.level, chunk); -- CompletableFuture completableFuture = CompletableFuture.supplyAsync(serializableChunkData::write, Util.backgroundExecutor()); -- this.write(pos, completableFuture::join).handle((_void, exception1) -> { -- if (exception1 != null) { -- this.level.getServer().reportChunkSaveFailure(exception1, this.storageInfo(), pos); +- SerializableChunkData data = SerializableChunkData.copyOf(this.level, chunk); +- CompletableFuture encodedData = CompletableFuture.supplyAsync(data::write, Util.backgroundExecutor()); +- this.write(pos, encodedData::join).handle((ignored, throwable) -> { +- if (throwable != null) { +- this.level.getServer().reportChunkSaveFailure(throwable, this.storageInfo(), pos); - } - - this.activeChunkWrites.decrementAndGet(); - return null; - }); -- this.markPosition(pos, persistedStatus.getChunkType()); +- this.markPosition(pos, status.getChunkType()); - return true; - } catch (Exception var6) { - this.level.getServer().reportChunkSaveFailure(var6, this.storageInfo(), pos); @@ -24476,41 +25222,41 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private boolean isExistingChunkFull(ChunkPos chunkPos) { -- byte b = this.chunkTypeCache.get(chunkPos.toLong()); -- if (b != 0) { -- return b == 1; + private boolean isExistingChunkFull(final ChunkPos pos) { +- byte cachedChunkType = this.chunkTypeCache.get(pos.pack()); +- if (cachedChunkType != 0) { +- return cachedChunkType == 1; - } else { -- CompoundTag compoundTag; +- CompoundTag currentTag; - try { -- compoundTag = this.readChunk(chunkPos).join().orElse(null); -- if (compoundTag == null) { -- this.markPositionReplaceable(chunkPos); +- currentTag = this.readChunk(pos).join().orElse(null); +- if (currentTag == null) { +- this.markPositionReplaceable(pos); - return false; - } - } catch (Exception var5) { -- LOGGER.error("Failed to read chunk {}", chunkPos, var5); -- this.markPositionReplaceable(chunkPos); +- LOGGER.error("Failed to read chunk {}", pos, var5); +- this.markPositionReplaceable(pos); - return false; - } - -- ChunkType chunkType = SerializableChunkData.getChunkStatusFromTag(compoundTag).getChunkType(); -- return this.markPosition(chunkPos, chunkType) == 1; +- ChunkType chunkType = SerializableChunkData.getChunkStatusFromTag(currentTag).getChunkType(); +- return this.markPosition(pos, chunkType) == 1; - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public void setServerViewDistance(int viewDistance) { -- int i = Mth.clamp(viewDistance, 2, 32); -- if (i != this.serverViewDistance) { -- this.serverViewDistance = i; + public void setServerViewDistance(final int newViewDistance) { +- int actualNewDistance = Mth.clamp(newViewDistance, 2, 32); +- if (actualNewDistance != this.serverViewDistance) { +- this.serverViewDistance = actualNewDistance; - this.distanceManager.updatePlayerTickets(this.serverViewDistance); - -- for (ServerPlayer serverPlayer : this.playerMap.getAllPlayers()) { -- this.updateChunkTracking(serverPlayer); +- for (ServerPlayer player : this.playerMap.getAllPlayers()) { +- this.updateChunkTracking(player); - } + // Paper start - rewrite chunk system -+ final int clamped = Mth.clamp(viewDistance, 2, ca.spottedleaf.moonrise.common.util.MoonriseConstants.MAX_VIEW_DISTANCE); ++ final int clamped = Mth.clamp(newViewDistance, 2, ca.spottedleaf.moonrise.common.util.MoonriseConstants.MAX_VIEW_DISTANCE); + if (clamped == this.serverViewDistance) { + return; } @@ -24520,26 +25266,26 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + // Paper end - rewrite chunk system } - int getPlayerViewDistance(ServerPlayer player) { + private int getPlayerViewDistance(final ServerPlayer player) { - return Mth.clamp(player.requestedViewDistance(), 2, this.serverViewDistance); + return ca.spottedleaf.moonrise.common.PlatformHooks.get().getSendViewDistance(player); // Paper - rewrite chunk system } - private void markChunkPendingToSend(ServerPlayer player, ChunkPos chunkPos) { -- LevelChunk chunkToSend = this.getChunkToSend(chunkPos.toLong()); -- if (chunkToSend != null) { -- markChunkPendingToSend(player, chunkToSend); + private void markChunkPendingToSend(final ServerPlayer player, final ChunkPos pos) { +- LevelChunk chunk = this.getChunkToSend(pos.pack()); +- if (chunk != null) { +- markChunkPendingToSend(player, chunk); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private static void markChunkPendingToSend(ServerPlayer player, LevelChunk chunk) { + private static void markChunkPendingToSend(final ServerPlayer player, final LevelChunk chunk) { - player.connection.chunkSender.markChunkPendingToSend(chunk); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private static void dropChunk(ServerPlayer player, ChunkPos chunkPos) { -- player.connection.chunkSender.dropChunk(player, chunkPos); + private static void dropChunk(final ServerPlayer player, final ChunkPos pos) { +- player.connection.chunkSender.dropChunk(player, pos); + // Paper - rewrite chunk system } @@ -24549,7 +25295,7 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + final CompletableFuture> ret = new CompletableFuture<>(); + + ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.loadDataAsync( -+ this.level, pos.x, pos.z, ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, ++ this.level, pos.x(), pos.z(), ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, + (final CompoundTag data, final Throwable thr) -> { + if (thr != null) { + ret.completeExceptionally(thr); @@ -24565,7 +25311,7 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + @Override + public CompletableFuture write(final ChunkPos pos, final Supplier tag) { + ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.scheduleSave( -+ this.level, pos.x, pos.z, tag.get(), ++ this.level, pos.x(), pos.z(), tag.get(), + ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionFileType.CHUNK_DATA + ); + return null; @@ -24585,20 +25331,20 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + } + // Paper end - rewrite chunk system + - public @Nullable LevelChunk getChunkToSend(long chunkPos) { - ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(chunkPos); - return visibleChunkIfPresent == null ? null : visibleChunkIfPresent.getChunkToSend(); -@@ -971,7 +661,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP - return this.read(pos).thenApplyAsync(optional -> optional.map(this::upgradeChunkTag), Util.backgroundExecutor().forName("upgradeChunk")); - } - -- private CompoundTag upgradeChunkTag(CompoundTag tag) { -+ public CompoundTag upgradeChunkTag(CompoundTag tag) { // Paper - rewrite chunk system - public - return this.upgradeChunkTag(tag, -1, getChunkDataFixContextTag(this.level.getTypeKey(), this.generator().getTypeNameForDataFixer()), this.level); // CraftBukkit + public @Nullable LevelChunk getChunkToSend(final long key) { + ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(key); + return chunkHolder == null ? null : chunkHolder.getChunkToSend(); +@@ -963,7 +650,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + return this.read(pos).thenApplyAsync(chunkTag -> chunkTag.map(this::upgradeChunkTag), Util.backgroundExecutor().forName("upgradeChunk")); } -@@ -982,23 +672,66 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP - return compoundTag; +- private CompoundTag upgradeChunkTag(final CompoundTag tag) { ++ public CompoundTag upgradeChunkTag(final CompoundTag tag) { // Paper - rewrite chunk system - public + return this.upgradeChunkTag( + tag, + -1, +@@ -979,23 +666,66 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + return contextTag; } + // Paper start - optimise chunk tick iteration @@ -24638,17 +25384,17 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + } + // Paper end - optimise chunk tick iteration + - void collectSpawningChunks(List output) { + void collectSpawningChunks(final List output) { - LongIterator spawnCandidateChunks = this.distanceManager.getSpawnCandidateChunks(); + // Paper start - optimise chunk tick iteration + final ca.spottedleaf.moonrise.common.list.ReferenceList tickingChunks = ((ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks(); - while (spawnCandidateChunks.hasNext()) { -- ChunkHolder chunkHolder = this.visibleChunkMap.get(spawnCandidateChunks.nextLong()); -- if (chunkHolder != null) { -- LevelChunk tickingChunk = chunkHolder.getTickingChunk(); -- if (tickingChunk != null && this.anyPlayerCloseEnoughForSpawningInternal(chunkHolder.getPos(), true)) { // Spigot -- output.add(tickingChunk); +- ChunkHolder holder = this.visibleChunkMap.get(spawnCandidateChunks.nextLong()); +- if (holder != null) { +- LevelChunk chunk = holder.getTickingChunk(); +- if (chunk != null && this.anyPlayerCloseEnoughForSpawningInternal(holder.getPos(), true)) { // Spigot +- output.add(chunk); - } + final LevelChunk[] raw = tickingChunks.getRawDataUnchecked(); + final int size = tickingChunks.size(); @@ -24666,32 +25412,32 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + // Paper end - optimise chunk tick iteration } - void forEachBlockTickingChunk(Consumer action) { + void forEachBlockTickingChunk(final Consumer tickingChunkConsumer) { this.distanceManager.forEachEntityTickingChunk(chunkPos -> { -- ChunkHolder chunkHolder = this.visibleChunkMap.get(chunkPos); -+ ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(chunkPos); // Paper - rewrite chunk system - if (chunkHolder != null) { - LevelChunk tickingChunk = chunkHolder.getTickingChunk(); - if (tickingChunk != null) { -@@ -1014,8 +747,8 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP +- ChunkHolder holder = this.visibleChunkMap.get(chunkPos); ++ ChunkHolder holder = this.getVisibleChunkIfPresent(chunkPos); // Paper - rewrite chunk system + if (holder != null) { + LevelChunk chunk = holder.getTickingChunk(); + if (chunk != null) { +@@ -1011,8 +741,8 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } - boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkPos, boolean reducedRange) { -- TriState triState = this.distanceManager.hasPlayersNearby(chunkPos.toLong()); -- return triState == TriState.DEFAULT ? this.anyPlayerCloseEnoughForSpawningInternal(chunkPos, reducedRange) : triState.toBoolean(true); + boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos, boolean reducedRange) { +- TriState triState = this.distanceManager.hasPlayersNearby(pos.pack()); +- return triState == TriState.DEFAULT ? this.anyPlayerCloseEnoughForSpawningInternal(pos, reducedRange) : triState.toBoolean(true); + // Paper - chunk tick iteration optimisation - cannot use narrow check due to custom range -+ return this.anyPlayerCloseEnoughForSpawningInternal(chunkPos, reducedRange); // Paper - chunk tick iteration optimisation ++ return this.anyPlayerCloseEnoughForSpawningInternal(pos, reducedRange); // Paper - chunk tick iteration optimisation // Spigot end } -@@ -1039,7 +772,20 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP - private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos chunkPos, boolean reducedRange) { +@@ -1036,7 +766,20 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + private boolean anyPlayerCloseEnoughForSpawningInternal(final ChunkPos pos, final boolean reducedRange) { double blockRange; // Paper - use from event // Spigot end -- for (ServerPlayer serverPlayer : this.playerMap.getAllPlayers()) { +- for (ServerPlayer player : this.playerMap.getAllPlayers()) { + // Paper start - chunk tick iteration optimisation + final ca.spottedleaf.moonrise.common.list.ReferenceList players = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers( -+ chunkPos, ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.SPAWN_RANGE ++ pos, ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.SPAWN_RANGE + ); + if (players == null) { + return false; @@ -24702,26 +25448,26 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + + Objects.checkFromIndexSize(0, len, raw.length); + for (int i = 0; i < len; ++i) { -+ final ServerPlayer serverPlayer = raw[i]; ++ final ServerPlayer player = raw[i]; // Paper start - PlayerNaturallySpawnCreaturesEvent com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event; - blockRange = 16384.0D; -@@ -1055,26 +801,41 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + blockRange = 16384.0; +@@ -1052,26 +795,41 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } return false; + // Paper end - chunk tick iteration optimisation } - public List getPlayersCloseForSpawning(ChunkPos chunkPos) { -- long packedChunkPos = chunkPos.toLong(); -- if (!this.distanceManager.hasPlayersNearby(packedChunkPos).toBoolean(true)) { + public List getPlayersCloseForSpawning(final ChunkPos pos) { +- long key = pos.pack(); +- if (!this.distanceManager.hasPlayersNearby(key).toBoolean(true)) { - return List.of(); - } else { - Builder builder = ImmutableList.builder(); + // Paper start - chunk tick iteration optimisation + final ca.spottedleaf.moonrise.common.list.ReferenceList players = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers( -+ chunkPos, ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.SPAWN_RANGE ++ pos, ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.SPAWN_RANGE + ); + if (players == null) { + return new ArrayList<>(); @@ -24732,13 +25478,13 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + final ServerPlayer[] raw = players.getRawDataUnchecked(); + final int len = players.size(); -- for (ServerPlayer serverPlayer : this.playerMap.getAllPlayers()) { -- if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos, 16384.0D)) { // Spigot -- builder.add(serverPlayer); +- for (ServerPlayer player : this.playerMap.getAllPlayers()) { +- if (this.playerIsCloseEnoughForSpawning(player, pos, 16384.0)) { // Spigot +- builder.add(player); + Objects.checkFromIndexSize(0, len, raw.length); + for (int i = 0; i < len; ++i) { + final ServerPlayer player = raw[i]; -+ if (this.playerIsCloseEnoughForSpawning(player, chunkPos, 16384.0D)) { // Spigot ++ if (this.playerIsCloseEnoughForSpawning(player, pos, 16384.0D)) { // Spigot + if (ret == null) { + ret = new ArrayList<>(len - i); + ret.add(player); @@ -24754,14 +25500,14 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + // Paper end - chunk tick iteration optimisation } -- private boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos chunkPos, double range) { // Spigot -+ public boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos chunkPos, double range) { // Spigot // Paper - chunk tick iteration optimisation - public +- private boolean playerIsCloseEnoughForSpawning(final ServerPlayer player, final ChunkPos pos, final double range) { // Spigot ++ public boolean playerIsCloseEnoughForSpawning(final ServerPlayer player, final ChunkPos pos, final double range) { // Spigot // Paper - chunk tick iteration optimisation - public if (player.isSpectator()) { return false; } else { -@@ -1112,18 +873,20 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP +@@ -1109,18 +867,20 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP this.updatePlayerPos(player); - if (!flag) { + if (!ignored) { this.distanceManager.addPlayer(SectionPos.of(player), player); + ((ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager)this.distanceManager).moonrise$addPlayer(player, SectionPos.of(player)); // Paper - chunk tick iteration optimisation } @@ -24770,10 +25516,10 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 - this.updateChunkTracking(player); + ca.spottedleaf.moonrise.common.PlatformHooks.get().addPlayerToDistanceMaps(this.level, player); // Paper - rewrite chunk system } else { - SectionPos lastSectionPos = player.getLastSectionPos(); + SectionPos lastPos = player.getLastSectionPos(); this.playerMap.removePlayer(player); - if (!flag1) { - this.distanceManager.removePlayer(lastSectionPos, player); + if (!wasIgnored) { + this.distanceManager.removePlayer(lastPos, player); + ((ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager)this.distanceManager).moonrise$removePlayer(player, SectionPos.of(player)); // Paper - chunk tick iteration optimisation } @@ -24782,10 +25528,10 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 } } -@@ -1133,13 +896,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP +@@ -1130,13 +890,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } - public void move(ServerPlayer player) { + public void move(final ServerPlayer player) { - for (ChunkMap.TrackedEntity trackedEntity : this.entityMap.values()) { - if (trackedEntity.entity == player) { - trackedEntity.updatePlayers(this.level.players()); @@ -24795,17 +25541,17 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 - } + // Paper - optimise entity tracker - SectionPos lastSectionPos = player.getLastSectionPos(); - SectionPos sectionPos = SectionPos.of(player); -@@ -1148,6 +905,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP - boolean flag2 = lastSectionPos.asLong() != sectionPos.asLong(); - if (flag2 || flag != flag1) { + SectionPos oldSection = player.getLastSectionPos(); + SectionPos newSection = SectionPos.of(player); +@@ -1145,6 +899,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + boolean positionChanged = oldSection.asLong() != newSection.asLong(); + if (positionChanged || wasIgnored != ignored) { this.updatePlayerPos(player); -+ ((ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager)this.distanceManager).moonrise$updatePlayer(player, lastSectionPos, sectionPos, flag, flag1); // Paper - chunk tick iteration optimisation - if (!flag) { - this.distanceManager.removePlayer(lastSectionPos, player); ++ ((ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager)this.distanceManager).moonrise$updatePlayer(player, oldSection, newSection, wasIgnored, ignored); // Paper - chunk tick iteration optimisation + if (!wasIgnored) { + this.distanceManager.removePlayer(oldSection, player); } -@@ -1164,49 +922,29 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP +@@ -1161,47 +916,29 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP this.playerMap.unIgnorePlayer(player); } @@ -24815,60 +25561,58 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + ca.spottedleaf.moonrise.common.PlatformHooks.get().updateMaps(this.level, player); // Paper - rewrite chunk system } - private void updateChunkTracking(ServerPlayer player) { + private void updateChunkTracking(final ServerPlayer player) { - ChunkPos chunkPos = player.chunkPosition(); - int playerViewDistance = this.getPlayerViewDistance(player); - if (!( -- player.getChunkTrackingView() instanceof ChunkTrackingView.Positioned positioned -- && positioned.center().equals(chunkPos) -- && positioned.viewDistance() == playerViewDistance +- player.getChunkTrackingView() instanceof ChunkTrackingView.Positioned view +- && view.center().equals(chunkPos) +- && view.viewDistance() == playerViewDistance - )) { - this.applyChunkTrackingView(player, ChunkTrackingView.of(chunkPos, playerViewDistance)); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void applyChunkTrackingView(ServerPlayer player, ChunkTrackingView chunkTrackingView) { + private void applyChunkTrackingView(final ServerPlayer player, final ChunkTrackingView next) { - if (player.level() == this.level) { -- ChunkTrackingView chunkTrackingView1 = player.getChunkTrackingView(); -- if (chunkTrackingView instanceof ChunkTrackingView.Positioned positioned -- && !(chunkTrackingView1 instanceof ChunkTrackingView.Positioned positioned1 && positioned1.center().equals(positioned.center()))) { -- player.connection.send(new ClientboundSetChunkCacheCenterPacket(positioned.center().x, positioned.center().z)); +- ChunkTrackingView previous = player.getChunkTrackingView(); +- if (next instanceof ChunkTrackingView.Positioned to +- && !(previous instanceof ChunkTrackingView.Positioned from && from.center().equals(to.center()))) { +- player.connection.send(new ClientboundSetChunkCacheCenterPacket(to.center().x(), to.center().z())); - } - -- ChunkTrackingView.difference( -- chunkTrackingView1, chunkTrackingView, chunkPos -> this.markChunkPendingToSend(player, chunkPos), chunkPos -> dropChunk(player, chunkPos) -- ); -- player.setChunkTrackingView(chunkTrackingView); +- ChunkTrackingView.difference(previous, next, pos -> this.markChunkPendingToSend(player, pos), pos -> dropChunk(player, pos)); +- player.setChunkTrackingView(next); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @Override - public List getPlayers(ChunkPos pos, boolean boundaryOnly) { + public List getPlayers(final ChunkPos pos, final boolean borderOnly) { - Set allPlayers = this.playerMap.getAllPlayers(); -- Builder builder = ImmutableList.builder(); +- Builder result = ImmutableList.builder(); - -- for (ServerPlayer serverPlayer : allPlayers) { -- if (boundaryOnly && this.isChunkOnTrackedBorder(serverPlayer, pos.x, pos.z) || !boundaryOnly && this.isChunkTracked(serverPlayer, pos.x, pos.z)) { -- builder.add(serverPlayer); +- for (ServerPlayer player : allPlayers) { +- if (borderOnly && this.isChunkOnTrackedBorder(player, pos.x(), pos.z()) || !borderOnly && this.isChunkTracked(player, pos.x(), pos.z())) { +- result.add(player); - } + // Paper start - rewrite chunk system -+ final ChunkHolder holder = this.getVisibleChunkIfPresent(pos.toLong()); ++ final ChunkHolder holder = this.getVisibleChunkIfPresent(pos.pack()); + if (holder == null) { + return new ArrayList<>(); + } else { -+ return ((ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder)holder).moonrise$getPlayers(boundaryOnly); ++ return ((ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder)holder).moonrise$getPlayers(borderOnly); } - -- return builder.build(); +- return result.build(); + // Paper end - rewrite chunk system } - public void addEntity(Entity entity) { -@@ -1230,6 +968,12 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + public void addEntity(final Entity entity) { +@@ -1225,6 +962,12 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } else { - ChunkMap.TrackedEntity trackedEntity = new ChunkMap.TrackedEntity(entity, i, updateInterval, type.trackDeltas()); + ChunkMap.TrackedEntity trackedEntity = new ChunkMap.TrackedEntity(entity, range, updateInterval, type.trackDeltas()); this.entityMap.put(entity.getId(), trackedEntity); + // Paper start - optimise entity tracker + if (((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity)entity).moonrise$getTrackedEntity() != null) { @@ -24877,11 +25621,11 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity)entity).moonrise$setTrackedEntity(trackedEntity); + // Paper end - optimise entity tracker trackedEntity.updatePlayers(this.level.players()); - if (entity instanceof ServerPlayer serverPlayer) { - this.updatePlayerStatus(serverPlayer, true); -@@ -1259,12 +1003,38 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP - if (trackedEntity1 != null) { - trackedEntity1.broadcastRemoved(); + if (entity instanceof ServerPlayer player) { + this.updatePlayerStatus(player, true); +@@ -1254,12 +997,38 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + if (trackedEntity != null) { + trackedEntity.broadcastRemoved(); } + ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity)entity).moonrise$setTrackedEntity(null); // Paper - optimise entity tracker } @@ -24908,8 +25652,8 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + // Paper end - optimise entity tracker + protected void tick() { -- for (ServerPlayer serverPlayer : this.playerMap.getAllPlayers()) { -- this.updateChunkTracking(serverPlayer); +- for (ServerPlayer player : this.playerMap.getAllPlayers()) { +- this.updateChunkTracking(player); + // Paper start - optimise entity tracker + if (true) { + this.newTrackerTick(); @@ -24918,36 +25662,37 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + // Paper end - optimise entity tracker + // Paper - rewrite chunk system - List list = Lists.newArrayList(); - List list1 = this.level.players(); -@@ -1362,17 +1132,11 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + List movedPlayers = Lists.newArrayList(); + List players = this.level.players(); +@@ -1359,17 +1128,11 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } - public void waitForLightBeforeSending(ChunkPos chunkPos, int range) { -- int i = range + 1; -- ChunkPos.rangeClosed(chunkPos, i).forEach(chunkPos1 -> { -- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(chunkPos1.toLong()); -- if (visibleChunkIfPresent != null) { -- visibleChunkIfPresent.addSendDependency(this.lightEngine.waitForPendingTasks(chunkPos1.x, chunkPos1.z)); + public void waitForLightBeforeSending(final ChunkPos centerChunk, final int chunkRadius) { +- int affectedLightChunkRadius = chunkRadius + 1; +- ChunkPos.rangeClosed(centerChunk, affectedLightChunkRadius).forEach(chunkPos -> { +- ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(chunkPos.pack()); +- if (chunkHolder != null) { +- chunkHolder.addSendDependency(this.lightEngine.waitForPendingTasks(chunkPos.x(), chunkPos.z())); - } - }); + // Paper - rewrite chunk system } - public void forEachReadyToSendChunk(Consumer action) { + public void forEachReadyToSendChunk(final Consumer consumer) { - for (ChunkHolder chunkHolder : this.visibleChunkMap.values()) { + for (ChunkHolder chunkHolder : ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getOldChunkHolders()) { // Paper - rewrite chunk system - LevelChunk chunkToSend = chunkHolder.getChunkToSend(); - if (chunkToSend != null) { - action.accept(chunkToSend); -@@ -1380,14 +1144,21 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + LevelChunk chunk = chunkHolder.getChunkToSend(); + if (chunk != null) { + consumer.accept(chunk); +@@ -1377,15 +1140,22 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } } - public class DistanceManager extends net.minecraft.server.level.DistanceManager { + public class DistanceManager extends net.minecraft.server.level.DistanceManager implements ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager { // Paper - rewrite chunk system - protected DistanceManager(final TicketStorage ticketStorage, final Executor dispatcher, final Executor mainThreadExecutor) { - super(ticketStorage, dispatcher, mainThreadExecutor); + protected DistanceManager(final TicketStorage ticketStorage, final Executor executor, final Executor mainThreadExecutor) { + Objects.requireNonNull(ChunkMap.this); + super(ticketStorage, executor, mainThreadExecutor); } + // Paper start - rewrite chunk system @@ -24958,23 +25703,23 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + // Paper end - rewrite chunk system + @Override - protected boolean isChunkToRemove(long chunkPos) { -- return ChunkMap.this.toDrop.contains(chunkPos); + protected boolean isChunkToRemove(final long node) { +- return ChunkMap.this.toDrop.contains(node); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @Override -@@ -1401,13 +1172,96 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP +@@ -1399,13 +1169,96 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } } - public class TrackedEntity implements ServerEntity.Synchronizer { + public class TrackedEntity implements ServerEntity.Synchronizer, ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity { // Paper - optimise entity tracker public final ServerEntity serverEntity; - final Entity entity; + private final Entity entity; private final int range; - SectionPos lastSectionPos; - public final Set seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl + private SectionPos lastSectionPos; + public final Set seenBy; + // Paper start - optimise entity tracker + private long lastChunkUpdate = -1L; @@ -25060,22 +25805,22 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + // Paper end - optimise entity tracker + public TrackedEntity(final Entity entity, final int range, final int updateInterval, final boolean trackDelta) { - this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, updateInterval, trackDelta, this, this.seenBy); // Paper - this.entity = entity; -@@ -1517,17 +1371,24 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP + Objects.requireNonNull(ChunkMap.this); + super(); +@@ -1520,17 +1373,24 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP } private int getEffectiveRange() { -- int i = this.range; +- int effectiveRange = this.range; + // Paper start - optimise entity tracker + final Entity entity = this.entity; + int range = this.range; -- for (Entity entity : this.entity.getIndirectPassengers()) { -- int i1 = entity.getType().clientTrackingRange() * 16; -- i1 = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i1); // Paper -- if (i1 > i) { -- i = i1; +- for (Entity passenger : this.entity.getIndirectPassengers()) { +- int passengerRange = passenger.getType().clientTrackingRange() * 16; +- passengerRange = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, passengerRange); // Paper +- if (passengerRange > effectiveRange) { +- effectiveRange = passengerRange; - } + if (entity.getPassengers() == ImmutableList.of()) { + return this.scaledRange(range); @@ -25089,25 +25834,25 @@ index b6d96f521929feddd2176fd93eef77f0af33e865..93845d083dc7fce80069bc279207ef43 + range = Math.max(range, ca.spottedleaf.moonrise.common.PlatformHooks.get().modifyEntityTrackingRange(passenger, passenger.getType().clientTrackingRange() << 4)); } -- return this.scaledRange(i); +- return this.scaledRange(effectiveRange); + return this.scaledRange(range); + // Paper end - optimise entity tracker } - public void updatePlayers(List playersList) { + public void updatePlayers(final List players) { diff --git a/net/minecraft/server/level/DistanceManager.java b/net/minecraft/server/level/DistanceManager.java -index d444c29800e103de55bfb99fd8671ee69b003419..8a5c37cf572abc6b9526648d3c8ec92b7af37e3d 100644 +index abc610594c3d37993df8762783baaae0c053a5cc..f1fa6c7169635724e202c10adafe7d70e14ac7de 100644 --- a/net/minecraft/server/level/DistanceManager.java +++ b/net/minecraft/server/level/DistanceManager.java -@@ -31,96 +31,79 @@ import net.minecraft.world.level.chunk.LevelChunk; +@@ -32,94 +32,79 @@ import net.minecraft.world.level.chunk.LevelChunk; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; -public abstract class DistanceManager { +public abstract class DistanceManager implements ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager, ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager { // Paper - rewrite chunk system // Paper - chunk tick iteration optimisation private static final Logger LOGGER = LogUtils.getLogger(); - static final int PLAYER_TICKET_LEVEL = ChunkLevel.byStatus(FullChunkStatus.ENTITY_TICKING); - final Long2ObjectMap> playersPerChunk = new Long2ObjectOpenHashMap<>(); + private static final int PLAYER_TICKET_LEVEL = ChunkLevel.byStatus(FullChunkStatus.ENTITY_TICKING); + private final Long2ObjectMap> playersPerChunk = new Long2ObjectOpenHashMap<>(); - private final LoadingChunkTracker loadingChunkTracker; - private final SimulationChunkTracker simulationChunkTracker; + // Paper - rewrite chunk system @@ -25115,27 +25860,27 @@ index d444c29800e103de55bfb99fd8671ee69b003419..8a5c37cf572abc6b9526648d3c8ec92b - private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8); - private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(32); - protected final Set chunksToUpdateFutures = new ReferenceOpenHashSet<>(); -- final ThrottlingChunkTaskDispatcher ticketDispatcher; -- final LongSet ticketsToRelease = new LongOpenHashSet(); -- final Executor mainThreadExecutor; +- private final ThrottlingChunkTaskDispatcher ticketDispatcher; +- private final LongSet ticketsToRelease = new LongOpenHashSet(); +- private final Executor mainThreadExecutor; - public int simulationDistance = 10; + // Paper - chunk tick iteration optimisation + // Paper - rewrite chunk system - protected DistanceManager(TicketStorage ticketStorage, Executor dispatcher, Executor mainThreadExecutor) { + protected DistanceManager(final TicketStorage ticketStorage, final Executor executor, final Executor mainThreadExecutor) { this.ticketStorage = ticketStorage; - this.loadingChunkTracker = new LoadingChunkTracker(this, ticketStorage); - this.simulationChunkTracker = new SimulationChunkTracker(ticketStorage); + // Paper - rewrite chunk system - TaskScheduler taskScheduler = TaskScheduler.wrapExecutor("player ticket throttler", mainThreadExecutor); -- this.ticketDispatcher = new ThrottlingChunkTaskDispatcher(taskScheduler, dispatcher, 4); + TaskScheduler mainThreadTaskScheduler = TaskScheduler.wrapExecutor("player ticket throttler", mainThreadExecutor); +- this.ticketDispatcher = new ThrottlingChunkTaskDispatcher(mainThreadTaskScheduler, executor, 4); - this.mainThreadExecutor = mainThreadExecutor; + this.ticketStorage.moonrise$setChunkMap(this.moonrise$getChunkMap()); // Paper - rewrite chunk system } -- protected abstract boolean isChunkToRemove(long chunkPos); +- protected abstract boolean isChunkToRemove(final long node); - -- protected abstract @Nullable ChunkHolder getChunk(long chunkPos); +- protected abstract @Nullable ChunkHolder getChunk(final long node); + // Paper start - rewrite chunk system + @Override + public final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager moonrise$getChunkHolderManager() { @@ -25152,21 +25897,21 @@ index d444c29800e103de55bfb99fd8671ee69b003419..8a5c37cf572abc6b9526648d3c8ec92b + // Note: Cannot do narrow tracking on Paper due to custom spawn range + } -- protected abstract @Nullable ChunkHolder updateChunkScheduling(long chunkPos, int newLevel, @Nullable ChunkHolder holder, int oldLevel); +- protected abstract @Nullable ChunkHolder updateChunkScheduling(final long node, final int level, final @Nullable ChunkHolder chunk, final int oldLevel); + @Override + public final void moonrise$removePlayer(final ServerPlayer player, final SectionPos pos) { + this.spawnChunkTracker.remove(player); + // Note: Cannot do narrow tracking on Paper due to custom spawn range + } -- public boolean runAllUpdates(ChunkMap chunkMap) { +- public boolean runAllUpdates(final ChunkMap scheduler) { - this.naturalSpawnChunkCounter.runAllUpdates(); - this.simulationChunkTracker.runAllUpdates(); - this.playerTicketManager.runAllUpdates(); -- int i = Integer.MAX_VALUE - this.loadingChunkTracker.runDistanceUpdates(Integer.MAX_VALUE); -- boolean flag = i != 0; -- if (flag && SharedConstants.DEBUG_VERBOSE_SERVER_EVENTS) { -- LOGGER.debug("DMU {}", i); +- int updates = Integer.MAX_VALUE - this.loadingChunkTracker.runDistanceUpdates(Integer.MAX_VALUE); +- boolean updated = updates != 0; +- if (updated && SharedConstants.DEBUG_VERBOSE_SERVER_EVENTS) { +- LOGGER.debug("DMU {}", updates); + @Override + public final void moonrise$updatePlayer(final ServerPlayer player, + final SectionPos oldPos, final SectionPos newPos, @@ -25182,76 +25927,74 @@ index d444c29800e103de55bfb99fd8671ee69b003419..8a5c37cf572abc6b9526648d3c8ec92b - if (!this.chunksToUpdateFutures.isEmpty()) { - // CraftBukkit start - SPIGOT-7780: Call chunk unload events before updateHighestAllowedStatus -- for (final ChunkHolder chunkHolder : this.chunksToUpdateFutures) { -- chunkHolder.callEventIfUnloading(chunkMap); +- for (final ChunkHolder chunksToUpdateFuture : this.chunksToUpdateFutures) { +- chunksToUpdateFuture.callEventIfUnloading(scheduler); - } - // CraftBukkit end - SPIGOT-7780: Call chunk unload events before updateHighestAllowedStatus - -- for (ChunkHolder chunkHolder : this.chunksToUpdateFutures) { -- chunkHolder.updateHighestAllowedStatus(chunkMap); +- for (ChunkHolder chunksToUpdateFuture : this.chunksToUpdateFutures) { +- chunksToUpdateFuture.updateHighestAllowedStatus(scheduler); - } - - for (ChunkHolder chunkHolder : this.chunksToUpdateFutures) { -- chunkHolder.updateFutures(chunkMap, this.mainThreadExecutor); +- chunkHolder.updateFutures(scheduler, this.mainThreadExecutor); - } - - this.chunksToUpdateFutures.clear(); - return true; - } else { - if (!this.ticketsToRelease.isEmpty()) { -- LongIterator longIterator = this.ticketsToRelease.iterator(); +- LongIterator iterator = this.ticketsToRelease.iterator(); + @Override + public final boolean moonrise$hasAnyNearbyNarrow(final int chunkX, final int chunkZ) { + throw new UnsupportedOperationException(); // Note: Cannot do narrow tracking on Paper due to custom spawn range + } ++ // Paper end - chunk tick iteration optimisation -- while (longIterator.hasNext()) { -- long l = longIterator.nextLong(); -- if (this.ticketStorage.getTickets(l).stream().anyMatch(ticket -> ticket.getType() == TicketType.PLAYER_LOADING)) { -- ChunkHolder updatingChunkIfPresent = chunkMap.getUpdatingChunkIfPresent(l); -- if (updatingChunkIfPresent == null) { +- while (iterator.hasNext()) { +- long pos = iterator.nextLong(); +- if (this.ticketStorage.getTickets(pos).stream().anyMatch(t -> t.getType() == TicketType.PLAYER_LOADING)) { +- ChunkHolder chunk = scheduler.getUpdatingChunkIfPresent(pos); +- if (chunk == null) { - throw new IllegalStateException(); - } -+ // Paper end - chunk tick iteration optimisation -+ protected abstract boolean isChunkToRemove(long chunkPos); ++ protected abstract boolean isChunkToRemove(final long node); -- CompletableFuture> entityTickingChunkFuture = updatingChunkIfPresent.getEntityTickingChunkFuture(); -- entityTickingChunkFuture.thenAccept( -- chunkResult -> this.mainThreadExecutor.execute(() -> this.ticketDispatcher.release(l, () -> {}, false)) -- ); +- CompletableFuture> future = chunk.getEntityTickingChunkFuture(); +- future.thenAccept(c -> this.mainThreadExecutor.execute(() -> this.ticketDispatcher.release(pos, () -> {}, false))); - } - } -+ protected abstract @Nullable ChunkHolder getChunk(long chunkPos); ++ protected abstract @Nullable ChunkHolder getChunk(final long node); - this.ticketsToRelease.clear(); - } -+ protected abstract @Nullable ChunkHolder updateChunkScheduling(long chunkPos, int newLevel, @Nullable ChunkHolder holder, int oldLevel); ++ protected abstract @Nullable ChunkHolder updateChunkScheduling(final long node, final int level, final @Nullable ChunkHolder chunk, final int oldLevel); -- return flag; +- return updated; - } -+ public boolean runAllUpdates(ChunkMap chunkMap) { ++ public boolean runAllUpdates(final ChunkMap scheduler) { + return this.moonrise$getChunkHolderManager().processTicketUpdates(); // Paper - rewrite chunk system } - public void addPlayer(SectionPos sectionPos, ServerPlayer player) { - ChunkPos chunkPos = sectionPos.chunk(); - long packedChunkPos = chunkPos.toLong(); - this.playersPerChunk.computeIfAbsent(packedChunkPos, l -> new ObjectOpenHashSet<>()).add(player); -- this.naturalSpawnChunkCounter.update(packedChunkPos, 0, true); -- this.playerTicketManager.update(packedChunkPos, 0, true); -- this.ticketStorage.addTicket(new Ticket(TicketType.PLAYER_SIMULATION, this.getPlayerTicketLevel()), chunkPos); + public void addPlayer(final SectionPos pos, final ServerPlayer player) { + ChunkPos chunk = pos.chunk(); + long chunkPos = chunk.pack(); + this.playersPerChunk.computeIfAbsent(chunkPos, k -> new ObjectOpenHashSet<>()).add(player); +- this.naturalSpawnChunkCounter.update(chunkPos, 0, true); +- this.playerTicketManager.update(chunkPos, 0, true); +- this.ticketStorage.addTicket(new Ticket(TicketType.PLAYER_SIMULATION, this.getPlayerTicketLevel()), chunk); + // Paper - chunk tick iteration optimisation + // Paper - rewrite chunk system } - public void removePlayer(SectionPos sectionPos, ServerPlayer player) { -@@ -132,71 +115,79 @@ public abstract class DistanceManager { - if (set == null || set.isEmpty()) { + public void removePlayer(final SectionPos pos, final ServerPlayer player) { +@@ -131,71 +116,79 @@ public abstract class DistanceManager { + if (chunkPlayers == null || chunkPlayers.isEmpty()) { // Paper end - some state corruption happens here, don't crash, clean up gracefully - this.playersPerChunk.remove(packedChunkPos); -- this.naturalSpawnChunkCounter.update(packedChunkPos, Integer.MAX_VALUE, false); -- this.playerTicketManager.update(packedChunkPos, Integer.MAX_VALUE, false); -- this.ticketStorage.removeTicket(new Ticket(TicketType.PLAYER_SIMULATION, this.getPlayerTicketLevel()), chunkPos); + this.playersPerChunk.remove(chunkPos); +- this.naturalSpawnChunkCounter.update(chunkPos, Integer.MAX_VALUE, false); +- this.playerTicketManager.update(chunkPos, Integer.MAX_VALUE, false); +- this.ticketStorage.removeTicket(new Ticket(TicketType.PLAYER_SIMULATION, this.getPlayerTicketLevel()), chunk); + // Paper - chunk tick iteration optimisation + // Paper - rewrite chunk system } @@ -25262,43 +26005,43 @@ index d444c29800e103de55bfb99fd8671ee69b003419..8a5c37cf572abc6b9526648d3c8ec92b + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public boolean inEntityTickingRange(long chunkPos) { -- return ChunkLevel.isEntityTicking(this.simulationChunkTracker.getLevel(chunkPos)); + public boolean inEntityTickingRange(final long key) { +- return ChunkLevel.isEntityTicking(this.simulationChunkTracker.getLevel(key)); + // Paper start - rewrite chunk system -+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(chunkPos); ++ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(key); + return chunkHolder != null && chunkHolder.isEntityTickingReady(); + // Paper end - rewrite chunk system } - public boolean inBlockTickingRange(long chunkPos) { -- return ChunkLevel.isBlockTicking(this.simulationChunkTracker.getLevel(chunkPos)); + public boolean inBlockTickingRange(final long key) { +- return ChunkLevel.isBlockTicking(this.simulationChunkTracker.getLevel(key)); + // Paper start - rewrite chunk system -+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(chunkPos); ++ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(key); + return chunkHolder != null && chunkHolder.isTickingReady(); + // Paper end - rewrite chunk system } - public int getChunkLevel(long chunkPos, boolean simulate) { -- return simulate ? this.simulationChunkTracker.getLevel(chunkPos) : this.loadingChunkTracker.getLevel(chunkPos); + public int getChunkLevel(final long key, final boolean simulation) { +- return simulation ? this.simulationChunkTracker.getLevel(key) : this.loadingChunkTracker.getLevel(key); + // Paper start - rewrite chunk system -+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(chunkPos); ++ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(key); + return chunkHolder == null ? ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager.MAX_TICKET_LEVEL + 1 : chunkHolder.getTicketLevel(); + // Paper end - rewrite chunk system } - protected void updatePlayerTickets(int viewDistance) { + protected void updatePlayerTickets(final int viewDistance) { - this.playerTicketManager.updateViewDistance(viewDistance); + this.moonrise$getChunkMap().setServerViewDistance(viewDistance); // Paper - rewrite chunk system } - public void updateSimulationDistance(int simulationDistance) { -- if (simulationDistance != this.simulationDistance) { -- this.simulationDistance = simulationDistance; + public void updateSimulationDistance(final int newDistance) { +- if (newDistance != this.simulationDistance) { +- this.simulationDistance = newDistance; - this.ticketStorage.replaceTicketLevelOfType(this.getPlayerTicketLevel(), TicketType.PLAYER_SIMULATION); - } + // Paper start - rewrite chunk system + // note: vanilla does not clamp to 0, but we do simply because we need a min of 0 -+ final int clamped = net.minecraft.util.Mth.clamp(simulationDistance, 0, ca.spottedleaf.moonrise.common.util.MoonriseConstants.MAX_VIEW_DISTANCE); ++ final int clamped = net.minecraft.util.Mth.clamp(newDistance, 0, ca.spottedleaf.moonrise.common.util.MoonriseConstants.MAX_VIEW_DISTANCE); + + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getPlayerChunkLoader().setTickDistance(clamped); + // Paper end - rewrite chunk system @@ -25310,24 +26053,24 @@ index d444c29800e103de55bfb99fd8671ee69b003419..8a5c37cf572abc6b9526648d3c8ec92b + return this.spawnChunkTracker.getTotalPositions(); // Paper - chunk tick iteration optimisation } - public TriState hasPlayersNearby(long chunkPos) { + public TriState hasPlayersNearby(final long pos) { - this.naturalSpawnChunkCounter.runAllUpdates(); -- int level = this.naturalSpawnChunkCounter.getLevel(chunkPos); -- if (level <= NaturalSpawner.INSCRIBED_SQUARE_SPAWN_DISTANCE_CHUNK) { +- int distance = this.naturalSpawnChunkCounter.getLevel(pos); +- if (distance <= NaturalSpawner.INSCRIBED_SQUARE_SPAWN_DISTANCE_CHUNK) { - return TriState.TRUE; - } else { -- return level > 8 ? TriState.FALSE : TriState.DEFAULT; +- return distance > 8 ? TriState.FALSE : TriState.DEFAULT; - } + // Note: Cannot do narrow tracking on Paper due to custom spawn range // Paper - chunk tick iteration optimisation -+ return this.spawnChunkTracker.hasObjectsNear(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(chunkPos), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(chunkPos)) ? net.minecraft.util.TriState.DEFAULT : net.minecraft.util.TriState.FALSE; // Paper - chunk tick iteration optimisation ++ return this.spawnChunkTracker.hasObjectsNear(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(pos), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(pos)) ? net.minecraft.util.TriState.DEFAULT : net.minecraft.util.TriState.FALSE; // Paper - chunk tick iteration optimisation } - public void forEachEntityTickingChunk(LongConsumer action) { + public void forEachEntityTickingChunk(final LongConsumer consumer) { - for (Entry entry : Long2ByteMaps.fastIterable(this.simulationChunkTracker.chunks)) { -- byte byteValue = entry.getByteValue(); -- long longKey = entry.getLongKey(); -- if (ChunkLevel.isEntityTicking(byteValue)) { -- action.accept(longKey); +- byte level = entry.getByteValue(); +- long key = entry.getLongKey(); +- if (ChunkLevel.isEntityTicking(level)) { +- consumer.accept(key); - } + // Paper start - rewrite chunk system + final ca.spottedleaf.moonrise.common.list.ReferenceList chunks = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getEntityTickingChunks(); @@ -25338,7 +26081,7 @@ index d444c29800e103de55bfb99fd8671ee69b003419..8a5c37cf572abc6b9526648d3c8ec92b + for (int i = 0; i < size; ++i) { + final LevelChunk chunk = raw[i]; + -+ action.accept(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunk.getPos())); ++ consumer.accept(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunk.getPos())); } + // Paper end - rewrite chunk system } @@ -25355,26 +26098,26 @@ index d444c29800e103de55bfb99fd8671ee69b003419..8a5c37cf572abc6b9526648d3c8ec92b } public boolean hasTickets() { -@@ -248,6 +239,7 @@ public abstract class DistanceManager { +@@ -249,6 +242,7 @@ public abstract class DistanceManager { } } +/* // Paper - rewrite chunk system - class PlayerTicketTracker extends DistanceManager.FixedPlayerDistanceChunkTracker { + private class PlayerTicketTracker extends DistanceManager.FixedPlayerDistanceChunkTracker { private int viewDistance; - private final Long2IntMap queueLevels = Long2IntMaps.synchronize(new Long2IntOpenHashMap()); -@@ -326,5 +318,5 @@ public abstract class DistanceManager { - private boolean haveTicketFor(int level) { + private final Long2IntMap queueLevels; +@@ -330,5 +324,5 @@ public abstract class DistanceManager { + private boolean haveTicketFor(final int level) { return level <= this.viewDistance; } - } + }*/ // Paper - rewrite chunk system } diff --git a/net/minecraft/server/level/GenerationChunkHolder.java b/net/minecraft/server/level/GenerationChunkHolder.java -index 7629a94896551f4e1db4bb61ed410e147924df47..49f9980ba8d8ade61a46ef3ee1d4d88092146375 100644 +index 531cc9b4375f7eb7c1ca6dcd40c29b74b600ec63..75686eb990d7275b442e16df39b7b2e181c0048f 100644 --- a/net/minecraft/server/level/GenerationChunkHolder.java +++ b/net/minecraft/server/level/GenerationChunkHolder.java -@@ -27,12 +27,7 @@ public abstract class GenerationChunkHolder { +@@ -26,12 +26,7 @@ public abstract class GenerationChunkHolder { public static final ChunkResult UNLOADED_CHUNK = ChunkResult.error("Unloaded chunk"); public static final CompletableFuture> UNLOADED_CHUNK_FUTURE = CompletableFuture.completedFuture(UNLOADED_CHUNK); protected final ChunkPos pos; @@ -25386,22 +26129,22 @@ index 7629a94896551f4e1db4bb61ed410e147924df47..49f9980ba8d8ade61a46ef3ee1d4d880 - private volatile CompletableFuture generationSaveSyncFuture = CompletableFuture.completedFuture(null); + // Paper - rewrite chunk system - public GenerationChunkHolder(ChunkPos pos) { + public GenerationChunkHolder(final ChunkPos pos) { this.pos = pos; -@@ -42,238 +37,91 @@ public abstract class GenerationChunkHolder { +@@ -41,240 +36,93 @@ public abstract class GenerationChunkHolder { } - public CompletableFuture> scheduleChunkGenerationTask(ChunkStatus targetStatus, ChunkMap chunkMap) { -- if (this.isStatusDisallowed(targetStatus)) { + public CompletableFuture> scheduleChunkGenerationTask(final ChunkStatus status, final ChunkMap scheduler) { +- if (this.isStatusDisallowed(status)) { - return UNLOADED_CHUNK_FUTURE; - } else { -- CompletableFuture> future = this.getOrCreateFuture(targetStatus); +- CompletableFuture> future = this.getOrCreateFuture(status); - if (future.isDone()) { - return future; - } else { -- ChunkGenerationTask chunkGenerationTask = this.task.get(); -- if (chunkGenerationTask == null || targetStatus.isAfter(chunkGenerationTask.targetStatus)) { -- this.rescheduleChunkTask(chunkMap, targetStatus); +- ChunkGenerationTask task = this.task.get(); +- if (task == null || status.isAfter(task.targetStatus)) { +- this.rescheduleChunkTask(scheduler, status); - } - - return future; @@ -25410,137 +26153,139 @@ index 7629a94896551f4e1db4bb61ed410e147924df47..49f9980ba8d8ade61a46ef3ee1d4d880 + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - CompletableFuture> applyStep(ChunkStep step, GeneratingChunkMap chunkMap, StaticCache2D cache) { + CompletableFuture> applyStep( + final ChunkStep step, final GeneratingChunkMap chunkMap, final StaticCache2D cache + ) { - if (this.isStatusDisallowed(step.targetStatus())) { - return UNLOADED_CHUNK_FUTURE; - } else { -- return this.acquireStatusBump(step.targetStatus()) ? chunkMap.applyStep(this, step, cache).handle((chunkAccess, throwable) -> { -- if (throwable != null) { -- CrashReport crashReport = CrashReport.forThrowable(throwable, "Exception chunk generation/loading"); -- MinecraftServer.setFatalException(new ReportedException(crashReport)); +- return this.acquireStatusBump(step.targetStatus()) ? chunkMap.applyStep(this, step, cache).handle((chunk, exception) -> { +- if (exception != null) { +- CrashReport report = CrashReport.forThrowable(exception, "Exception chunk generation/loading"); +- BlockableEventLoop.relayDelayCrash(report); - } else { -- this.completeFuture(step.targetStatus(), chunkAccess); +- this.completeFuture(step.targetStatus(), chunk); - } - -- return ChunkResult.of(chunkAccess); +- return ChunkResult.of(chunk); - }) : this.getOrCreateFuture(step.targetStatus()); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - protected void updateHighestAllowedStatus(ChunkMap chunkMap) { -- ChunkStatus chunkStatus = this.highestAllowedStatus; -- ChunkStatus chunkStatus1 = ChunkLevel.generationStatus(this.getTicketLevel()); -- this.highestAllowedStatus = chunkStatus1; -- boolean flag = chunkStatus != null && (chunkStatus1 == null || chunkStatus1.isBefore(chunkStatus)); -- if (flag) { -- this.failAndClearPendingFuturesBetween(chunkStatus1, chunkStatus); + protected void updateHighestAllowedStatus(final ChunkMap scheduler) { +- ChunkStatus oldStatus = this.highestAllowedStatus; +- ChunkStatus newStatus = ChunkLevel.generationStatus(this.getTicketLevel()); +- this.highestAllowedStatus = newStatus; +- boolean statusDropped = oldStatus != null && (newStatus == null || newStatus.isBefore(oldStatus)); +- if (statusDropped) { +- this.failAndClearPendingFuturesBetween(newStatus, oldStatus); - if (this.task.get() != null) { -- this.rescheduleChunkTask(chunkMap, this.findHighestStatusWithPendingFuture(chunkStatus1)); +- this.rescheduleChunkTask(scheduler, this.findHighestStatusWithPendingFuture(newStatus)); - } - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public void replaceProtoChunk(ImposterProtoChunk chunk) { -- CompletableFuture> completableFuture = CompletableFuture.completedFuture(ChunkResult.of(chunk)); + public void replaceProtoChunk(final ImposterProtoChunk chunk) { +- CompletableFuture> imposterFuture = CompletableFuture.completedFuture(ChunkResult.of(chunk)); - - for (int i = 0; i < this.futures.length() - 1; i++) { -- CompletableFuture> completableFuture1 = this.futures.get(i); -- Objects.requireNonNull(completableFuture1); -- ChunkAccess chunkAccess = completableFuture1.getNow(NOT_DONE_YET).orElse(null); -- if (!(chunkAccess instanceof ProtoChunk)) { -- throw new IllegalStateException("Trying to replace a ProtoChunk, but found " + chunkAccess); +- CompletableFuture> future = this.futures.get(i); +- Objects.requireNonNull(future); +- ChunkAccess maybeProtoChunk = future.getNow(NOT_DONE_YET).orElse(null); +- if (!(maybeProtoChunk instanceof ProtoChunk)) { +- throw new IllegalStateException("Trying to replace a ProtoChunk, but found " + maybeProtoChunk); - } - -- if (!this.futures.compareAndSet(i, completableFuture1, completableFuture)) { +- if (!this.futures.compareAndSet(i, future, imposterFuture)) { - throw new IllegalStateException("Future changed by other thread while trying to replace it"); - } - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - void removeTask(ChunkGenerationTask task) { + void removeTask(final ChunkGenerationTask task) { - this.task.compareAndSet(task, null); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void rescheduleChunkTask(ChunkMap chunkMap, @Nullable ChunkStatus targetStatus) { -- ChunkGenerationTask chunkGenerationTask; -- if (targetStatus != null) { -- chunkGenerationTask = chunkMap.scheduleGenerationTask(targetStatus, this.getPos()); + private void rescheduleChunkTask(final ChunkMap scheduler, final @Nullable ChunkStatus status) { +- ChunkGenerationTask newTask; +- if (status != null) { +- newTask = scheduler.scheduleGenerationTask(status, this.getPos()); - } else { -- chunkGenerationTask = null; +- newTask = null; - } - -- ChunkGenerationTask chunkGenerationTask1 = this.task.getAndSet(chunkGenerationTask); -- if (chunkGenerationTask1 != null) { -- chunkGenerationTask1.markForCancellation(); +- ChunkGenerationTask oldTask = this.task.getAndSet(newTask); +- if (oldTask != null) { +- oldTask.markForCancellation(); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private CompletableFuture> getOrCreateFuture(ChunkStatus targetStatus) { -- if (this.isStatusDisallowed(targetStatus)) { + private CompletableFuture> getOrCreateFuture(final ChunkStatus status) { +- if (this.isStatusDisallowed(status)) { - return UNLOADED_CHUNK_FUTURE; - } else { -- int index = targetStatus.getIndex(); -- CompletableFuture> completableFuture = this.futures.get(index); +- int index = status.getIndex(); +- CompletableFuture> future = this.futures.get(index); - -- while (completableFuture == null) { -- CompletableFuture> completableFuture1 = new CompletableFuture<>(); -- completableFuture = this.futures.compareAndExchange(index, null, completableFuture1); -- if (completableFuture == null) { -- if (this.isStatusDisallowed(targetStatus)) { -- this.failAndClearPendingFuture(index, completableFuture1); +- while (future == null) { +- CompletableFuture> newValue = new CompletableFuture<>(); +- future = this.futures.compareAndExchange(index, null, newValue); +- if (future == null) { +- if (this.isStatusDisallowed(status)) { +- this.failAndClearPendingFuture(index, newValue); - return UNLOADED_CHUNK_FUTURE; - } - -- return completableFuture1; +- return newValue; - } - } - -- return completableFuture; +- return future; - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void failAndClearPendingFuturesBetween(@Nullable ChunkStatus highestAllowableStatus, ChunkStatus currentStatus) { -- int i = highestAllowableStatus == null ? 0 : highestAllowableStatus.getIndex() + 1; -- int index = currentStatus.getIndex(); + private void failAndClearPendingFuturesBetween(final @Nullable ChunkStatus fromExclusive, final ChunkStatus toInclusive) { +- int start = fromExclusive == null ? 0 : fromExclusive.getIndex() + 1; +- int end = toInclusive.getIndex(); - -- for (int i1 = i; i1 <= index; i1++) { -- CompletableFuture> completableFuture = this.futures.get(i1); -- if (completableFuture != null) { -- this.failAndClearPendingFuture(i1, completableFuture); +- for (int i = start; i <= end; i++) { +- CompletableFuture> previous = this.futures.get(i); +- if (previous != null) { +- this.failAndClearPendingFuture(i, previous); - } - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void failAndClearPendingFuture(int status, CompletableFuture> future) { -- if (future.complete(UNLOADED_CHUNK) && !this.futures.compareAndSet(status, future, null)) { + private void failAndClearPendingFuture(final int index, final CompletableFuture> previous) { +- if (previous.complete(UNLOADED_CHUNK) && !this.futures.compareAndSet(index, previous, null)) { - throw new IllegalStateException("Nothing else should replace the future here"); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void completeFuture(ChunkStatus targetStatus, ChunkAccess chunkAccess) { -- ChunkResult chunkResult = ChunkResult.of(chunkAccess); -- int index = targetStatus.getIndex(); + private void completeFuture(final ChunkStatus status, final ChunkAccess chunk) { +- ChunkResult result = ChunkResult.of(chunk); +- int index = status.getIndex(); - - while (true) { -- CompletableFuture> completableFuture = this.futures.get(index); -- if (completableFuture == null) { -- if (this.futures.compareAndSet(index, null, CompletableFuture.completedFuture(chunkResult))) { +- CompletableFuture> future = this.futures.get(index); +- if (future == null) { +- if (this.futures.compareAndSet(index, null, CompletableFuture.completedFuture(result))) { - return; - } - } else { -- if (completableFuture.complete(chunkResult)) { +- if (future.complete(result)) { - return; - } - -- if (completableFuture.getNow(NOT_DONE_YET).isSuccess()) { +- if (future.getNow(NOT_DONE_YET).isSuccess()) { - throw new IllegalStateException("Trying to complete a future but found it to be completed successfully already"); - } - @@ -25550,21 +26295,21 @@ index 7629a94896551f4e1db4bb61ed410e147924df47..49f9980ba8d8ade61a46ef3ee1d4d880 + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private @Nullable ChunkStatus findHighestStatusWithPendingFuture(@Nullable ChunkStatus generationStatus) { -- if (generationStatus == null) { + private @Nullable ChunkStatus findHighestStatusWithPendingFuture(final @Nullable ChunkStatus newStatus) { +- if (newStatus == null) { - return null; - } else { -- ChunkStatus chunkStatus = generationStatus; +- ChunkStatus highestStatus = newStatus; - -- for (ChunkStatus chunkStatus1 = this.startedWork.get(); -- chunkStatus1 == null || chunkStatus.isAfter(chunkStatus1); -- chunkStatus = chunkStatus.getParent() +- for (ChunkStatus alreadyStarted = this.startedWork.get(); +- alreadyStarted == null || highestStatus.isAfter(alreadyStarted); +- highestStatus = highestStatus.getParent() - ) { -- if (this.futures.get(chunkStatus.getIndex()) != null) { -- return chunkStatus; +- if (this.futures.get(highestStatus.getIndex()) != null) { +- return highestStatus; - } - -- if (chunkStatus == ChunkStatus.EMPTY) { +- if (highestStatus == ChunkStatus.EMPTY) { - break; - } - } @@ -25574,26 +26319,26 @@ index 7629a94896551f4e1db4bb61ed410e147924df47..49f9980ba8d8ade61a46ef3ee1d4d880 + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private boolean acquireStatusBump(ChunkStatus status) { -- ChunkStatus chunkStatus = status == ChunkStatus.EMPTY ? null : status.getParent(); -- ChunkStatus chunkStatus1 = this.startedWork.compareAndExchange(chunkStatus, status); -- if (chunkStatus1 == chunkStatus) { + private boolean acquireStatusBump(final ChunkStatus status) { +- ChunkStatus parent = status == ChunkStatus.EMPTY ? null : status.getParent(); +- ChunkStatus previousStarted = this.startedWork.compareAndExchange(parent, status); +- if (previousStarted == parent) { - return true; -- } else if (chunkStatus1 != null && !status.isAfter(chunkStatus1)) { +- } else if (previousStarted != null && !status.isAfter(previousStarted)) { - return false; - } else { -- throw new IllegalStateException("Unexpected last startedWork status: " + chunkStatus1 + " while trying to start: " + status); +- throw new IllegalStateException("Unexpected last startedWork status: " + previousStarted + " while trying to start: " + status); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private boolean isStatusDisallowed(ChunkStatus status) { -- ChunkStatus chunkStatus = this.highestAllowedStatus; -- return chunkStatus == null || status.isAfter(chunkStatus); + private boolean isStatusDisallowed(final ChunkStatus status) { +- ChunkStatus highestAllowedStatus = this.highestAllowedStatus; +- return highestAllowedStatus == null || status.isAfter(highestAllowedStatus); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - protected abstract void addSaveDependency(CompletableFuture saveDependency); + protected abstract void addSaveDependency(final CompletableFuture sync); public void increaseGenerationRefCount() { - if (this.generationRefCount.getAndIncrement() == 0) { @@ -25604,27 +26349,27 @@ index 7629a94896551f4e1db4bb61ed410e147924df47..49f9980ba8d8ade61a46ef3ee1d4d880 } public void decreaseGenerationRefCount() { -- CompletableFuture completableFuture = this.generationSaveSyncFuture; -- int i = this.generationRefCount.decrementAndGet(); -- if (i == 0) { -- completableFuture.complete(null); +- CompletableFuture future = this.generationSaveSyncFuture; +- int newValue = this.generationRefCount.decrementAndGet(); +- if (newValue == 0) { +- future.complete(null); - } - -- if (i < 0) { -- throw new IllegalStateException("More releases than claims. Count: " + i); +- if (newValue < 0) { +- throw new IllegalStateException("More releases than claims. Count: " + newValue); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public @Nullable ChunkAccess getChunkIfPresentUnchecked(ChunkStatus status) { -- CompletableFuture> completableFuture = this.futures.get(status.getIndex()); -- return completableFuture == null ? null : completableFuture.getNow(NOT_DONE_YET).orElse(null); + public @Nullable ChunkAccess getChunkIfPresentUnchecked(final ChunkStatus status) { +- CompletableFuture> future = this.futures.get(status.getIndex()); +- return future == null ? null : future.getNow(NOT_DONE_YET).orElse(null); + // Paper start - rewrite chunk system + return ((ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder)(Object)this).moonrise$getRealChunkHolder().getChunkIfPresentUnchecked(status); + // Paper end - rewrite chunk system } - public @Nullable ChunkAccess getChunkIfPresent(ChunkStatus status) { + public @Nullable ChunkAccess getChunkIfPresent(final ChunkStatus status) { - return this.isStatusDisallowed(status) ? null : this.getChunkIfPresentUnchecked(status); + // Paper start - rewrite chunk system + return ((ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder)(Object)this).moonrise$getRealChunkHolder().getChunkIfPresent(status); @@ -25632,12 +26377,12 @@ index 7629a94896551f4e1db4bb61ed410e147924df47..49f9980ba8d8ade61a46ef3ee1d4d880 } public @Nullable ChunkAccess getLatestChunk() { -- ChunkStatus chunkStatus = this.startedWork.get(); -- if (chunkStatus == null) { +- ChunkStatus status = this.startedWork.get(); +- if (status == null) { - return null; - } else { -- ChunkAccess chunkIfPresentUnchecked = this.getChunkIfPresentUnchecked(chunkStatus); -- return chunkIfPresentUnchecked != null ? chunkIfPresentUnchecked : this.getChunkIfPresentUnchecked(chunkStatus.getParent()); +- ChunkAccess chunk = this.getChunkIfPresentUnchecked(status); +- return chunk != null ? chunk : this.getChunkIfPresentUnchecked(status.getParent()); - } + // Paper start - rewrite chunk system + final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder.ChunkCompletion lastCompletion = ((ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder)(Object)this).moonrise$getRealChunkHolder().getLastChunkCompletion(); @@ -25646,8 +26391,8 @@ index 7629a94896551f4e1db4bb61ed410e147924df47..49f9980ba8d8ade61a46ef3ee1d4d880 } public @Nullable ChunkStatus getPersistedStatus() { -- CompletableFuture> completableFuture = this.futures.get(ChunkStatus.EMPTY.getIndex()); -- ChunkAccess chunkAccess = completableFuture == null ? null : completableFuture.getNow(NOT_DONE_YET).orElse(null); +- CompletableFuture> future = this.futures.get(ChunkStatus.EMPTY.getIndex()); +- ChunkAccess chunkAccess = future == null ? null : future.getNow(NOT_DONE_YET).orElse(null); - return chunkAccess == null ? null : chunkAccess.getPersistedStatus(); + // Paper start - rewrite chunk system + final ChunkAccess chunk = this.getLatestChunk(); @@ -25656,7 +26401,7 @@ index 7629a94896551f4e1db4bb61ed410e147924df47..49f9980ba8d8ade61a46ef3ee1d4d880 } public ChunkPos getPos() { -@@ -281,7 +129,7 @@ public abstract class GenerationChunkHolder { +@@ -282,7 +130,7 @@ public abstract class GenerationChunkHolder { } public FullChunkStatus getFullStatus() { @@ -25665,28 +26410,28 @@ index 7629a94896551f4e1db4bb61ed410e147924df47..49f9980ba8d8ade61a46ef3ee1d4d880 } public abstract int getTicketLevel(); -@@ -290,23 +138,14 @@ public abstract class GenerationChunkHolder { +@@ -291,23 +139,14 @@ public abstract class GenerationChunkHolder { @VisibleForDebug public List>>> getAllFutures() { -- List>>> list = new ArrayList<>(); +- List>>> result = new ArrayList<>(); - - for (int i = 0; i < CHUNK_STATUSES.size(); i++) { -- list.add(Pair.of(CHUNK_STATUSES.get(i), this.futures.get(i))); +- result.add(Pair.of(CHUNK_STATUSES.get(i), this.futures.get(i))); - } - -- return list; +- return result; + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @VisibleForDebug public @Nullable ChunkStatus getLatestStatus() { -- ChunkStatus chunkStatus = this.startedWork.get(); -- if (chunkStatus == null) { +- ChunkStatus status = this.startedWork.get(); +- if (status == null) { - return null; - } else { -- ChunkAccess chunkIfPresentUnchecked = this.getChunkIfPresentUnchecked(chunkStatus); -- return chunkIfPresentUnchecked != null ? chunkStatus : chunkStatus.getParent(); +- ChunkAccess chunk = this.getChunkIfPresentUnchecked(status); +- return chunk != null ? status : status.getParent(); - } + // Paper start - rewrite chunk system + final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder.ChunkCompletion lastCompletion = ((ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder)(Object)this).moonrise$getRealChunkHolder().getLastChunkCompletion(); @@ -25695,12 +26440,12 @@ index 7629a94896551f4e1db4bb61ed410e147924df47..49f9980ba8d8ade61a46ef3ee1d4d880 } } diff --git a/net/minecraft/server/level/LoadingChunkTracker.java b/net/minecraft/server/level/LoadingChunkTracker.java -index 302841522cf990c38b1493b716048c0f2db40726..7932a6676db7b652d63be5ae4dcf9bcf9ca298e6 100644 +index b107715893fc5329ae4a27967780d3587166b6dd..15cd107d347ee01c809938684bf90cc24d0b4caf 100644 --- a/net/minecraft/server/level/LoadingChunkTracker.java +++ b/net/minecraft/server/level/LoadingChunkTracker.java @@ -38,7 +38,7 @@ class LoadingChunkTracker extends ChunkTracker { - if (i != level) { - chunk = this.distanceManager.updateChunkScheduling(chunkPos, level, chunk, i); + if (oldLevel != level) { + chunk = this.distanceManager.updateChunkScheduling(node, level, chunk, oldLevel); if (chunk != null) { - this.distanceManager.chunksToUpdateFutures.add(chunk); + // Paper - rewrite chunk system @@ -25708,23 +26453,25 @@ index 302841522cf990c38b1493b716048c0f2db40726..7932a6676db7b652d63be5ae4dcf9bcf } } diff --git a/net/minecraft/server/level/PlayerSpawnFinder.java b/net/minecraft/server/level/PlayerSpawnFinder.java -index d930750ae2ccfe72ff4fa574a49a024cb5f67baa..2c6d0e17fe28751c38cf8186ba869aac2ec6d778 100644 +index 045d4b0ab26622e2763d1583e1e8fcb83ba44427..ddffd36d8213a40dc3f6cbb0ef0f33a0a7490ac1 100644 --- a/net/minecraft/server/level/PlayerSpawnFinder.java +++ b/net/minecraft/server/level/PlayerSpawnFinder.java -@@ -122,9 +122,7 @@ public class PlayerSpawnFinder { +@@ -115,9 +115,9 @@ public class PlayerSpawnFinder { if (!this.finishedFuture.isDone()) { - int sectionPosX = SectionPos.blockToSectionCoord(x); - int sectionPosZ = SectionPos.blockToSectionCoord(z); + int chunkX = SectionPos.blockToSectionCoord(candidateX); + int chunkZ = SectionPos.blockToSectionCoord(candidateZ); - this.level - .getChunkSource() -- .addTicketAndLoadWithRadius(TicketType.SPAWN_SEARCH, new ChunkPos(sectionPosX, sectionPosZ), 0) -+ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.addTicketAndLoadWithRadius(TicketType.SPAWN_SEARCH, new ChunkPos(sectionPosX, sectionPosZ), 0, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, ca.spottedleaf.concurrentutil.util.Priority.HIGH) // Paper - rewrite chunk system - .whenCompleteAsync((object, throwable) -> { +- .addTicketAndLoadWithRadius(TicketType.SPAWN_SEARCH, new ChunkPos(chunkX, chunkZ), 0) ++ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level) // Paper - rewrite chunk system ++ .moonrise$getChunkTaskScheduler().chunkHolderManager // Paper - rewrite chunk system ++ .addTicketAndLoadWithRadius(TicketType.SPAWN_SEARCH, new ChunkPos(chunkX, chunkZ), 0, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, ca.spottedleaf.concurrentutil.util.Priority.HIGH) // Paper - rewrite chunk system + .whenCompleteAsync((ignored, throwable) -> { if (throwable == null) { try { -@@ -148,7 +146,7 @@ public class PlayerSpawnFinder { - crashReportCategory.setDetail("Progress", () -> index + " out of " + this.candidateCount); - this.finishedFuture.completeExceptionally(new ReportedException(crashReport)); +@@ -141,7 +141,7 @@ public class PlayerSpawnFinder { + details.setDetail("Progress", () -> candidateIndex + " out of " + this.candidateCount); + this.finishedFuture.completeExceptionally(new ReportedException(report)); } - }, this.level.getServer()); + }, this.level.chunkSource.mainThreadProcessor); // Paper - rewrite chunk system @@ -25732,10 +26479,10 @@ index d930750ae2ccfe72ff4fa574a49a024cb5f67baa..2c6d0e17fe28751c38cf8186ba869aac } diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5277f7661 100644 +index 319335341617051c5b78578465f2224f78c3ea3d..fcb23814c2539f4e31ed5191e03cc28f4e7f9c2d 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -55,7 +55,7 @@ import net.minecraft.world.level.storage.LevelStorageSource; +@@ -54,7 +54,7 @@ import net.minecraft.world.level.storage.SavedDataStorage; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; @@ -25744,7 +26491,7 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 private static final Logger LOGGER = LogUtils.getLogger(); private final DistanceManager distanceManager; private final ServerLevel level; -@@ -83,6 +83,106 @@ public class ServerChunkCache extends ChunkSource { +@@ -82,6 +82,106 @@ public class ServerChunkCache extends ChunkSource { } long chunkFutureAwaitCounter; // Paper end @@ -25850,7 +26597,7 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 + public ServerChunkCache( - ServerLevel level, + final ServerLevel level, @@ -141,13 +241,7 @@ public class ServerChunkCache extends ChunkSource { } // CraftBukkit end @@ -25866,19 +26613,19 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 @Nullable public ChunkAccess getChunkAtImmediately(int x, int z) { -@@ -212,67 +306,41 @@ public class ServerChunkCache extends ChunkSource { +@@ -212,67 +306,42 @@ public class ServerChunkCache extends ChunkSource { @Override - public @Nullable ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk) { + public @Nullable ChunkAccess getChunk(final int x, final int z, final ChunkStatus targetStatus, final boolean loadOrGenerate) { - if (Thread.currentThread() != this.mainThread) { -- return CompletableFuture.supplyAsync(() -> this.getChunk(x, z, chunkStatus, requireChunk), this.mainThreadProcessor).join(); +- return CompletableFuture.supplyAsync(() -> this.getChunk(x, z, targetStatus, loadOrGenerate), this.mainThreadProcessor).join(); - } else { -- ProfilerFiller profilerFiller = Profiler.get(); -- profilerFiller.incrementCounter("getChunk"); -- long packedChunkPos = ChunkPos.asLong(x, z); +- ProfilerFiller profiler = Profiler.get(); +- profiler.incrementCounter("getChunk"); +- long pos = ChunkPos.pack(x, z); - - for (int i = 0; i < 4; i++) { -- if (packedChunkPos == this.lastChunkPos[i] && chunkStatus == this.lastChunkStatus[i]) { +- if (pos == this.lastChunkPos[i] && targetStatus == this.lastChunkStatus[i]) { - ChunkAccess chunkAccess = this.lastChunk[i]; - if (chunkAccess != null) { // CraftBukkit - the chunk can become accessible in the meantime TODO for non-null chunks it might also make sense to check that the chunk's state hasn't changed in the meantime - return chunkAccess; @@ -25886,60 +26633,60 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 - } - } + // Paper start - rewrite chunk system -+ if (chunkStatus == ChunkStatus.FULL) { ++ if (targetStatus == ChunkStatus.FULL) { + final LevelChunk ret = this.fullChunks.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(x, z)); -- profilerFiller.incrementCounter("getChunkCacheMiss"); -- CompletableFuture> chunkFutureMainThread = this.getChunkFutureMainThread(x, z, chunkStatus, requireChunk); -- this.mainThreadProcessor.managedBlock(chunkFutureMainThread::isDone); +- profiler.incrementCounter("getChunkCacheMiss"); +- CompletableFuture> serverFuture = this.getChunkFutureMainThread(x, z, targetStatus, loadOrGenerate); +- this.mainThreadProcessor.managedBlock(serverFuture::isDone); - // com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x, z); // Paper - Add debug for sync chunk loads -- ChunkResult chunkResult = chunkFutureMainThread.join(); -- ChunkAccess chunkAccess1 = chunkResult.orElse(null); -- if (chunkAccess1 == null && requireChunk) { +- ChunkResult chunkResult = serverFuture.join(); +- ChunkAccess chunk = chunkResult.orElse(null); +- if (chunk == null && loadOrGenerate) { - throw (IllegalStateException)Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + chunkResult.getError())); - } else { -- this.storeInCache(packedChunkPos, chunkAccess1, chunkStatus); -- return chunkAccess1; +- this.storeInCache(pos, chunk, targetStatus); +- return chunk; + if (ret != null) { + return ret; } + -+ return requireChunk ? this.getChunkFallback(x, z, chunkStatus, requireChunk) : null; ++ return loadOrGenerate ? this.getChunkFallback(x, z, targetStatus, loadOrGenerate) : null; } + -+ return this.getChunkFallback(x, z, chunkStatus, requireChunk); ++ return this.getChunkFallback(x, z, targetStatus, loadOrGenerate); + // Paper end - rewrite chunk system } @Override - public @Nullable LevelChunk getChunkNow(int chunkX, int chunkZ) { + public @Nullable LevelChunk getChunkNow(final int x, final int z) { - if (Thread.currentThread() != this.mainThread) { - return null; - } else { - Profiler.get().incrementCounter("getChunkNow"); -- long packedChunkPos = ChunkPos.asLong(chunkX, chunkZ); -- +- long pos = ChunkPos.pack(x, z); ++ // Paper start - rewrite chunk system ++ final LevelChunk ret = this.fullChunks.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(x, z)); ++ if (!ca.spottedleaf.moonrise.common.PlatformHooks.get().hasCurrentlyLoadingChunk()) { ++ return ret; + - for (int i = 0; i < 4; i++) { -- if (packedChunkPos == this.lastChunkPos[i] && this.lastChunkStatus[i] == ChunkStatus.FULL) { +- if (pos == this.lastChunkPos[i] && this.lastChunkStatus[i] == ChunkStatus.FULL) { - ChunkAccess chunkAccess = this.lastChunk[i]; - return chunkAccess instanceof LevelChunk ? (LevelChunk)chunkAccess : null; - } - } -+ // Paper start - rewrite chunk system -+ final LevelChunk ret = this.fullChunks.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkX, chunkZ)); -+ if (!ca.spottedleaf.moonrise.common.PlatformHooks.get().hasCurrentlyLoadingChunk()) { -+ return ret; + } -- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(packedChunkPos); -- if (visibleChunkIfPresent == null) { +- ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(pos); +- if (chunkHolder == null) { - return null; - } else { -- ChunkAccess chunkAccess = visibleChunkIfPresent.getChunkIfPresent(ChunkStatus.FULL); -- if (chunkAccess != null) { -- this.storeInCache(packedChunkPos, chunkAccess, ChunkStatus.FULL); -- if (chunkAccess instanceof LevelChunk) { -- return (LevelChunk)chunkAccess; +- ChunkAccess chunk = chunkHolder.getChunkIfPresent(ChunkStatus.FULL); +- if (chunk != null) { +- this.storeInCache(pos, chunk, ChunkStatus.FULL); +- if (chunk instanceof LevelChunk) { +- return (LevelChunk)chunk; - } - } + if (ret != null || !ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) { @@ -25949,7 +26696,7 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 - return null; - } + final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder holder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler() -+ .chunkHolderManager.getChunkHolder(chunkX, chunkZ); ++ .chunkHolderManager.getChunkHolder(x, z); + if (holder == null) { + return ret; } @@ -25959,51 +26706,50 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 } private void clearCache() { -@@ -298,53 +366,58 @@ public class ServerChunkCache extends ChunkSource { - } - - private CompletableFuture> getChunkFutureMainThread(int x, int z, ChunkStatus chunkStatus, boolean requireChunk) { -- ChunkPos chunkPos = new ChunkPos(x, z); -- long packedChunkPos = chunkPos.toLong(); -- int i = ChunkLevel.byStatus(chunkStatus); -- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(packedChunkPos); +@@ -300,53 +369,62 @@ public class ServerChunkCache extends ChunkSource { + private CompletableFuture> getChunkFutureMainThread( + final int x, final int z, final ChunkStatus targetStatus, final boolean loadOrGenerate + ) { +- ChunkPos pos = new ChunkPos(x, z); +- long key = pos.pack(); +- int targetTicketLevel = ChunkLevel.byStatus(targetStatus); +- ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(key); - // CraftBukkit start - don't add new ticket for currently unloading chunk - boolean currentlyUnloading = false; -- if (visibleChunkIfPresent != null) { -- FullChunkStatus oldChunkState = ChunkLevel.fullStatus(visibleChunkIfPresent.oldTicketLevel); -- FullChunkStatus currentChunkState = ChunkLevel.fullStatus(visibleChunkIfPresent.getTicketLevel()); +- if (chunkHolder != null) { +- FullChunkStatus oldChunkState = ChunkLevel.fullStatus(chunkHolder.oldTicketLevel); +- FullChunkStatus currentChunkState = ChunkLevel.fullStatus(chunkHolder.getTicketLevel()); - currentlyUnloading = (oldChunkState.isOrAfter(FullChunkStatus.FULL) && !currentChunkState.isOrAfter(FullChunkStatus.FULL)); - } -- if (requireChunk && !currentlyUnloading) { +- if (loadOrGenerate && !currentlyUnloading) { - // CraftBukkit end -- this.addTicket(new Ticket(TicketType.UNKNOWN, i), chunkPos); -- if (this.chunkAbsent(visibleChunkIfPresent, i)) { -- ProfilerFiller profilerFiller = Profiler.get(); -- profilerFiller.push("chunkLoad"); +- this.addTicket(new Ticket(TicketType.UNKNOWN, targetTicketLevel), pos); +- if (this.chunkAbsent(chunkHolder, targetTicketLevel)) { +- ProfilerFiller profiler = Profiler.get(); +- profiler.push("chunkLoad"); - this.runDistanceManagerUpdates(); -- visibleChunkIfPresent = this.getVisibleChunkIfPresent(packedChunkPos); -- profilerFiller.pop(); -- if (this.chunkAbsent(visibleChunkIfPresent, i)) { +- chunkHolder = this.getVisibleChunkIfPresent(key); +- profiler.pop(); +- if (this.chunkAbsent(chunkHolder, targetTicketLevel)) { - throw (IllegalStateException)Util.pauseInIde(new IllegalStateException("No chunk holder after ticket has been added")); - } - } + // Paper start - rewrite chunk system + ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.level, x, z, "Scheduling chunk load off-main"); + -+ final int minLevel = ChunkLevel.byStatus(chunkStatus); ++ final int minLevel = ChunkLevel.byStatus(targetStatus); + final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(x, z); + -+ final boolean needsFullScheduling = chunkStatus == ChunkStatus.FULL && (chunkHolder == null || !chunkHolder.getChunkStatus().isOrAfter(FullChunkStatus.FULL)); ++ final boolean needsFullScheduling = targetStatus == ChunkStatus.FULL && (chunkHolder == null || !chunkHolder.getChunkStatus().isOrAfter(FullChunkStatus.FULL)); + -+ if ((chunkHolder == null || chunkHolder.getTicketLevel() > minLevel || needsFullScheduling) && !requireChunk) { ++ if ((chunkHolder == null || chunkHolder.getTicketLevel() > minLevel || needsFullScheduling) && !loadOrGenerate) { + return ChunkHolder.UNLOADED_CHUNK_FUTURE; } -- return this.chunkAbsent(visibleChunkIfPresent, i) +- return this.chunkAbsent(chunkHolder, targetTicketLevel) - ? GenerationChunkHolder.UNLOADED_CHUNK_FUTURE -- : visibleChunkIfPresent.scheduleChunkGenerationTask(chunkStatus, this.chunkMap); -- } -+ final ChunkAccess ifPresent = chunkHolder == null ? null : chunkHolder.getChunkIfPresent(chunkStatus); +- : chunkHolder.scheduleChunkGenerationTask(targetStatus, this.chunkMap); ++ final ChunkAccess ifPresent = chunkHolder == null ? null : chunkHolder.getChunkIfPresent(targetStatus); + if (needsFullScheduling || ifPresent == null) { + // schedule + final CompletableFuture> ret = new CompletableFuture<>(); @@ -26016,13 +26762,11 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 + }; + + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().scheduleChunkLoad( -+ x, z, chunkStatus, true, ++ x, z, targetStatus, true, + ca.spottedleaf.concurrentutil.util.Priority.HIGHER, + complete + ); - -- private boolean chunkAbsent(@Nullable ChunkHolder chunkHolder, int status) { -- return chunkHolder == null || chunkHolder.oldTicketLevel > status; // CraftBukkit using oldTicketLevel for isLoaded checks ++ + return ret; + } else { + // can return now @@ -26031,21 +26775,26 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 + // Paper end - rewrite chunk system } + private boolean chunkAbsent(final @Nullable ChunkHolder chunkHolder, final int targetTicketLevel) { +- return chunkHolder == null || chunkHolder.oldTicketLevel > targetTicketLevel; // CraftBukkit - using oldTicketLevel for isLoaded checks ++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system + } + @Override - public boolean hasChunk(int x, int z) { -- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(new ChunkPos(x, z).toLong()); -- int i = ChunkLevel.byStatus(ChunkStatus.FULL); -- return !this.chunkAbsent(visibleChunkIfPresent, i); + public boolean hasChunk(final int x, final int z) { +- ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(new ChunkPos(x, z).pack()); +- int targetTicketLevel = ChunkLevel.byStatus(ChunkStatus.FULL); +- return !this.chunkAbsent(chunkHolder, targetTicketLevel); + return this.getChunkNow(x, z) != null; // Paper - rewrite chunk system } @Override - public @Nullable LightChunk getChunkForLighting(int chunkX, int chunkZ) { -- long packedChunkPos = ChunkPos.asLong(chunkX, chunkZ); -- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(packedChunkPos); -- return visibleChunkIfPresent == null ? null : visibleChunkIfPresent.getChunkIfPresentUnchecked(ChunkStatus.INITIALIZE_LIGHT.getParent()); + public @Nullable LightChunk getChunkForLighting(final int x, final int z) { +- long key = ChunkPos.pack(x, z); +- ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(key); +- return chunkHolder == null ? null : chunkHolder.getChunkIfPresentUnchecked(ChunkStatus.INITIALIZE_LIGHT.getParent()); + // Paper start - rewrite chunk system -+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder newChunkHolder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkX, chunkZ); ++ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder newChunkHolder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(x, z); + if (newChunkHolder == null) { + return null; + } @@ -26054,14 +26803,14 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 } @Override -@@ -357,28 +430,18 @@ public class ServerChunkCache extends ChunkSource { +@@ -359,28 +437,18 @@ public class ServerChunkCache extends ChunkSource { } public boolean runDistanceManagerUpdates() { -- boolean flag = this.distanceManager.runAllUpdates(this.chunkMap); -- boolean flag1 = this.chunkMap.promoteChunkMap(); +- boolean updated = this.distanceManager.runAllUpdates(this.chunkMap); +- boolean promoted = this.chunkMap.promoteChunkMap(); - this.chunkMap.runGenerationTasks(); -- if (!flag && !flag1) { +- if (!updated && !promoted) { - return false; - } else { - this.clearCache(); @@ -26070,35 +26819,35 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 + return ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.processTicketUpdates(); // Paper - rewrite chunk system } - public boolean isPositionTicking(long chunkPos) { -- if (!this.level.shouldTickBlocksAt(chunkPos)) { + public boolean isPositionTicking(final long chunkKey) { +- if (!this.level.shouldTickBlocksAt(chunkKey)) { - return false; - } else { -- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(chunkPos); -- return visibleChunkIfPresent != null && visibleChunkIfPresent.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).isSuccess(); +- ChunkHolder holder = this.getVisibleChunkIfPresent(chunkKey); +- return holder != null && holder.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).isSuccess(); - } + // Paper start - rewrite chunk system -+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder newChunkHolder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos); ++ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder newChunkHolder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkKey); + return newChunkHolder != null && newChunkHolder.isTickingReady(); + // Paper end - rewrite chunk system } - public void save(boolean flush) { + public void save(final boolean flushStorage) { - this.runDistanceManagerUpdates(); + // Paper - rewrite chunk system - this.chunkMap.saveAllChunks(flush); + this.chunkMap.saveAllChunks(flushStorage); } -@@ -389,17 +452,15 @@ public class ServerChunkCache extends ChunkSource { +@@ -391,17 +459,15 @@ public class ServerChunkCache extends ChunkSource { } public void close(boolean save) throws IOException { - if (save) { - this.save(true); - } - // CraftBukkit end + // Paper - rewrite chunk system - this.dataStorage.close(); + // CraftBukkit end + this.savedDataStorage.close(); - this.lightEngine.close(); - this.chunkMap.close(); + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.close(save, true); // Paper - rewrite chunk system @@ -26110,100 +26859,99 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 ProfilerFiller gameprofilerfiller = Profiler.get(); gameprofilerfiller.push("purge"); -@@ -423,6 +484,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -425,6 +491,7 @@ public class ServerChunkCache extends ChunkSource { this.runDistanceManagerUpdates(); - profilerFiller.popPush("chunks"); + profiler.popPush("chunks"); if (tickChunks) { + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getPlayerChunkLoader().tick(); // Paper - rewrite chunk system this.tickChunks(); this.chunkMap.tick(); } -@@ -455,7 +517,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -457,7 +524,7 @@ public class ServerChunkCache extends ChunkSource { profiler.push("broadcast"); for (ChunkHolder chunkHolder : this.chunkHoldersToBroadcast) { -- LevelChunk tickingChunk = chunkHolder.getTickingChunk(); -+ LevelChunk tickingChunk = chunkHolder.getChunkToSend(); // Paper - rewrite chunk system - if (tickingChunk != null) { - chunkHolder.broadcastChanges(tickingChunk); +- LevelChunk chunk = chunkHolder.getTickingChunk(); ++ LevelChunk chunk = chunkHolder.getChunkToSend(); // Paper - rewrite chunk system + if (chunk != null) { + chunkHolder.broadcastChanges(chunk); } -@@ -496,7 +558,10 @@ public class ServerChunkCache extends ChunkSource { +@@ -498,7 +565,10 @@ public class ServerChunkCache extends ChunkSource { profiler.popPush("filteringSpawningChunks"); - this.chunkMap.collectSpawningChunks(list); + this.chunkMap.collectSpawningChunks(spawningChunks); profiler.popPush("shuffleSpawningChunks"); -- Util.shuffle(list, this.level.random); + // Paper start - chunk tick iteration optimisation -+ this.shuffleRandom.setSeed(this.level.random.nextLong()); -+ Util.shuffle(list, this.shuffleRandom); ++ this.shuffleRandom.setSeed(this.level.getRandom().nextLong()); + Util.shuffle(spawningChunks, this.level.getRandom()); + // Paper end - chunk tick iteration optimisation profiler.popPush("tickSpawningChunks"); - for (LevelChunk levelChunk : list) { -@@ -507,7 +572,7 @@ public class ServerChunkCache extends ChunkSource { + for (LevelChunk chunk : spawningChunks) { +@@ -509,7 +579,7 @@ public class ServerChunkCache extends ChunkSource { } profiler.popPush("tickTickingChunks"); -- this.chunkMap.forEachBlockTickingChunk(levelChunk1 -> this.level.tickChunk(levelChunk1, i)); +- this.chunkMap.forEachBlockTickingChunk(chunkx -> this.level.tickChunk(chunkx, tickSpeed)); + this.iterateTickingChunksFaster(); // Paper - chunk tick iteration optimizations - if (flag) { + if (doMobSpawning) { profiler.popPush("customSpawners"); this.level.tickCustomSpawners(this.spawnEnemies); -@@ -519,22 +584,25 @@ public class ServerChunkCache extends ChunkSource { - private void tickSpawningChunk(LevelChunk chunk, long timeInhabited, List spawnCategories, NaturalSpawner.SpawnState spawnState) { - ChunkPos pos = chunk.getPos(); - chunk.incrementInhabitedTime(timeInhabited); -- if (this.distanceManager.inEntityTickingRange(pos.toLong())) { +@@ -523,22 +593,25 @@ public class ServerChunkCache extends ChunkSource { + ) { + ChunkPos chunkPos = chunk.getPos(); + chunk.incrementInhabitedTime(timeDiff); +- if (this.distanceManager.inEntityTickingRange(chunkPos.pack())) { + if (true) { // Paper - rewrite chunk system this.level.tickThunder(chunk); } - if (!spawnCategories.isEmpty()) { -- if (this.level.canSpawnEntitiesInChunk(pos)) { -+ if (this.level.getWorldBorder().isWithinBounds(pos)) { // Paper - rewrite chunk system - NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories); + if (!spawningCategories.isEmpty()) { +- if (this.level.canSpawnEntitiesInChunk(chunkPos)) { ++ if (this.level.getWorldBorder().isWithinBounds(chunkPos)) { // Paper - rewrite chunk system + NaturalSpawner.spawnForChunk(this.level, chunk, spawnCookie, spawningCategories); } } } - private void getFullChunk(long chunkPos, Consumer fullChunkGetter) { -- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(chunkPos); -- if (visibleChunkIfPresent != null) { -- visibleChunkIfPresent.getFullChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).ifSuccess(fullChunkGetter); + private void getFullChunk(final long chunkKey, final Consumer output) { +- ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(chunkKey); +- if (chunkHolder != null) { +- chunkHolder.getFullChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).ifSuccess(output); + // Paper start - rewrite chunk system + // note: bypass currentlyLoaded from getChunkNow -+ final LevelChunk fullChunk = this.fullChunks.get(chunkPos); ++ final LevelChunk fullChunk = this.fullChunks.get(chunkKey); + if (fullChunk != null) { -+ fullChunkGetter.accept(fullChunk); ++ output.accept(fullChunk); } + // Paper end - rewrite chunk system } @Override -@@ -592,17 +660,11 @@ public class ServerChunkCache extends ChunkSource { +@@ -596,17 +669,11 @@ public class ServerChunkCache extends ChunkSource { } - public CompletableFuture addTicketAndLoadWithRadius(TicketType ticketType, ChunkPos chunkPos, int radius) { -- if (!ticketType.doesLoad()) { -- throw new IllegalStateException("Ticket type " + ticketType + " does not trigger chunk loading"); -- } else if (ticketType.canExpireIfUnloaded()) { -- throw new IllegalStateException("Ticket type " + ticketType + " can expire before it loads, cannot fetch asynchronously"); + public CompletableFuture addTicketAndLoadWithRadius(final TicketType type, final ChunkPos pos, final int radius) { +- if (!type.doesLoad()) { +- throw new IllegalStateException("Ticket type " + type + " does not trigger chunk loading"); +- } else if (type.canExpireIfUnloaded()) { +- throw new IllegalStateException("Ticket type " + type + " can expire before it loads, cannot fetch asynchronously"); - } else { -- this.addTicketWithRadius(ticketType, chunkPos, radius); +- this.addTicketWithRadius(type, pos, radius); - this.runDistanceManagerUpdates(); -- ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(chunkPos.toLong()); -- Objects.requireNonNull(visibleChunkIfPresent, "No chunk was scheduled for loading"); -- return this.chunkMap.getChunkRangeFuture(visibleChunkIfPresent, radius, i -> ChunkStatus.FULL); +- ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(pos.pack()); +- Objects.requireNonNull(chunkHolder, "No chunk was scheduled for loading"); +- return this.chunkMap.getChunkRangeFuture(chunkHolder, radius, distance -> ChunkStatus.FULL); - } + // Paper start - rewrite chunk system + return ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.addTicketAndLoadWithRadius( -+ ticketType, chunkPos, radius, ChunkStatus.FULL, ca.spottedleaf.concurrentutil.util.Priority.NORMAL ++ type, pos, radius, ChunkStatus.FULL, ca.spottedleaf.concurrentutil.util.Priority.NORMAL + ); + // Paper end - rewrite chunk system } - public void addTicketWithRadius(TicketType ticketType, ChunkPos chunkPos, int radius) { -@@ -652,6 +714,12 @@ public class ServerChunkCache extends ChunkSource { - this.chunkMap.setServerViewDistance(viewDistance); + public void addTicketWithRadius(final TicketType type, final ChunkPos pos, final int radius) { +@@ -656,6 +723,12 @@ public class ServerChunkCache extends ChunkSource { + this.chunkMap.setServerViewDistance(newDistance); } + // Paper start - rewrite chunk system @@ -26212,13 +26960,13 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 + } + // Paper end - rewrite chunk system + - public void setSimulationDistance(int simulationDistance) { + public void setSimulationDistance(final int simulationDistance) { this.distanceManager.updateSimulationDistance(simulationDistance); } -@@ -736,18 +804,14 @@ public class ServerChunkCache extends ChunkSource { +@@ -736,18 +809,14 @@ public class ServerChunkCache extends ChunkSource { @Override - public boolean pollTask() { + protected boolean pollTask() { - try { // CraftBukkit - process pending Chunk loadCallback() and unloadCallback() after each run task - if (ServerChunkCache.this.runDistanceManagerUpdates()) { + // Paper start - rewrite chunk system @@ -26240,10 +26988,10 @@ index 4bf684986cadfa25ae475be4d54f0359e94b9e11..75fe6e026e3f91b66dcf5b97a86491d5 } } diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index 182e7468a01e76a22c7af3c081469b4f84f87558..b7915685026e9a944f42440e4a95215f927f385e 100644 +index de75dda2e6f17f0ae6e7f6a5620fd3eac9d389e5..6774d12d04fa848624fb8bf7a3c2b8bcb88f6e8f 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java -@@ -88,6 +88,11 @@ public class ServerEntity { +@@ -90,6 +90,11 @@ public class ServerEntity { } public void sendChanges() { @@ -26256,28 +27004,28 @@ index 182e7468a01e76a22c7af3c081469b4f84f87558..b7915685026e9a944f42440e4a95215f List passengers = this.entity.getPassengers(); if (!passengers.equals(this.lastPassengers)) { diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41fc436e1f 100644 +index d0742abe193dfd8eeeea69a53f78d809d26efabe..96e45eab1e8cf857f5068e911070259866d8bdcb 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -181,7 +181,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter; +@@ -182,7 +182,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; --public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLevel { -+public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLevel, ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel, ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader, ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel { // Paper - rewrite chunk system // Paper - chunk tick iteration +-public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGetter { ++public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGetter, ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel, ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader, ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel { // Paper - rewrite chunk system // Paper - chunk tick iteration public static final BlockPos END_SPAWN_POINT = new BlockPos(100, 50, 0); public static final IntProvider RAIN_DELAY = UniformInt.of(12000, 180000); public static final IntProvider RAIN_DURATION = UniformInt.of(12000, 24000); -@@ -197,7 +197,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - final EntityTickList entityTickList = new EntityTickList(); +@@ -198,7 +198,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + private final EntityTickList entityTickList = new EntityTickList(); private final ServerWaypointManager waypointManager; - private final EnvironmentAttributeSystem environmentAttributes; + private EnvironmentAttributeSystem environmentAttributes; - public final PersistentEntitySectionManager entityManager; + // Paper - rewrite chunk system private final GameEventDispatcher gameEventDispatcher; public boolean noSave; private final SleepStatus sleepStatus; -@@ -270,12 +270,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -275,12 +275,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet public final void loadChunksForMoveAsync(AABB box, ca.spottedleaf.concurrentutil.util.Priority priority, java.util.function.Consumer> onLoad) { @@ -26288,10 +27036,10 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 - return; - } + // Paper - rewrite chunk system - int minBlockX = Mth.floor(box.minX - 1.0E-7D) - 3; - int minBlockZ = Mth.floor(box.minZ - 1.0E-7D) - 3; + int minBlockX = Mth.floor(box.minX - 1.0E-7) - 3; + int minBlockZ = Mth.floor(box.minZ - 1.0E-7) - 3; -@@ -294,30 +289,171 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -299,30 +294,171 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet public final void loadChunks(int minChunkX, int minChunkZ, int maxChunkX, int maxChunkZ, ca.spottedleaf.concurrentutil.util.Priority priority, java.util.function.Consumer> onLoad) { @@ -26363,9 +27111,7 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + public final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler moonrise$getChunkTaskScheduler() { + return this.chunkTaskScheduler; + } - -- int requiredChunks = (maxChunkX - minChunkX + 1) * (maxChunkZ - minChunkZ + 1); -- int[] loadedChunks = new int[1]; ++ + @Override + public final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController moonrise$getChunkDataController() { + return this.chunkDataController; @@ -26385,8 +27131,7 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + public final int moonrise$getRegionChunkShift() { + return io.papermc.paper.threadedregions.TickRegions.getRegionChunkShift(); + } - -- java.util.function.Consumer consumer = (net.minecraft.world.level.chunk.ChunkAccess chunk) -> { ++ + @Override + public final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader moonrise$getPlayerChunkLoader() { + return this.chunkLoader; @@ -26417,7 +27162,9 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + chunkStatus, priority, onLoad + ); + } -+ + +- int requiredChunks = (maxChunkX - minChunkX + 1) * (maxChunkZ - minChunkZ + 1); +- int[] loadedChunks = new int[1]; + @Override + public final void moonrise$loadChunksAsync(final int minChunkX, final int maxChunkX, final int minChunkZ, final int maxChunkZ, + final ca.spottedleaf.concurrentutil.util.Priority priority, @@ -26445,10 +27192,11 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + final int ticketLevel = ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.getTicketLevel(chunkStatus); + + final List ret = new ArrayList<>(requiredChunks); -+ + +- java.util.function.Consumer consumer = (net.minecraft.world.level.chunk.ChunkAccess chunk) -> { + final java.util.function.Consumer consumer = (final ChunkAccess chunk) -> { if (chunk != null) { -- int ticketLevel = Math.max(33, chunkProvider.chunkMap.getUpdatingChunkIfPresent(chunk.getPos().toLong()).getTicketLevel()); +- int ticketLevel = Math.max(33, chunkProvider.chunkMap.getUpdatingChunkIfPresent(chunk.getPos().pack()).getTicketLevel()); - ret.add(chunk); - ticketLevels.add(ticketLevel); - chunkProvider.addTicketAtLevel(TicketType.FUTURE_AWAIT, chunk.getPos(), ticketLevel); @@ -26456,11 +27204,11 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + ret.add(chunk); + } + chunkHolderManager.addTicketAtLevel(ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.CHUNK_LOAD, chunk.getPos(), ticketLevel, holderIdentifier); - } -- if (++loadedChunks[0] == requiredChunks) { ++ } + if (onEachLoad != null) { + onEachLoad.accept(chunk); -+ } + } +- if (++loadedChunks[0] == requiredChunks) { + if (loadedChunks.incrementAndGet() == requiredChunks) { try { - onLoad.accept(java.util.Collections.unmodifiableList(ret)); @@ -26479,7 +27227,7 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 } } } -@@ -331,16 +467,135 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -336,16 +472,135 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet } } } @@ -26493,7 +27241,8 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 - return player != null && player.level() == this ? player : null; + public final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder moonrise$getViewDistanceHolder() { + return this.viewDistanceHolder; -+ } + } +- // Paper end - optimise getPlayerByUUID + + @Override + public final long moonrise$getLastMidTickFailure() { @@ -26523,8 +27272,7 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + @Override + public final ca.spottedleaf.moonrise.common.list.ReferenceList moonrise$getEntityTickingChunks() { + return this.entityTickingChunks; - } -- // Paper end - optimise getPlayerByUUID ++ } + + @Override + public final boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) { @@ -26621,38 +27369,38 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + // Paper end - chunk tick iteration public ServerLevel( - MinecraftServer server, -@@ -387,18 +642,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + final MinecraftServer server, +@@ -409,18 +664,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet // CraftBukkit end - boolean flag = server.forceSynchronousWrites(); + boolean syncWrites = server.forceSynchronousWrites(); DataFixer fixerUpper = server.getFixerUpper(); -- EntityPersistentStorage entityPersistentStorage = new EntityStorage( +- EntityPersistentStorage entityStorage = new EntityStorage( - new SimpleRegionStorage( -- new RegionStorageInfo(levelStorageAccess.getLevelId(), dimension, "entities"), -- levelStorageAccess.getDimensionPath(dimension).resolve("entities"), +- new RegionStorageInfo(levelStorage.getLevelId(), dimension, "entities"), +- levelStorage.getDimensionPath(dimension).resolve("entities"), - fixerUpper, -- flag, +- syncWrites, - DataFixTypes.ENTITY_CHUNK - ), - this, - server - ); -- this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entityPersistentStorage); +- this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entityStorage); + // Paper - rewrite chunk system this.chunkSource = new ServerChunkCache( this, - levelStorageAccess, -@@ -409,7 +653,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + levelStorage, +@@ -431,7 +675,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet this.spigotConfig.viewDistance, // Spigot this.spigotConfig.simulationDistance, // Spigot - flag, + syncWrites, - this.entityManager::updateChunkStatus, + null, // Paper - rewrite chunk system () -> server.overworld().getDataStorage() + , savedDataStorage // Paper - initialize SavedDataStorage earlier ); - this.chunkSource.getGeneratorState().ensureStructuresGenerated(); -@@ -449,6 +693,20 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - this.waypointManager = new ServerWaypointManager(); +@@ -471,6 +715,20 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + this.waypointManager = new ServerWaypointManager(this); // Paper - optimize ServerWaypointManager with locator bar disabled this.environmentAttributes = EnvironmentAttributeSystem.builder().addDefaultLayers(this).build(); this.updateSkyBrightness(); + // Paper start - rewrite chunk system @@ -26660,8 +27408,8 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + this.chunkTaskScheduler = new ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler((ServerLevel)(Object)this); + this.entityDataController = new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController( + new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController.EntityRegionFileStorage( -+ new RegionStorageInfo(levelStorageAccess.getLevelId(), dimension, "entities"), -+ levelStorageAccess.getDimensionPath(dimension).resolve("entities"), ++ new RegionStorageInfo(levelStorage.getLevelId(), dimension, "entities"), ++ levelStorage.getDimensionPath(dimension).resolve("entities"), + server.forceSynchronousWrites() + ), + this.chunkTaskScheduler @@ -26672,29 +27420,29 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit } -@@ -580,8 +838,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - profilerFiller.push("checkDespawn"); +@@ -607,8 +865,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + profiler.push("checkDespawn"); entity.checkDespawn(); - profilerFiller.pop(); + profiler.pop(); - if (entity instanceof ServerPlayer -- || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { +- || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().pack())) { + if (true) { // Paper - rewrite chunk system Entity vehicle = entity.getVehicle(); if (vehicle != null) { if (!vehicle.isRemoved() && vehicle.hasPassenger(entity)) { -@@ -605,7 +862,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -632,7 +889,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet } - profilerFiller.push("entityManagement"); + profiler.push("entityManagement"); - this.entityManager.tick(); + // Paper - rewrite chunk system - profilerFiller.pop(); - profilerFiller.push("debugSynchronizers"); + profiler.pop(); + profiler.push("debugSynchronizers"); if (this.debugSynchronizers.hasAnySubscriberFor(DebugSubscriptions.NEIGHBOR_UPDATES)) { -@@ -622,7 +879,10 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -649,7 +906,10 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet @Override - public boolean shouldTickBlocksAt(long chunkPos) { + public boolean shouldTickBlocksAt(final long chunkPos) { - return this.chunkSource.chunkMap.getDistanceManager().inBlockTickingRange(chunkPos); + // Paper start - rewrite chunk system + final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder holder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos); @@ -26703,7 +27451,7 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 } protected void tickTime() { -@@ -657,7 +917,60 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -673,7 +933,60 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList()).forEach(player -> player.stopSleepInBed(false, false)); } @@ -26717,8 +27465,8 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); + + final ChunkPos cpos = chunk.getPos(); -+ final int offsetX = cpos.x << 4; -+ final int offsetZ = cpos.z << 4; ++ final int offsetX = cpos.x() << 4; ++ final int offsetZ = cpos.z() << 4; + + for (int sectionIndex = 0, sectionsLen = sections.length; sectionIndex < sectionsLen; sectionIndex++) { + final int offsetY = (sectionIndex + minSection) << 4; @@ -26759,57 +27507,55 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + } + // Paper end - optimise random ticking + - public void tickChunk(LevelChunk chunk, int randomTickSpeed) { + public void tickChunk(final LevelChunk chunk, final int tickSpeed) { + final ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom simpleRandom = this.simpleRandom; // Paper - optimise random ticking - ChunkPos pos = chunk.getPos(); - int minBlockX = pos.getMinBlockX(); - int minBlockZ = pos.getMinBlockZ(); -@@ -666,7 +979,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + ChunkPos chunkPos = chunk.getPos(); + int minX = chunkPos.getMinBlockX(); + int minZ = chunkPos.getMinBlockZ(); +@@ -682,7 +995,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet if (!this.paperConfig().environment.disableIceAndSnow) { // Paper - Option to disable ice and snow - for (int i = 0; i < randomTickSpeed; i++) { + for (int i = 0; i < tickSpeed; i++) { - if (this.random.nextInt(48) == 0) { -+ if (simpleRandom.nextInt(48) == 0) { // Paper - optimise random ticking - this.tickPrecipitation(this.getBlockRandomPos(minBlockX, 0, minBlockZ, 15)); ++ if (simpleRandom.nextInt(48) == 0) { // Paper - optimise random ticking + this.tickPrecipitation(this.getBlockRandomPos(minX, 0, minZ, 15)); } } -@@ -674,33 +987,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -690,31 +1003,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet - profilerFiller.popPush("tickBlocks"); - if (randomTickSpeed > 0) { + profiler.popPush("tickBlocks"); + if (tickSpeed > 0) { - LevelChunkSection[] sections = chunk.getSections(); - -- for (int i1 = 0; i1 < sections.length; i1++) { -- LevelChunkSection levelChunkSection = sections[i1]; -- if (levelChunkSection.isRandomlyTicking()) { -- int sectionYFromSectionIndex = chunk.getSectionYFromSectionIndex(i1); -- int blockPosCoord = SectionPos.sectionToBlockCoord(sectionYFromSectionIndex); +- for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { +- LevelChunkSection section = sections[sectionIndex]; +- if (section.isRandomlyTicking()) { +- int sectionY = chunk.getSectionYFromSectionIndex(sectionIndex); +- int minYInSection = SectionPos.sectionToBlockCoord(sectionY); - -- for (int i2 = 0; i2 < randomTickSpeed; i2++) { -- BlockPos blockRandomPos = this.getBlockRandomPos(minBlockX, blockPosCoord, minBlockZ, 15); -- profilerFiller.push("randomTick"); -- BlockState blockState = levelChunkSection.getBlockState( -- blockRandomPos.getX() - minBlockX, blockRandomPos.getY() - blockPosCoord, blockRandomPos.getZ() - minBlockZ -- ); +- for (int ix = 0; ix < tickSpeed; ix++) { +- BlockPos pos = this.getBlockRandomPos(minX, minYInSection, minZ, 15); +- profiler.push("randomTick"); +- BlockState blockState = section.getBlockState(pos.getX() - minX, pos.getY() - minYInSection, pos.getZ() - minZ); - if (blockState.isRandomlyTicking()) { -- blockState.randomTick(this, blockRandomPos, this.random); +- blockState.randomTick(this, pos, this.random); - } - - FluidState fluidState = blockState.getFluidState(); - if (fluidState.isRandomlyTicking()) { -- fluidState.randomTick(this, blockRandomPos, this.random); +- fluidState.randomTick(this, pos, this.random); - } - -- profilerFiller.pop(); +- profiler.pop(); - } - } - } -+ this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking ++ this.optimiseRandomTick(chunk, tickSpeed); // Paper - optimise random ticking } - profilerFiller.pop(); -@@ -1013,6 +1300,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - if (fluidState.is(fluid)) { + profiler.pop(); +@@ -1037,6 +1326,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + if (fluidState.is(type)) { fluidState.tick(this, pos, blockState); } + // Paper start - rewrite chunk system @@ -26820,10 +27566,10 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + } - private void tickBlock(BlockPos pos, Block block) { -@@ -1020,6 +1313,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - if (blockState.is(block)) { - blockState.tick(this, pos, this.random); + private void tickBlock(final BlockPos pos, final Block type) { +@@ -1044,6 +1339,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + if (state.is(type)) { + state.tick(this, pos, this.random); } + // Paper start - rewrite chunk system + if ((++this.tickedBlocksOrFluids & 7L) != 0L) { @@ -26834,20 +27580,20 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 } // Paper start - log detailed entity tick information -@@ -1104,6 +1403,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1128,6 +1429,11 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet } - public void save(@Nullable ProgressListener progress, boolean flush, boolean skipSave) { + public void save(final @Nullable ProgressListener progressListener, final boolean flush, final boolean noSave) { + // Paper start - add close param -+ this.save(progress, flush, skipSave, false); ++ this.save(progressListener, flush, noSave, false); + } -+ public void save(@Nullable ProgressListener progress, boolean flush, boolean skipSave, boolean close) { ++ public void save(final @Nullable ProgressListener progressListener, final boolean flush, final boolean noSave, final boolean close) { + // Paper end - add close param ServerChunkCache chunkSource = this.getChunkSource(); - if (!skipSave) { - org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(this.getWorld())); // CraftBukkit -@@ -1116,13 +1420,18 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - progress.progressStage(Component.translatable("menu.savingChunks")); + if (!noSave) { + new org.bukkit.event.world.WorldSaveEvent(this.getWorld()).callEvent(); // CraftBukkit +@@ -1140,13 +1446,18 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + progressListener.progressStage(Component.translatable("menu.savingChunks")); } - chunkSource.save(flush); @@ -26861,17 +27607,17 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + // Paper start - add close param + if (close) { + try { -+ chunkSource.close(!skipSave); ++ chunkSource.close(!noSave); + } catch (IOException never) { + throw new RuntimeException(never); } } + // Paper end - add close param + } - // CraftBukkit start - moved from MinecraftServer#saveAllChunks - ServerLevel serverLevel1 = this; -@@ -1251,7 +1560,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - this.removePlayerImmediately((ServerPlayer)entity, Entity.RemovalReason.DISCARDED); + private void saveLevelData(final boolean sync) { +@@ -1267,7 +1578,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + this.removePlayerImmediately((ServerPlayer)existing, Entity.RemovalReason.DISCARDED); } - this.entityManager.addNewEntity(player); @@ -26879,7 +27625,7 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 } // CraftBukkit start -@@ -1282,7 +1591,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1298,7 +1609,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet } // CraftBukkit end @@ -26888,52 +27634,52 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 } } -@@ -1293,7 +1602,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1309,7 +1620,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet - public boolean tryAddFreshEntityWithPassengers(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { + public boolean tryAddFreshEntityWithPassengers(final Entity entity, final org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { // CraftBukkit end - if (entity.getSelfAndPassengers().map(Entity::getUUID).anyMatch(this.entityManager::isLoaded)) { + if (entity.getSelfAndPassengers().map(Entity::getUUID).anyMatch(this.moonrise$getEntityLookup()::hasEntity)) { // Paper - rewrite chunk system return false; } else { this.addFreshEntityWithPassengers(entity, reason); // CraftBukkit -@@ -2039,7 +2348,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -2103,7 +2414,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet } } -- bufferedWriter.write(String.format(Locale.ROOT, "entities: %s\n", this.entityManager.gatherStats())); -+ bufferedWriter.write(String.format(Locale.ROOT, "entities: %s\n", this.moonrise$getEntityLookup().getDebugInfo())); // Paper - rewrite chunk system - bufferedWriter.write(String.format(Locale.ROOT, "block_entity_tickers: %d\n", this.blockEntityTickers.size())); - bufferedWriter.write(String.format(Locale.ROOT, "block_ticks: %d\n", this.getBlockTicks().count())); - bufferedWriter.write(String.format(Locale.ROOT, "fluid_ticks: %d\n", this.getFluidTicks().count())); -@@ -2057,13 +2366,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - Path path1 = path.resolve("chunks.csv"); - - try (Writer bufferedWriter2 = Files.newBufferedWriter(path1)) { -- chunkMap.dumpChunks(bufferedWriter2); -+ //chunkMap.dumpChunks(bufferedWriter2); // Paper - rewrite chunk system +- output.write(String.format(Locale.ROOT, "entities: %s\n", this.entityManager.gatherStats())); ++ output.write(String.format(Locale.ROOT, "entities: %s\n", this.moonrise$getEntityLookup().getDebugInfo())); // Paper - rewrite chunk system + output.write(String.format(Locale.ROOT, "block_entity_tickers: %d\n", this.blockEntityTickers.size())); + output.write(String.format(Locale.ROOT, "block_ticks: %d\n", this.getBlockTicks().count())); + output.write(String.format(Locale.ROOT, "fluid_ticks: %d\n", this.getFluidTicks().count())); +@@ -2121,13 +2432,13 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + Path chunks = rootDir.resolve("chunks.csv"); + + try (Writer output = Files.newBufferedWriter(chunks)) { +- chunkMap.dumpChunks(output); ++ //chunkMap.dumpChunks(output); // Paper - rewrite chunk system } - Path path2 = path.resolve("entity_chunks.csv"); + Path entityChunks = rootDir.resolve("entity_chunks.csv"); - try (Writer bufferedWriter3 = Files.newBufferedWriter(path2)) { -- this.entityManager.dumpSections(bufferedWriter3); -+ //this.entityManager.dumpSections(bufferedWriter3); // Paper - rewrite chunk system + try (Writer output = Files.newBufferedWriter(entityChunks)) { +- this.entityManager.dumpSections(output); ++ //this.entityManager.dumpSections(output); // Paper - rewrite chunk system } - Path path3 = path.resolve("entities.csv"); -@@ -2159,8 +2468,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + Path entities = rootDir.resolve("entities.csv"); +@@ -2222,8 +2533,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet Locale.ROOT, "players: %s, entities: %s [%s], block_entities: %d [%s], block_ticks: %d, fluid_ticks: %d, chunk_source: %s", this.players.size(), - this.entityManager.gatherStats(), -- getTypeCount(this.entityManager.getEntityGetter().getAll(), entity -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString()), +- getTypeCount(this.entityManager.getEntityGetter().getAll(), e -> e.typeHolder().getRegisteredName()), + this.moonrise$getEntityLookup().getDebugInfo(), // Paper - rewrite chunk system -+ getTypeCount(this.moonrise$getEntityLookup().getAll(), entity -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString()), // Paper - rewrite chunk system ++ getTypeCount(this.moonrise$getEntityLookup().getAll(), e -> e.typeHolder().getRegisteredName()), // Paper - rewrite chunk system this.blockEntityTickers.size(), getTypeCount(this.blockEntityTickers, TickingBlockEntity::getType), this.getBlockTicks().count(), -@@ -2192,15 +2501,25 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -2256,15 +2567,25 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet @Override public LevelEntityGetter getEntities() { org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot @@ -26941,44 +27687,44 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + return this.moonrise$getEntityLookup(); // Paper - rewrite chunk system } - public void addLegacyChunkEntities(Stream entities) { -- this.entityManager.addLegacyChunkEntities(entities); + public void addLegacyChunkEntities(final Stream loaded) { +- this.entityManager.addLegacyChunkEntities(loaded); + // Paper start - add chunkpos param -+ this.addLegacyChunkEntities(entities, null); ++ this.addLegacyChunkEntities(loaded, null); + } -+ public void addLegacyChunkEntities(Stream entities, ChunkPos chunkPos) { ++ public void addLegacyChunkEntities(final Stream loaded, ChunkPos chunkPos) { + // Paper end - add chunkpos param -+ this.moonrise$getEntityLookup().addLegacyChunkEntities(entities.toList(), chunkPos); // Paper - rewrite chunk system ++ this.moonrise$getEntityLookup().addLegacyChunkEntities(loaded.toList(), chunkPos); // Paper - rewrite chunk system } - public void addWorldGenChunkEntities(Stream entities) { -- this.entityManager.addWorldGenChunkEntities(entities); + public void addWorldGenChunkEntities(final Stream loaded) { +- this.entityManager.addWorldGenChunkEntities(loaded); + // Paper start - add chunkpos param -+ this.addWorldGenChunkEntities(entities, null); ++ this.addWorldGenChunkEntities(loaded, null); + } -+ public void addWorldGenChunkEntities(Stream entities, ChunkPos chunkPos) { ++ public void addWorldGenChunkEntities(final Stream loaded, ChunkPos chunkPos) { + // Paper end - add chunkpos param -+ this.moonrise$getEntityLookup().addWorldGenChunkEntities(entities.toList(), chunkPos); // Paper - rewrite chunk system ++ this.moonrise$getEntityLookup().addWorldGenChunkEntities(loaded.toList(), chunkPos); // Paper - rewrite chunk system } - public void startTickingChunk(LevelChunk chunk) { -@@ -2217,8 +2536,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + public void startTickingChunk(final LevelChunk levelChunk) { +@@ -2281,8 +2602,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet - public void waitForEntities(ChunkPos chunkPos, int radius) { - List list = ChunkPos.rangeClosed(chunkPos, radius).toList(); + public void waitForEntities(final ChunkPos centerChunk, final int radius) { + List chunks = ChunkPos.rangeClosed(centerChunk, radius).toList(); - this.server.managedBlock(() -> { - this.entityManager.processPendingLoads(); + this.chunkSource.mainThreadProcessor.managedBlock(() -> { // Paper - rewrite chunk system + //this.entityManager.processPendingLoads(); // Paper - rewrite chunk system - for (ChunkPos chunkPos1 : list) { - if (!this.areEntitiesLoaded(chunkPos1.toLong())) { -@@ -2239,28 +2558,38 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + for (ChunkPos chunk : chunks) { + if (!this.areEntitiesLoaded(chunk.pack())) { +@@ -2303,28 +2624,38 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet @Override public void close() throws IOException { super.close(); - this.entityManager.close(); -+ // Paper - rewrite chunk system ++ //this.entityManager.close(); // Paper - rewrite chunk system } @Override @@ -26987,63 +27733,63 @@ index 061bc47972d9c67375c0ff9461c506625c80108c..7656665352632fc718bea91dcfd3dd41 + return "Chunks[S] W: " + this.chunkSource.gatherStats() + " E: " + this.moonrise$getEntityLookup().getDebugInfo(); // Paper - rewrite chunk system } - public boolean areEntitiesLoaded(long chunkPos) { -- return this.entityManager.areEntitiesLoaded(chunkPos); -+ return this.moonrise$getAnyChunkIfLoaded(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(chunkPos), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(chunkPos)) != null; // Paper - rewrite chunk system + public boolean areEntitiesLoaded(final long chunkKey) { +- return this.entityManager.areEntitiesLoaded(chunkKey); ++ return this.moonrise$getAnyChunkIfLoaded(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(chunkKey), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(chunkKey)) != null; // Paper - rewrite chunk system } - public boolean isPositionTickingWithEntitiesLoaded(long chunkPos) { -- return this.areEntitiesLoaded(chunkPos) && this.chunkSource.isPositionTicking(chunkPos); + public boolean isPositionTickingWithEntitiesLoaded(final long key) { +- return this.areEntitiesLoaded(key) && this.chunkSource.isPositionTicking(key); + // Paper start - rewrite chunk system -+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos); ++ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(key); + // isTicking implies the chunk is loaded, and the chunk is loaded now implies the entities are loaded + return chunkHolder != null && chunkHolder.isTickingReady(); + // Paper end - rewrite chunk system } - public boolean isPositionEntityTicking(BlockPos pos) { -- return this.entityManager.canPositionTick(pos) && this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(ChunkPos.asLong(pos)); + public boolean isPositionEntityTicking(final BlockPos pos) { +- return this.entityManager.canPositionTick(pos) && this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(ChunkPos.pack(pos)); + // Paper start - rewrite chunk system + final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(pos)); + return chunkHolder != null && chunkHolder.isEntityTickingReady(); + // Paper end - rewrite chunk system } - public boolean areEntitiesActuallyLoadedAndTicking(ChunkPos chunkPos) { -- return this.entityManager.isTicking(chunkPos) && this.entityManager.areEntitiesLoaded(chunkPos.toLong()); + public boolean areEntitiesActuallyLoadedAndTicking(final ChunkPos pos) { +- return this.entityManager.isTicking(pos) && this.entityManager.areEntitiesLoaded(pos.pack()); + // Paper start - rewrite chunk system -+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkPos)); ++ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(pos)); + return chunkHolder != null && chunkHolder.isEntityTickingReady(); + // Paper end - rewrite chunk system } - public boolean anyPlayerCloseEnoughForSpawning(BlockPos pos) { -@@ -2277,7 +2606,10 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + public boolean anyPlayerCloseEnoughForSpawning(final BlockPos pos) { +@@ -2341,7 +2672,10 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet } - public boolean canSpawnEntitiesInChunk(ChunkPos chunkPos) { -- return this.entityManager.canPositionTick(chunkPos) && this.getWorldBorder().isWithinBounds(chunkPos); + public boolean canSpawnEntitiesInChunk(final ChunkPos pos) { +- return this.entityManager.canPositionTick(pos) && this.getWorldBorder().isWithinBounds(pos); + // Paper start - rewrite chunk system -+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkPos)); -+ return chunkHolder != null && chunkHolder.isEntityTickingReady() && this.getWorldBorder().isWithinBounds(chunkPos); ++ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(pos)); ++ return chunkHolder != null && chunkHolder.isEntityTickingReady() && this.getWorldBorder().isWithinBounds(pos); + // Paper end - rewrite chunk system } @Override -@@ -2332,7 +2664,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - @Override - public CrashReportCategory fillReportDetails(CrashReport report) { - CrashReportCategory crashReportCategory = super.fillReportDetails(report); -- crashReportCategory.setDetail("Loaded entity count", () -> String.valueOf(this.entityManager.count())); -+ crashReportCategory.setDetail("Loaded entity count", () -> String.valueOf(this.moonrise$getEntityLookup().getEntityCount())); // Paper - rewrite chunk system - return crashReportCategory; - } - +@@ -2389,7 +2723,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + public CrashReportCategory fillReportDetails(final CrashReport report) { + CrashReportCategory category = super.fillReportDetails(report); + WeatherData weatherData = this.getWeatherData(); +- category.setDetail("Loaded entity count", () -> String.valueOf(this.entityManager.count())); ++ category.setDetail("Loaded entity count", () -> String.valueOf(this.moonrise$getEntityLookup().getEntityCount())); // Paper - rewrite chunk system + category.setDetail( + "Server weather", + () -> String.format( diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index b1f60ef5e69c4f85eea5de42288d80fbda569ef6..1776f98636ca53a7cdbd160fbf6c22526fdb26a4 100644 +index 2ffb9c7388e15ec3e3afd3fa50cc0149c0c8910d..41816ca8d4ef3d959d2b88cd4b2eedaaa541e612 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -201,7 +201,7 @@ import net.minecraft.world.scores.criteria.ObjectiveCriteria; +@@ -203,7 +203,7 @@ import net.minecraft.world.scores.criteria.ObjectiveCriteria; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; @@ -27052,7 +27798,7 @@ index b1f60ef5e69c4f85eea5de42288d80fbda569ef6..1776f98636ca53a7cdbd160fbf6c2252 private static final Logger LOGGER = LogUtils.getLogger(); private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32; private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10; -@@ -428,6 +428,36 @@ public class ServerPlayer extends Player { +@@ -449,6 +449,36 @@ public class ServerPlayer extends Player { public com.destroystokyo.paper.event.entity.@Nullable PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent public org.bukkit.event.player.PlayerQuitEvent.@Nullable QuitReason quitReason = null; // Paper - Add API for quit reason; there are a lot of changes to do if we change all methods leading to the event @@ -27086,23 +27832,23 @@ index b1f60ef5e69c4f85eea5de42288d80fbda569ef6..1776f98636ca53a7cdbd160fbf6c2252 + } + // Paper end - rewrite chunk system + - public ServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile, ClientInformation clientInformation) { + public ServerPlayer(final MinecraftServer server, final ServerLevel level, final GameProfile gameProfile, final ClientInformation clientInformation) { super(level, gameProfile); this.server = server; -@@ -450,7 +480,7 @@ public class ServerPlayer extends Player { +@@ -471,7 +501,7 @@ public class ServerPlayer extends Player { @Override - public BlockPos adjustSpawnLocation(ServerLevel level, BlockPos pos) { - CompletableFuture completableFuture = PlayerSpawnFinder.findSpawn(level, pos); -- this.server.managedBlock(completableFuture::isDone); -+ level.chunkSource.mainThreadProcessor.managedBlock(completableFuture::isDone); // Paper - rewrite chunk system - return BlockPos.containing(completableFuture.join()); + public BlockPos adjustSpawnLocation(final ServerLevel level, final BlockPos spawnSuggestion) { + CompletableFuture future = PlayerSpawnFinder.findSpawn(level, spawnSuggestion); +- this.server.managedBlock(future::isDone); ++ level.chunkSource.mainThreadProcessor.managedBlock(future::isDone); // Paper - rewrite chunk system + return BlockPos.containing(future.join()); } diff --git a/net/minecraft/server/level/ThreadedLevelLightEngine.java b/net/minecraft/server/level/ThreadedLevelLightEngine.java -index 6acaf953df4f2775a73ff6b5168e6b7cdfc702f2..c33edee3c8a32507f01fc025135d57af0b41b10d 100644 +index 46ce24ea601b1525834c1165075fc89d7ed8c69b..c050f208f485f67061fe5bf617915cd83860e5f3 100644 --- a/net/minecraft/server/level/ThreadedLevelLightEngine.java +++ b/net/minecraft/server/level/ThreadedLevelLightEngine.java -@@ -23,23 +23,151 @@ import net.minecraft.world.level.lighting.LevelLightEngine; +@@ -23,15 +23,144 @@ import net.minecraft.world.level.lighting.LevelLightEngine; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; @@ -27176,16 +27922,16 @@ index 6acaf953df4f2775a73ff6b5168e6b7cdfc702f2..c33edee3c8a32507f01fc025135d57af + for (final java.util.Iterator iterator = chunks.iterator(); iterator.hasNext();) { + final ChunkPos pos = iterator.next(); + -+ minX = Math.min(pos.x, minX); -+ minZ = Math.min(pos.z, minZ); -+ maxX = Math.max(pos.x, maxX); -+ maxZ = Math.max(pos.z, maxZ); ++ minX = Math.min(pos.x(), minX); ++ minZ = Math.min(pos.z(), minZ); ++ maxX = Math.max(pos.x(), maxX); ++ maxZ = Math.max(pos.z(), maxZ); + + final Long id = ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.getNextChunkRelightId(); + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().chunkHolderManager.addTicketAtLevel(ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.CHUNK_RELIGHT, pos, ca.spottedleaf.moonrise.patches.starlight.light.StarLightInterface.LIGHT_TICKET_LEVEL, id); + ticketIds.put(pos, id); + -+ final ChunkAccess chunk = (ChunkAccess)world.getChunkSource().getChunkForLighting(pos.x, pos.z); ++ final ChunkAccess chunk = (ChunkAccess)world.getChunkSource().getChunkForLighting(pos.x(), pos.z()); + if (chunk == null || !chunk.isLightCorrect() || !chunk.getPersistedStatus().isOrAfter(net.minecraft.world.level.chunk.status.ChunkStatus.LIGHT)) { + // cannot relight this chunk + iterator.remove(); @@ -27203,9 +27949,9 @@ index 6acaf953df4f2775a73ff6b5168e6b7cdfc702f2..c33edee3c8a32507f01fc025135d57af + chunkLightCallback.accept(pos); + } + -+ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().scheduleChunkTask(pos.x, pos.z, () -> { ++ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().scheduleChunkTask(pos.x(), pos.z(), () -> { + final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder( -+ pos.x, pos.z ++ pos.x(), pos.z() + ); + + if (chunkHolder == null) { @@ -27251,9 +27997,10 @@ index 6acaf953df4f2775a73ff6b5168e6b7cdfc702f2..c33edee3c8a32507f01fc025135d57af + // Paper end - rewrite chunk system public ThreadedLevelLightEngine( - LightChunkGetter lightChunkGetter, ChunkMap chunkMap, boolean skyLight, ConsecutiveExecutor consecutiveExecutor, ChunkTaskDispatcher taskDispatcher + final LightChunkGetter lightChunkGetter, +@@ -42,8 +171,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl ) { - super(lightChunkGetter, true, skyLight); + super(lightChunkGetter, true, hasSkyLight); this.chunkMap = chunkMap; - this.taskDispatcher = taskDispatcher; - this.consecutiveExecutor = consecutiveExecutor; @@ -27261,16 +28008,16 @@ index 6acaf953df4f2775a73ff6b5168e6b7cdfc702f2..c33edee3c8a32507f01fc025135d57af } @Override -@@ -53,167 +181,73 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl +@@ -57,164 +185,73 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl @Override - public void checkBlock(BlockPos pos) { -- BlockPos blockPos = pos.immutable(); + public void checkBlock(final BlockPos pos) { +- BlockPos immutable = pos.immutable(); - this.addTask( - SectionPos.blockToSectionCoord(pos.getX()), - SectionPos.blockToSectionCoord(pos.getZ()), - ThreadedLevelLightEngine.TaskType.PRE_UPDATE, -- Util.name(() -> super.checkBlock(blockPos), () -> "checkBlock " + blockPos) +- Util.name(() -> super.checkBlock(immutable), () -> "checkBlock " + immutable) - ); + // Paper start - rewrite chunk system + final BlockPos posCopy = pos.immutable(); @@ -27280,133 +28027,130 @@ index 6acaf953df4f2775a73ff6b5168e6b7cdfc702f2..c33edee3c8a32507f01fc025135d57af + // Paper end - rewrite chunk system } - protected void updateChunkStatus(ChunkPos chunkPos) { -- this.addTask(chunkPos.x, chunkPos.z, () -> 0, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { -- super.retainData(chunkPos, false); -- super.setLightEnabled(chunkPos, false); + protected void updateChunkStatus(final ChunkPos pos) { +- this.addTask(pos.x(), pos.z(), () -> 0, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { +- super.retainData(pos, false); +- super.setLightEnabled(pos, false); - -- for (int lightSection = this.getMinLightSection(); lightSection < this.getMaxLightSection(); lightSection++) { -- super.queueSectionData(LightLayer.BLOCK, SectionPos.of(chunkPos, lightSection), null); -- super.queueSectionData(LightLayer.SKY, SectionPos.of(chunkPos, lightSection), null); +- for (int sectionY = this.getMinLightSection(); sectionY < this.getMaxLightSection(); sectionY++) { +- super.queueSectionData(LightLayer.BLOCK, SectionPos.of(pos, sectionY), null); +- super.queueSectionData(LightLayer.SKY, SectionPos.of(pos, sectionY), null); - } - -- for (int lightSection = this.levelHeightAccessor.getMinSectionY(); lightSection <= this.levelHeightAccessor.getMaxSectionY(); lightSection++) { -- super.updateSectionStatus(SectionPos.of(chunkPos, lightSection), true); +- for (int sectionY = this.levelHeightAccessor.getMinSectionY(); sectionY <= this.levelHeightAccessor.getMaxSectionY(); sectionY++) { +- super.updateSectionStatus(SectionPos.of(pos, sectionY), true); - } -- }, () -> "updateChunkStatus " + chunkPos + " true")); +- }, () -> "updateChunkStatus " + pos + " true")); + // Paper - rewrite chunk system } @Override - public void updateSectionStatus(SectionPos pos, boolean isEmpty) { + public void updateSectionStatus(final SectionPos pos, final boolean sectionEmpty) { - this.addTask( - pos.x(), - pos.z(), - () -> 0, - ThreadedLevelLightEngine.TaskType.PRE_UPDATE, -- Util.name(() -> super.updateSectionStatus(pos, isEmpty), () -> "updateSectionStatus " + pos + " " + isEmpty) +- Util.name(() -> super.updateSectionStatus(pos, sectionEmpty), () -> "updateSectionStatus " + pos + " " + sectionEmpty) - ); + // Paper start - rewrite chunk system + this.queueTaskForSection(pos.getX(), pos.getY(), pos.getZ(), () -> { -+ return ThreadedLevelLightEngine.this.starlight$getLightEngine().sectionChange(pos, isEmpty); ++ return ThreadedLevelLightEngine.this.starlight$getLightEngine().sectionChange(pos, sectionEmpty); + }); + // Paper end - rewrite chunk system } @Override - public void propagateLightSources(ChunkPos chunkPos) { + public void propagateLightSources(final ChunkPos pos) { - this.addTask( -- chunkPos.x, -- chunkPos.z, -- ThreadedLevelLightEngine.TaskType.PRE_UPDATE, -- Util.name(() -> super.propagateLightSources(chunkPos), () -> "propagateLight " + chunkPos) +- pos.x(), pos.z(), ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> super.propagateLightSources(pos), () -> "propagateLight " + pos) - ); + // Paper - rewrite chunk system } @Override - public void setLightEnabled(ChunkPos chunkPos, boolean lightEnabled) { + public void setLightEnabled(final ChunkPos pos, final boolean enable) { - this.addTask( -- chunkPos.x, -- chunkPos.z, +- pos.x(), +- pos.z(), - ThreadedLevelLightEngine.TaskType.PRE_UPDATE, -- Util.name(() -> super.setLightEnabled(chunkPos, lightEnabled), () -> "enableLight " + chunkPos + " " + lightEnabled) +- Util.name(() -> super.setLightEnabled(pos, enable), () -> "enableLight " + pos + " " + enable) - ); -+ // Paper start - rewrite chunk system ++ // Paper - rewrite chunk system } @Override - public void queueSectionData(LightLayer lightLayer, SectionPos sectionPos, @Nullable DataLayer dataLayer) { + public void queueSectionData(final LightLayer layer, final SectionPos pos, final @Nullable DataLayer data) { - this.addTask( -- sectionPos.x(), -- sectionPos.z(), +- pos.x(), +- pos.z(), - () -> 0, - ThreadedLevelLightEngine.TaskType.PRE_UPDATE, -- Util.name(() -> super.queueSectionData(lightLayer, sectionPos, dataLayer), () -> "queueData " + sectionPos) +- Util.name(() -> super.queueSectionData(layer, pos, data), () -> "queueData " + pos) - ); -+ // Paper start - rewrite chunk system ++ // Paper - rewrite chunk system } - private void addTask(int chunkX, int chunkZ, ThreadedLevelLightEngine.TaskType type, Runnable task) { -- this.addTask(chunkX, chunkZ, this.chunkMap.getChunkQueueLevel(ChunkPos.asLong(chunkX, chunkZ)), type, task); + private void addTask(final int chunkX, final int chunkZ, final ThreadedLevelLightEngine.TaskType type, final Runnable runnable) { +- this.addTask(chunkX, chunkZ, this.chunkMap.getChunkQueueLevel(ChunkPos.pack(chunkX, chunkZ)), type, runnable); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - private void addTask(int chunkX, int chunkZ, IntSupplier queueLevelSupplier, ThreadedLevelLightEngine.TaskType type, Runnable task) { + private void addTask(final int chunkX, final int chunkZ, final IntSupplier level, final ThreadedLevelLightEngine.TaskType type, final Runnable runnable) { - this.taskDispatcher.submit(() -> { -- this.lightTasks.add(Pair.of(type, task)); +- this.lightTasks.add(Pair.of(type, runnable)); - if (this.lightTasks.size() >= 1000) { - this.runUpdate(); - } -- }, ChunkPos.asLong(chunkX, chunkZ), queueLevelSupplier); +- }, ChunkPos.pack(chunkX, chunkZ), level); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @Override - public void retainData(ChunkPos pos, boolean retain) { + public void retainData(final ChunkPos pos, final boolean retain) { - this.addTask( -- pos.x, pos.z, () -> 0, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> super.retainData(pos, retain), () -> "retainData " + pos) +- pos.x(), pos.z(), () -> 0, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> super.retainData(pos, retain), () -> "retainData " + pos) - ); + // Paper start - rewrite chunk system } - public CompletableFuture initializeLight(ChunkAccess chunk, boolean lightEnabled) { + public CompletableFuture initializeLight(final ChunkAccess chunk, final boolean lighted) { - ChunkPos pos = chunk.getPos(); -- this.addTask(pos.x, pos.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { +- this.addTask(pos.x(), pos.z(), ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { - LevelChunkSection[] sections = chunk.getSections(); - -- for (int i = 0; i < chunk.getSectionsCount(); i++) { -- LevelChunkSection levelChunkSection = sections[i]; -- if (!levelChunkSection.hasOnlyAir()) { -- int sectionYFromSectionIndex = this.levelHeightAccessor.getSectionYFromSectionIndex(i); -- super.updateSectionStatus(SectionPos.of(pos, sectionYFromSectionIndex), false); +- for (int sectionIndex = 0; sectionIndex < chunk.getSectionsCount(); sectionIndex++) { +- LevelChunkSection section = sections[sectionIndex]; +- if (!section.hasOnlyAir()) { +- int sectionY = this.levelHeightAccessor.getSectionYFromSectionIndex(sectionIndex); +- super.updateSectionStatus(SectionPos.of(pos, sectionY), false); - } - } - }, () -> "initializeLight: " + pos)); - return CompletableFuture.supplyAsync(() -> { -- super.setLightEnabled(pos, lightEnabled); +- super.setLightEnabled(pos, lighted); - super.retainData(pos, false); - return chunk; -- }, task -> this.addTask(pos.x, pos.z, ThreadedLevelLightEngine.TaskType.POST_UPDATE, task)); +- }, r -> this.addTask(pos.x(), pos.z(), ThreadedLevelLightEngine.TaskType.POST_UPDATE, r)); + return CompletableFuture.completedFuture(chunk); // Paper start - rewrite chunk system } - public CompletableFuture lightChunk(ChunkAccess chunk, boolean isLighted) { -- ChunkPos pos = chunk.getPos(); -- chunk.setLightCorrect(false); -- this.addTask(pos.x, pos.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { -- if (!isLighted) { + public CompletableFuture lightChunk(final ChunkAccess centerChunk, final boolean lighted) { +- ChunkPos pos = centerChunk.getPos(); +- centerChunk.setLightCorrect(false); +- this.addTask(pos.x(), pos.z(), ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { +- if (!lighted) { - super.propagateLightSources(pos); - } - - if (SharedConstants.DEBUG_VERBOSE_SERVER_EVENTS) { - LOGGER.debug("LIT {}", pos); - } -- }, () -> "lightChunk " + pos + " " + isLighted)); +- }, () -> "lightChunk " + pos + " " + lighted)); - return CompletableFuture.supplyAsync(() -> { -- chunk.setLightCorrect(true); -- return chunk; -- }, task -> this.addTask(pos.x, pos.z, ThreadedLevelLightEngine.TaskType.POST_UPDATE, task)); +- centerChunk.setLightCorrect(true); +- return centerChunk; +- }, r -> this.addTask(pos.x(), pos.z(), ThreadedLevelLightEngine.TaskType.POST_UPDATE, r)); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @@ -27421,39 +28165,39 @@ index 6acaf953df4f2775a73ff6b5168e6b7cdfc702f2..c33edee3c8a32507f01fc025135d57af } private void runUpdate() { -- int min = Math.min(this.lightTasks.size(), 1000); -- ObjectListIterator> objectListIterator = this.lightTasks.iterator(); +- int totalSize = Math.min(this.lightTasks.size(), 1000); +- ObjectListIterator> iterator = this.lightTasks.iterator(); - -- int i; -- for (i = 0; objectListIterator.hasNext() && i < min; i++) { -- Pair pair = objectListIterator.next(); -- if (pair.getFirst() == ThreadedLevelLightEngine.TaskType.PRE_UPDATE) { -- pair.getSecond().run(); +- int count; +- for (count = 0; iterator.hasNext() && count < totalSize; count++) { +- Pair task = iterator.next(); +- if (task.getFirst() == ThreadedLevelLightEngine.TaskType.PRE_UPDATE) { +- task.getSecond().run(); - } - } - -- objectListIterator.back(i); +- iterator.back(count); - super.runLightUpdates(); - -- for (int var5 = 0; objectListIterator.hasNext() && var5 < min; var5++) { -- Pair pair = objectListIterator.next(); -- if (pair.getFirst() == ThreadedLevelLightEngine.TaskType.POST_UPDATE) { -- pair.getSecond().run(); +- for (int var5 = 0; iterator.hasNext() && var5 < totalSize; var5++) { +- Pair task = iterator.next(); +- if (task.getFirst() == ThreadedLevelLightEngine.TaskType.POST_UPDATE) { +- task.getSecond().run(); - } - -- objectListIterator.remove(); +- iterator.remove(); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public CompletableFuture waitForPendingTasks(int x, int z) { -- return CompletableFuture.runAsync(() -> {}, task -> this.addTask(x, z, ThreadedLevelLightEngine.TaskType.POST_UPDATE, task)); + public CompletableFuture waitForPendingTasks(final int chunkX, final int chunkZ) { +- return CompletableFuture.runAsync(() -> {}, r -> this.addTask(chunkX, chunkZ, ThreadedLevelLightEngine.TaskType.POST_UPDATE, r)); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - static enum TaskType { + private static enum TaskType { diff --git a/net/minecraft/server/level/Ticket.java b/net/minecraft/server/level/Ticket.java -index c86c72b384ade21165d61958119a21f2128ffdf8..481321fe20adc967d41959258e1915f7fbe8e63c 100644 +index 40f97e3657a1672d3865ab915c7f36d3dbf88c6e..b075d7f68a263422cfa1f35b1f896038d3c712dd 100644 --- a/net/minecraft/server/level/Ticket.java +++ b/net/minecraft/server/level/Ticket.java @@ -7,7 +7,7 @@ import net.minecraft.core.registries.BuiltInRegistries; @@ -27463,7 +28207,7 @@ index c86c72b384ade21165d61958119a21f2128ffdf8..481321fe20adc967d41959258e1915f7 -public class Ticket { +public class Ticket implements Comparable, ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicket { // Paper - rewrite chunk system public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group( + i -> i.group( BuiltInRegistries.TICKET_TYPE.byNameCodec().fieldOf("type").forGetter(Ticket::getType), @@ -20,13 +20,55 @@ public class Ticket { private final int ticketLevel; @@ -27520,14 +28264,14 @@ index c86c72b384ade21165d61958119a21f2128ffdf8..481321fe20adc967d41959258e1915f7 + } + // Paper end - rewrite chunk system - public Ticket(TicketType type, int ticketLevel) { + public Ticket(final TicketType type, final int ticketLevel) { // Paper start - add identifier @@ -37,12 +79,12 @@ public class Ticket { // Paper end - add identifier } -- private Ticket(TicketType type, int ticketLevel, long ticksLeft) { -+ public Ticket(TicketType type, int ticketLevel, long ticksLeft) { // Paper - rewrite chunk system - public +- private Ticket(final TicketType type, final int ticketLevel, final long ticksLeft) { ++ public Ticket(final TicketType type, final int ticketLevel, final long ticksLeft) { // Paper - rewrite chunk system - public // Paper start - add identifier this(type, ticketLevel, ticksLeft, null); } @@ -27557,7 +28301,7 @@ index c86c72b384ade21165d61958119a21f2128ffdf8..481321fe20adc967d41959258e1915f7 public TicketType getType() { diff --git a/net/minecraft/server/level/TicketType.java b/net/minecraft/server/level/TicketType.java -index 835d3177b1287cbd7801b0428b19994432c6f78d..6d5bfc6e26932ba1d22b339b4bcadd2cce00c989 100644 +index 4de90a597bd5c5ac639fd228b3494e70a945d7ff..147cd0458d719be6679b6bac2b6617b48d98ce4c 100644 --- a/net/minecraft/server/level/TicketType.java +++ b/net/minecraft/server/level/TicketType.java @@ -7,7 +7,45 @@ import java.lang.annotation.Target; @@ -27663,10 +28407,10 @@ index 835d3177b1287cbd7801b0428b19994432c6f78d..6d5bfc6e26932ba1d22b339b4bcadd2c return this == PLUGIN ? PLUGIN_TYPE_TIMEOUT : this.timeout; } diff --git a/net/minecraft/server/level/WorldGenRegion.java b/net/minecraft/server/level/WorldGenRegion.java -index f7bd29f062baa396ba25d987149159571ee8fa8a..bf5e460b89d7c0fcdcb8826ba693bd23761df430 100644 +index 690bb45bc91e4ca76754ca8f125c844a54484caf..f288ac0ddc60841ea263c74e15a2060e070c84a0 100644 --- a/net/minecraft/server/level/WorldGenRegion.java +++ b/net/minecraft/server/level/WorldGenRegion.java -@@ -78,6 +78,36 @@ public class WorldGenRegion implements WorldGenLevel { +@@ -76,6 +76,36 @@ public class WorldGenRegion implements WorldGenLevel { private final AtomicLong subTickCount = new AtomicLong(); private static final Identifier WORLDGEN_REGION_RANDOM = Identifier.withDefaultNamespace("worldgen_region_random"); @@ -27700,54 +28444,54 @@ index f7bd29f062baa396ba25d987149159571ee8fa8a..bf5e460b89d7c0fcdcb8826ba693bd23 + } + // Paper end - rewrite chunk system + - public WorldGenRegion(ServerLevel level, StaticCache2D cache, ChunkStep generatingStep, ChunkAccess center) { + public WorldGenRegion(final ServerLevel level, final StaticCache2D cache, final ChunkStep generatingStep, final ChunkAccess center) { this.generatingStep = generatingStep; this.cache = cache; diff --git a/net/minecraft/server/network/PlayerChunkSender.java b/net/minecraft/server/network/PlayerChunkSender.java -index f41ddc6f23786d3d3def15aa32ab3782d4839e5f..c65b274b965b95eae33690e63c5da2d5a9f2981a 100644 +index c974b6cafb1f6aa2a57cfdc8a39c887f02f42b1d..ec40f02032f965f548b0c0a29aa9d9bbad6f439b 100644 --- a/net/minecraft/server/network/PlayerChunkSender.java +++ b/net/minecraft/server/network/PlayerChunkSender.java @@ -78,7 +78,7 @@ public class PlayerChunkSender { } } -- private static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) { -+ public static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) { // Paper - rewrite chunk system - public - packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null)); +- private static void sendChunk(final ServerGamePacketListenerImpl connection, final ServerLevel level, final LevelChunk chunk) { ++ public static void sendChunk(final ServerGamePacketListenerImpl connection, final ServerLevel level, final LevelChunk chunk) { // Paper - rewrite chunk system - public + connection.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null)); // Paper start - PlayerChunkLoadEvent if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) { diff --git a/net/minecraft/server/network/config/PrepareSpawnTask.java b/net/minecraft/server/network/config/PrepareSpawnTask.java -index 4016d26304a0bf3894eed2bef72113b2ca2d7a6f..5014840bf5d7ef53a4fea8aa6f3b25eb4033ff96 100644 +index 83af9ee3ba150da85b9b694cd76a5fabb5b2d8ef..1fb40837bd02672850ec9adc2797190df22b33fc 100644 --- a/net/minecraft/server/network/config/PrepareSpawnTask.java +++ b/net/minecraft/server/network/config/PrepareSpawnTask.java -@@ -166,7 +166,7 @@ public class PrepareSpawnTask implements ConfigurationTask { - private Vec2 spawnAngle; // Paper - remove final - private @Nullable CompletableFuture chunkLoadFuture; - private @Nullable CompletableFuture eventFuture; // Paper -- private final ChunkLoadCounter chunkLoadCounter = new ChunkLoadCounter(); -+ private final ChunkLoadCounter chunkLoadCounter = new ca.spottedleaf.moonrise.patches.chunk_system.MoonriseChunkLoadCounter(); // Paper - rewrite chunk system - - Preparing(final ServerLevel spawnLevel, final CompletableFuture spawnPosition, final Vec2 spawnAngle) { +@@ -171,7 +171,7 @@ public class PrepareSpawnTask implements ConfigurationTask { + private Preparing(final ServerLevel spawnLevel, final CompletableFuture spawnPosition, final Vec2 spawnAngle) { + Objects.requireNonNull(PrepareSpawnTask.this); + super(); +- this.chunkLoadCounter = new ChunkLoadCounter(); ++ this.chunkLoadCounter = new ca.spottedleaf.moonrise.patches.chunk_system.MoonriseChunkLoadCounter(); // Paper - rewrite chunk system this.spawnLevel = spawnLevel; -@@ -229,11 +229,7 @@ public class PrepareSpawnTask implements ConfigurationTask { + this.spawnPosition = spawnPosition; + this.spawnAngle = spawnAngle; +@@ -236,11 +236,7 @@ public class PrepareSpawnTask implements ConfigurationTask { } // Paper end - PlayerSpawnLocationEvent - ChunkPos chunkPos = new ChunkPos(BlockPos.containing(vec3)); + ChunkPos spawnChunk = ChunkPos.containing(BlockPos.containing(spawnPosition)); - this.chunkLoadCounter - .track( - this.spawnLevel, -- () -> this.chunkLoadFuture = this.spawnLevel.getChunkSource().addTicketAndLoadWithRadius(TicketType.PLAYER_SPAWN, chunkPos, 3) +- () -> this.chunkLoadFuture = this.spawnLevel.getChunkSource().addTicketAndLoadWithRadius(TicketType.PLAYER_SPAWN, spawnChunk, 3) - ); -+ this.chunkLoadFuture = ((ca.spottedleaf.moonrise.patches.chunk_system.MoonriseChunkLoadCounter)this.chunkLoadCounter).trackLoadWithRadius(this.spawnLevel, chunkPos, 3, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, ca.spottedleaf.concurrentutil.util.Priority.HIGH, () -> { Preparing.this.spawnLevel.getChunkSource().addTicketWithRadius(TicketType.PLAYER_SPAWN, chunkPos, 3); }); ++ this.chunkLoadFuture = ((ca.spottedleaf.moonrise.patches.chunk_system.MoonriseChunkLoadCounter)this.chunkLoadCounter).trackLoadWithRadius(this.spawnLevel, spawnChunk, 3, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, ca.spottedleaf.concurrentutil.util.Priority.HIGH, () -> { Preparing.this.spawnLevel.getChunkSource().addTicketWithRadius(TicketType.PLAYER_SPAWN, spawnChunk, 3); }); // Paper - rewrite chunk system PrepareSpawnTask.this.loadListener.start(LevelLoadListener.Stage.LOAD_PLAYER_CHUNKS, this.chunkLoadCounter.totalChunks()); - PrepareSpawnTask.this.loadListener.updateFocus(this.spawnLevel.dimension(), chunkPos); + PrepareSpawnTask.this.loadListener.updateFocus(this.spawnLevel.dimension(), spawnChunk); } diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index e1a3af77d1566db9ad7a7976d6d69e08b2b32c99..38c30d98a200dd2a5c888cc080e9c77795f0e0c4 100644 +index 0c4de29676732f5e84d7584e1f89d508addc1cc4..0613b053f4a75c0884280011ee092d9d108b3857 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java -@@ -973,8 +973,8 @@ public abstract class PlayerList { - player.connection.send(new ClientboundSetTimePacket(level.getGameTime(), level.getDayTime(), level.getGameRules().get(GameRules.ADVANCE_TIME))); +@@ -983,8 +983,8 @@ public abstract class PlayerList { + player.connection.send(this.server.clockManager().createFullSyncPacket()); player.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getRespawnData())); // Paper start - view distances - player.connection.send(new ClientboundSetChunkCacheRadiusPacket(io.papermc.paper.FeatureHooks.getViewDistance(level))); @@ -27757,26 +28501,26 @@ index e1a3af77d1566db9ad7a7976d6d69e08b2b32c99..38c30d98a200dd2a5c888cc080e9c777 // Paper end - view distances if (level.isRaining()) { // CraftBukkit start - handle player weather -@@ -1195,7 +1195,7 @@ public abstract class PlayerList { +@@ -1201,7 +1201,7 @@ public abstract class PlayerList { - public void setViewDistance(int viewDistance) { + public void setViewDistance(final int viewDistance) { this.viewDistance = viewDistance; - this.broadcastAll(new ClientboundSetChunkCacheRadiusPacket(viewDistance)); + //this.broadcastAll(new ClientboundSetChunkCacheRadiusPacket(viewDistance)); // Paper - rewrite chunk system - for (ServerLevel serverLevel : this.server.getAllLevels()) { - serverLevel.getChunkSource().setViewDistance(viewDistance); -@@ -1204,7 +1204,7 @@ public abstract class PlayerList { + for (ServerLevel level : this.server.getAllLevels()) { + level.getChunkSource().setViewDistance(viewDistance); +@@ -1210,7 +1210,7 @@ public abstract class PlayerList { - public void setSimulationDistance(int simulationDistance) { + public void setSimulationDistance(final int simulationDistance) { this.simulationDistance = simulationDistance; - this.broadcastAll(new ClientboundSetSimulationDistancePacket(simulationDistance)); + //this.broadcastAll(new ClientboundSetSimulationDistancePacket(simulationDistance)); // Paper - rewrite chunk system - for (ServerLevel serverLevel : this.server.getAllLevels()) { - serverLevel.getChunkSource().setSimulationDistance(simulationDistance); + for (ServerLevel level : this.server.getAllLevels()) { + level.getChunkSource().setSimulationDistance(simulationDistance); diff --git a/net/minecraft/util/BitStorage.java b/net/minecraft/util/BitStorage.java -index 32fe9b22e1d3a422dd80c64d61156dbc7241ba20..02502d50f0255f5bbcc0ecb965abb48cc1a112da 100644 +index a7fb2632bd55ed422dea2a559272645c5b422cc1..48f6671ecabfd5a8c9f8151ccb59f9ea9dc201ac 100644 --- a/net/minecraft/util/BitStorage.java +++ b/net/minecraft/util/BitStorage.java @@ -2,7 +2,7 @@ package net.minecraft.util; @@ -27789,7 +28533,7 @@ index 32fe9b22e1d3a422dd80c64d61156dbc7241ba20..02502d50f0255f5bbcc0ecb965abb48c void set(int index, int value); @@ -20,4 +20,22 @@ public interface BitStorage { - void unpack(int[] array); + void unpack(int[] output); BitStorage copy(); + @@ -27812,7 +28556,7 @@ index 32fe9b22e1d3a422dd80c64d61156dbc7241ba20..02502d50f0255f5bbcc0ecb965abb48c + // Paper end - block counting } diff --git a/net/minecraft/util/CrudeIncrementalIntIdentityHashBiMap.java b/net/minecraft/util/CrudeIncrementalIntIdentityHashBiMap.java -index 4d406af549764de9827274cd0875e5a71b63cba2..d134400dcf2dee5a918fd5ededda802086dd5bbb 100644 +index 8df91fc917c22f3b633fe6df2f5311151c1cc54e..8cfb6a17b10130b5ad809698f5b021096e20dca8 100644 --- a/net/minecraft/util/CrudeIncrementalIntIdentityHashBiMap.java +++ b/net/minecraft/util/CrudeIncrementalIntIdentityHashBiMap.java @@ -7,7 +7,7 @@ import java.util.Iterator; @@ -27838,13 +28582,13 @@ index 4d406af549764de9827274cd0875e5a71b63cba2..d134400dcf2dee5a918fd5ededda8020 + } + // Paper end - optimise palette reads + - private CrudeIncrementalIntIdentityHashBiMap(int size) { - this.keys = (K[])(new Object[size]); - this.values = new int[size]; + private CrudeIncrementalIntIdentityHashBiMap(final int capacity) { + this.keys = (K[])(new Object[capacity]); + this.values = new int[capacity]; @@ -87,6 +97,12 @@ public class CrudeIncrementalIntIdentityHashBiMap implements IdMap { - this.byId = crudeIncrementalIntIdentityHashBiMap.byId; - this.nextId = crudeIncrementalIntIdentityHashBiMap.nextId; - this.size = crudeIncrementalIntIdentityHashBiMap.size; + this.byId = resized.byId; + this.nextId = resized.nextId; + this.size = resized.size; + // Paper start - optimise palette reads + final ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData ref = this.reference; + if (ref != null) { @@ -27853,9 +28597,9 @@ index 4d406af549764de9827274cd0875e5a71b63cba2..d134400dcf2dee5a918fd5ededda8020 + // Paper end - optimise palette reads } - public void addMapping(K object, int intKey) { + public void addMapping(final K key, final int id) { diff --git a/net/minecraft/util/SimpleBitStorage.java b/net/minecraft/util/SimpleBitStorage.java -index 82b13c0aa0762ff9aa3a9adb7227531804094712..08af7687cdbf395116c2ae0c40befa518b796846 100644 +index cd9b408afdabb632ceba265c8de6035e1276024f..5e0e33cdbd590e5aee8044f49a562434c230a426 100644 --- a/net/minecraft/util/SimpleBitStorage.java +++ b/net/minecraft/util/SimpleBitStorage.java @@ -208,6 +208,20 @@ public class SimpleBitStorage implements BitStorage { @@ -27876,12 +28620,12 @@ index 82b13c0aa0762ff9aa3a9adb7227531804094712..08af7687cdbf395116c2ae0c40befa51 + private final int mulBits; + // Paper end - optimise bitstorage read/write operations + - public SimpleBitStorage(int bits, int size, int[] data) { + public SimpleBitStorage(final int bits, final int size, final int[] values) { this(bits, size); - int i = 0; + int outputIndex = 0; @@ -261,6 +275,13 @@ public class SimpleBitStorage implements BitStorage { } else { - this.data = new long[i1]; + this.data = new long[requiredLength]; } + // Paper start - optimise bitstorage read/write operations + this.magic = BETTER_MAGIC[this.bits]; @@ -27892,17 +28636,17 @@ index 82b13c0aa0762ff9aa3a9adb7227531804094712..08af7687cdbf395116c2ae0c40befa51 + // Paper end - optimise bitstorage read/write operations } - private int cellIndex(int index) { + private int cellIndex(final int bitIndex) { @@ -269,28 +290,51 @@ public class SimpleBitStorage implements BitStorage { @Override - public final int getAndSet(int index, int value) { // Paper - Perf: Optimize SimpleBitStorage -- int i = this.cellIndex(index); -- long l = this.data[i]; -- int i1 = (index - i * this.valuesPerLong) * this.bits; -- int i2 = (int)(l >> i1 & this.mask); -- this.data[i] = l & ~(this.mask << i1) | (value & this.mask) << i1; -- return i2; + public final int getAndSet(final int index, final int value) { // Paper - Perf: Optimize SimpleBitStorage +- int cellIndex = this.cellIndex(index); +- long cellValue = this.data[cellIndex]; +- int bitIndex = (index - cellIndex * this.valuesPerLong) * this.bits; +- int oldValue = (int)(cellValue >> bitIndex & this.mask); +- this.data[cellIndex] = cellValue & ~(this.mask << bitIndex) | (value & this.mask) << bitIndex; +- return oldValue; + // Paper start - optimise bitstorage read/write operations + final int full = this.magic * index; // 20 bits of magic + 12 bits of index = barely int + final int divQ = full >>> 20; @@ -27922,11 +28666,11 @@ index 82b13c0aa0762ff9aa3a9adb7227531804094712..08af7687cdbf395116c2ae0c40befa51 } @Override - public final void set(int index, int value) { // Paper - Perf: Optimize SimpleBitStorage -- int i = this.cellIndex(index); -- long l = this.data[i]; -- int i1 = (index - i * this.valuesPerLong) * this.bits; -- this.data[i] = l & ~(this.mask << i1) | (value & this.mask) << i1; + public final void set(final int index, final int value) { // Paper - Perf: Optimize SimpleBitStorage +- int cellIndex = this.cellIndex(index); +- long cellValue = this.data[cellIndex]; +- int bitIndex = (index - cellIndex * this.valuesPerLong) * this.bits; +- this.data[cellIndex] = cellValue & ~(this.mask << bitIndex) | (value & this.mask) << bitIndex; + // Paper start - optimise bitstorage read/write operations + final int full = this.magic * index; // 20 bits of magic + 12 bits of index = barely int + final int divQ = full >>> 20; @@ -27944,11 +28688,11 @@ index 82b13c0aa0762ff9aa3a9adb7227531804094712..08af7687cdbf395116c2ae0c40befa51 } @Override - public final int get(int index) { // Paper - Perf: Optimize SimpleBitStorage -- int i = this.cellIndex(index); -- long l = this.data[i]; -- int i1 = (index - i * this.valuesPerLong) * this.bits; -- return (int)(l >> i1 & this.mask); + public final int get(final int index) { // Paper - Perf: Optimize SimpleBitStorage +- int cellIndex = this.cellIndex(index); +- long cellValue = this.data[cellIndex]; +- int bitIndex = (index - cellIndex * this.valuesPerLong) * this.bits; +- return (int)(cellValue >> bitIndex & this.mask); + // Paper start - optimise bitstorage read/write operations + final int full = this.magic * index; // 20 bits of magic + 12 bits of index = barely int + final int divQ = full >>> 20; @@ -28025,22 +28769,22 @@ index 82b13c0aa0762ff9aa3a9adb7227531804094712..08af7687cdbf395116c2ae0c40befa51 + // Paper end - block counting + public static class InitializationException extends RuntimeException { - InitializationException(String message) { + private InitializationException(final String message) { super(message); diff --git a/net/minecraft/util/SortedArraySet.java b/net/minecraft/util/SortedArraySet.java -index 2da05663f659b1e36c2c9ebe5e68b95779aa2789..2f13822f830ccb208a96e0b00f5b28109448ea2f 100644 +index b36eda8e0176ec52eff318d868b7a4cc39d93759..f996f95f42f8afabcfdaffccc61bd3906a63e604 100644 --- a/net/minecraft/util/SortedArraySet.java +++ b/net/minecraft/util/SortedArraySet.java -@@ -8,12 +8,88 @@ import java.util.Iterator; - import java.util.NoSuchElementException; +@@ -9,12 +9,88 @@ import java.util.NoSuchElementException; + import java.util.Objects; import org.jspecify.annotations.Nullable; -public class SortedArraySet extends AbstractSet { +public class SortedArraySet extends AbstractSet implements ca.spottedleaf.moonrise.patches.chunk_system.util.ChunkSystemSortedArraySet { // Paper - rewrite chunk system private static final int DEFAULT_INITIAL_CAPACITY = 10; private final Comparator comparator; - T[] contents; - int size; + private T[] contents; + private int size; + // Paper start - rewrite chunk system + @Override @@ -28118,11 +28862,11 @@ index 2da05663f659b1e36c2c9ebe5e68b95779aa2789..2f13822f830ccb208a96e0b00f5b2810 + } + // Paper end - rewrite chunk system + - private SortedArraySet(int initialCapacity, Comparator comparator) { + private SortedArraySet(final int initialCapacity, final Comparator comparator) { this.comparator = comparator; if (initialCapacity < 0) { diff --git a/net/minecraft/util/ZeroBitStorage.java b/net/minecraft/util/ZeroBitStorage.java -index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b89651948ed8 100644 +index 1ceb7e4a3abd4d9de5133d182d3267d2164918f6..eb2fa32cff6824c14f865c8731df7d082122f62b 100644 --- a/net/minecraft/util/ZeroBitStorage.java +++ b/net/minecraft/util/ZeroBitStorage.java @@ -62,4 +62,22 @@ public class ZeroBitStorage implements BitStorage { @@ -28149,19 +28893,19 @@ index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b896 + // Paper end - block counting } diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286eb3a5f895 100644 +index 3d7bc43e3e367a2328601ac6b74f99361b4be57e..f7b4da570ee28b7128b4d2cd1c67b89e3980bced 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -152,7 +152,7 @@ import org.jetbrains.annotations.Contract; - import org.jspecify.annotations.Nullable; - import org.slf4j.Logger; - --public abstract class Entity implements SyncedDataHolder, DebugValueSource, Nameable, ItemOwner, SlotProvider, EntityAccess, ScoreHolder, DataComponentGetter { -+public abstract class Entity implements SyncedDataHolder, DebugValueSource, Nameable, ItemOwner, SlotProvider, EntityAccess, ScoreHolder, DataComponentGetter, ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity, ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity { // Paper - rewrite chunk system // Paper - optimise entity tracker +@@ -167,7 +167,7 @@ public abstract class Entity + ItemOwner, + SlotProvider, + DebugValueSource, +- TypedInstance> { ++ TypedInstance>, ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity, ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity { // Paper - rewrite chunk system // Paper - optimise entity tracker // CraftBukkit start private static final int CURRENT_LEVEL = 2; static boolean isLevelAtLeast(ValueInput input, int level) { -@@ -161,7 +161,17 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -176,7 +176,17 @@ public abstract class Entity // Paper start - Share random for entities to make them more random public static RandomSource SHARED_RANDOM = new RandomRandomSource(); @@ -28180,7 +28924,7 @@ index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286e private boolean locked = false; @Override -@@ -174,61 +184,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -189,61 +199,7 @@ public abstract class Entity } } @@ -28243,7 +28987,7 @@ index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286e } // Paper end - Share random for entities to make them more random public org.bukkit.event.entity.CreatureSpawnEvent.@Nullable SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason -@@ -419,6 +375,156 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -433,6 +389,156 @@ public abstract class Entity return this.dimensions.makeBoundingBox(x, y, z); } // Paper end @@ -28398,28 +29142,26 @@ index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286e + } + // Paper end - optimise entity tracker - public Entity(EntityType type, Level level) { + public Entity(final EntityType type, final Level level) { this.type = type; -@@ -1389,35 +1495,77 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name - return list.isEmpty() ? distance : -Shapes.collide(Direction.Axis.Y, boundingBox, list, -distance); - } - -- private Vec3 collide(Vec3 vec) { -- AABB boundingBox = this.getBoundingBox(); -- List entityCollisions = this.level().getEntityCollisions(this, boundingBox.expandTowards(vec)); -- Vec3 vec3 = vec.lengthSqr() == 0.0 ? vec : collideBoundingBox(this, vec, boundingBox, this.level(), entityCollisions); -- boolean flag = vec.x != vec3.x; -- boolean flag1 = vec.y != vec3.y; -- boolean flag2 = vec.z != vec3.z; -- boolean flag3 = flag1 && vec.y < 0.0; -- if (this.maxUpStep() > 0.0F && (flag3 || this.onGround()) && (flag || flag2)) { -- AABB aabb = flag3 ? boundingBox.move(0.0, vec3.y, 0.0) : boundingBox; -- AABB aabb1 = aabb.expandTowards(vec.x, this.maxUpStep(), vec.z); -- if (!flag3) { -- aabb1 = aabb1.expandTowards(0.0, -1.0E-5F, 0.0); +@@ -1421,34 +1527,76 @@ public abstract class Entity + } + + private Vec3 collide(final Vec3 movement) { +- AABB aabb = this.getBoundingBox(); +- List entityColliders = this.level().getEntityCollisions(this, aabb.expandTowards(movement)); +- Vec3 movementStep = movement.lengthSqr() == 0.0 ? movement : collideBoundingBox(this, movement, aabb, this.level(), entityColliders); +- boolean xCollision = movement.x != movementStep.x; +- boolean yCollision = movement.y != movementStep.y; +- boolean zCollision = movement.z != movementStep.z; +- boolean onGroundAfterCollision = yCollision && movement.y < 0.0; +- if (this.maxUpStep() > 0.0F && (onGroundAfterCollision || this.onGround()) && (xCollision || zCollision)) { +- AABB groundedAABB = onGroundAfterCollision ? aabb.move(0.0, movementStep.y, 0.0) : aabb; +- AABB stepUpAABB = groundedAABB.expandTowards(movement.x, this.maxUpStep(), movement.z); +- if (!onGroundAfterCollision) { +- stepUpAABB = stepUpAABB.expandTowards(0.0, -1.0E-5F, 0.0); - } -+ // Paper start - optimise collisions -+ private Vec3 collide(Vec3 movement) { ++ // Paper start - optimise collisions + final boolean xZero = movement.x == 0.0; + final boolean yZero = movement.y == 0.0; + final boolean zZero = movement.z == 0.0; @@ -28427,16 +29169,16 @@ index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286e + return movement; + } -- List list = collectColliders(this, this.level, entityCollisions, aabb1); -- float f = (float)vec3.y; -- float[] floats = collectCandidateStepUpHeights(aabb, list, this.maxUpStep(), f); +- List colliders = collectColliders(this, this.level, entityColliders, stepUpAABB); +- float stepHeightToSkip = (float)movementStep.y; +- float[] candidateStepUpHeights = collectCandidateStepUpHeights(groundedAABB, colliders, this.maxUpStep(), stepHeightToSkip); + final AABB currentBox = this.getBoundingBox(); -- for (float f1 : floats) { -- Vec3 vec31 = collideWithShapes(new Vec3(vec.x, f1, vec.z), aabb, list); -- if (vec31.horizontalDistanceSqr() > vec3.horizontalDistanceSqr()) { -- double d = boundingBox.minY - aabb.minY; -- return vec31.subtract(0.0, d, 0.0); +- for (float candidateStepUpHeight : candidateStepUpHeights) { +- Vec3 stepFromGround = collideWithShapes(new Vec3(movement.x, candidateStepUpHeight, movement.z), groundedAABB, colliders); +- if (stepFromGround.horizontalDistanceSqr() > movementStep.horizontalDistanceSqr()) { +- double distanceToGround = aabb.minY - groundedAABB.minY; +- return stepFromGround.subtract(0.0, distanceToGround, 0.0); - } + final List potentialCollisionsVoxel = new ArrayList<>(); + final List potentialCollisionsBB = new ArrayList<>(); @@ -28496,13 +29238,13 @@ index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286e } } -- return vec3; +- return movementStep; + return collided; + // Paper end - optimise collisions } - private static float[] collectCandidateStepUpHeights(AABB box, List colliders, float deltaY, float maxUpStep) { -@@ -2712,21 +2860,110 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name + private static float[] collectCandidateStepUpHeights( +@@ -2738,21 +2886,110 @@ public abstract class Entity } public boolean isInWall() { @@ -28510,15 +29252,15 @@ index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286e if (this.noPhysics) { return false; - } else { -- float f = this.dimensions.width() * 0.8F; -- AABB aabb = AABB.ofSize(this.getEyePosition(), f, 1.0E-6, f); -- return BlockPos.betweenClosedStream(aabb) +- float checkWidth = this.dimensions.width() * 0.8F; +- AABB eyeBb = AABB.ofSize(this.getEyePosition(), checkWidth, 1.0E-6, checkWidth); +- return BlockPos.betweenClosedStream(eyeBb) - .anyMatch( - pos -> { -- BlockState blockState = this.level().getBlockState(pos); -- return !blockState.isAir() -- && blockState.isSuffocating(this.level(), pos) -- && Shapes.joinIsNotEmpty(blockState.getCollisionShape(this.level(), pos).move(pos), Shapes.create(aabb), BooleanOp.AND); +- BlockState state = this.level().getBlockState(pos); +- return !state.isAir() +- && state.isSuffocating(this.level(), pos) +- && Shapes.joinIsNotEmpty(state.getCollisionShape(this.level(), pos).move(pos), Shapes.create(eyeBb), BooleanOp.AND); + } + + final double reducedWith = (double)(this.dimensions.width() * 0.8F); @@ -28623,8 +29365,8 @@ index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286e + // Paper end - optimise collisions } - public InteractionResult interact(Player player, InteractionHand hand) { -@@ -4316,15 +4553,17 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name + public InteractionResult interact(final Player player, final InteractionHand hand, final Vec3 location) { +@@ -4364,15 +4601,17 @@ public abstract class Entity } public Iterable getIndirectPassengers() { @@ -28650,192 +29392,7 @@ index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286e } public int countPlayerPassengers() { -@@ -4473,77 +4712,126 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name - return Mth.lerp(partialTick, this.yRotO, this.yRot); - } - -- public boolean updateFluidHeightAndDoFluidPushing(TagKey fluidTag, double motionScale) { -+ // Paper start - optimise collisions -+ public boolean updateFluidHeightAndDoFluidPushing(final TagKey fluid, final double flowScale) { - if (this.touchingUnloadedChunk()) { - return false; -- } else { -- AABB aabb = this.getBoundingBox().deflate(0.001); -- int floor = Mth.floor(aabb.minX); -- int ceil = Mth.ceil(aabb.maxX); -- int floor1 = Mth.floor(aabb.minY); -- int ceil1 = Mth.ceil(aabb.maxY); -- int floor2 = Mth.floor(aabb.minZ); -- int ceil2 = Mth.ceil(aabb.maxZ); -- double d = 0.0; -- boolean isPushedByFluid = this.isPushedByFluid(); -- boolean flag = false; -- Vec3 vec3 = Vec3.ZERO; -- int i = 0; -- BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); -- -- for (int i1 = floor; i1 < ceil; i1++) { -- for (int i2 = floor1; i2 < ceil1; i2++) { -- for (int i3 = floor2; i3 < ceil2; i3++) { -- mutableBlockPos.set(i1, i2, i3); -- FluidState fluidState = this.level().getFluidState(mutableBlockPos); -- if (fluidState.is(fluidTag)) { -- double d1 = i2 + fluidState.getHeight(this.level(), mutableBlockPos); -- if (d1 >= aabb.minY) { -- flag = true; -- d = Math.max(d1 - aabb.minY, d); -- if (isPushedByFluid) { -- Vec3 flow = fluidState.getFlow(this.level(), mutableBlockPos); -- if (d < 0.4) { -- flow = flow.scale(d); -- } -+ } - -- vec3 = vec3.add(flow); -- i++; -- } -- // CraftBukkit start - store last lava contact location -- if (fluidTag == FluidTags.LAVA) { -- this.lastLavaContact = mutableBlockPos.immutable(); -- } -- // CraftBukkit end -- } -- } -+ final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3); -+ -+ final Level world = this.level; -+ final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(world); -+ -+ final int minBlockX = Mth.floor(boundingBox.minX); -+ final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY)); -+ final int minBlockZ = Mth.floor(boundingBox.minZ); -+ -+ // note: bounds are exclusive in Vanilla, so we subtract 1 - our loop expects bounds to be inclusive -+ final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1; -+ final int maxBlockY = Math.min((ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1); -+ final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1; -+ -+ final boolean isPushable = this.isPushedByFluid(); -+ final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); -+ -+ Vec3 pushVector = Vec3.ZERO; -+ double totalPushes = 0.0; -+ double maxHeightDiff = 0.0; -+ boolean inFluid = false; -+ -+ final int minChunkX = minBlockX >> 4; -+ final int maxChunkX = maxBlockX >> 4; -+ -+ final int minChunkZ = minBlockZ >> 4; -+ final int maxChunkZ = maxBlockZ >> 4; -+ -+ final net.minecraft.world.level.chunk.ChunkSource chunkSource = world.getChunkSource(); -+ -+ final int chunkLenX = maxChunkX - minChunkX + 1; -+ // chunk index = (x - minX) + (maxX-minX+1)*(z - minZ) -+ // = x + (maxX-minX+1)*z - (minX + (maxX-minX+1)*minZ) -+ final int chunkOffset = -(minChunkX + chunkLenX*minChunkZ); -+ // = x + (maxX-minX+1)*z + chunkOffset -+ final net.minecraft.world.level.chunk.LevelChunkSection[][] sections = new net.minecraft.world.level.chunk.LevelChunkSection[chunkLenX * (maxChunkZ - minChunkZ + 1)][]; -+ -+ // init chunks -+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) { -+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) { -+ sections[currChunkX + chunkLenX*currChunkZ + chunkOffset] = chunkSource.getChunk(currChunkX, currChunkZ, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, false).getSections(); -+ } -+ } -+ -+ for (int currX = minBlockX; currX <= maxBlockX; ++currX) { -+ for (int currY = minBlockY; currY <= maxBlockY; ++currY) { -+ for (int currZ = minBlockZ; currZ <= maxBlockZ; ++currZ) { -+ final FluidState fluidState = sections[(currX >> 4) + chunkLenX*(currZ >> 4) + chunkOffset][(currY >> 4) - minSection] -+ .states.get((currX & 15) | ((currZ & 15) << 4) | ((currY & 15) << 8)).getFluidState(); -+ -+ if (fluidState.isEmpty() || !fluidState.is(fluid)) { -+ continue; -+ } -+ -+ mutablePos.set(currX, currY, currZ); -+ -+ // CraftBukkit start - store last lava contact location -+ if (fluid == FluidTags.LAVA) { -+ this.lastLavaContact = mutablePos.immutable(); -+ } -+ // CraftBukkit end -+ -+ final double height = (double)((float)currY + fluidState.getHeight(world, mutablePos)); -+ final double diff = height - boundingBox.minY; -+ -+ if (diff < 0.0) { -+ continue; -+ } -+ -+ inFluid = true; -+ maxHeightDiff = Math.max(maxHeightDiff, diff); -+ -+ if (!isPushable) { -+ continue; -+ } -+ -+ ++totalPushes; -+ -+ final Vec3 flow = fluidState.getFlow(world, mutablePos); -+ -+ if (maxHeightDiff < 0.4) { -+ pushVector = pushVector.add(flow.scale(maxHeightDiff)); -+ } else { -+ pushVector = pushVector.add(flow); - } - } - } -+ } - -- if (vec3.length() > 0.0) { -- if (i > 0) { -- vec3 = vec3.scale(1.0 / i); -- } -+ this.fluidHeight.put(fluid, maxHeightDiff); - -- if (!(this instanceof Player)) { -- vec3 = vec3.normalize(); -- } -+ if (pushVector == Vec3.ZERO) { -+ return inFluid; -+ } - -- Vec3 deltaMovement = this.getDeltaMovement(); -- vec3 = vec3.scale(motionScale); -- double d2 = 0.003; -- if (Math.abs(deltaMovement.x) < 0.003 && Math.abs(deltaMovement.z) < 0.003 && vec3.length() < 0.0045000000000000005) { -- vec3 = vec3.normalize().scale(0.0045000000000000005); -- } -+ // note: totalPushes != 0 as pushVector was changed -+ pushVector = pushVector.scale(1.0 / totalPushes); -+ final Vec3 currMovement = this.getDeltaMovement(); - -- this.setDeltaMovement(this.getDeltaMovement().add(vec3)); -- } -+ if (!((Entity)(Object)this instanceof Player)) { -+ pushVector = pushVector.normalize(); -+ } - -- this.fluidHeight.put(fluidTag, d); -- return flag; -+ pushVector = pushVector.scale(flowScale); -+ if (Math.abs(currMovement.x) < 0.003 && Math.abs(currMovement.z) < 0.003 && pushVector.length() < 0.0045000000000000005) { -+ pushVector = pushVector.normalize().scale(0.0045000000000000005); - } -+ -+ this.setDeltaMovement(currMovement.add(pushVector)); -+ -+ // note: inFluid = true here as pushVector != 0 -+ return true; - } -+ // Paper end - optimise collisions - - public boolean touchingUnloadedChunk() { - AABB aabb = this.getBoundingBox().inflate(1.0); -@@ -4703,6 +4991,15 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -4683,6 +4922,15 @@ public abstract class Entity } public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) { @@ -28851,10 +29408,10 @@ index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286e if (!checkPosition(this, x, y, z)) { return; } -@@ -4852,6 +5149,12 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -4832,6 +5080,12 @@ public abstract class Entity @Override - public final void setRemoved(Entity.RemovalReason removalReason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause + public final void setRemoved(final Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause + // Paper start - rewrite chunk system + if (!((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this.level).moonrise$getEntityLookup().canRemoveEntity((Entity)(Object)this)) { + LOGGER.warn("Entity " + this + " is currently prevented from being removed from the world since it is processing section status updates", new Throwable()); @@ -28864,16 +29421,16 @@ index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286e org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers if (this.removalReason == null) { -@@ -4862,7 +5165,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -4842,7 +5096,7 @@ public abstract class Entity this.stopRiding(); } - this.getPassengers().forEach(Entity::stopRiding); + if (this.removalReason != Entity.RemovalReason.UNLOADED_TO_CHUNK) { this.getPassengers().forEach(Entity::stopRiding); } // Paper - rewrite chunk system - this.levelCallback.onRemove(removalReason); - this.onRemoval(removalReason); + this.levelCallback.onRemove(reason); + this.onRemoval(reason); // Paper start - Folia schedulers -@@ -4896,7 +5199,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -4876,7 +5130,7 @@ public abstract class Entity public boolean shouldBeSaved() { return (this.removalReason == null || this.removalReason.shouldSave()) && !this.isPassenger() @@ -28882,11 +29439,38 @@ index d7f71ef3631c39b440c6576789de4f24459b2df7..34ef15867759ebc7296256a6d5ec286e } @Override +diff --git a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +index 3325e7d4827067a65b176c5ba01aa491abd222a5..a8f2b22436ff0f4bff06b2c5cf01375da755cfde 100644 +--- a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java ++++ b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +@@ -88,12 +88,16 @@ public class AcquirePoi { + return true; + } + }; +- Set, BlockPos>> poiPositions = poiManager.findAllClosestFirstWithType( +- poiType, cacheTest, body.blockPosition(), SCAN_RANGE, PoiManager.Occupancy.HAS_SPACE +- ) +- .limit(5L) +- .filter(px -> validPoi.test(level, (BlockPos)px.getSecond())) +- .collect(Collectors.toSet()); ++ // Paper start - optimise POI searches ++ java.util.List, BlockPos>> poiPositionsRaw = new java.util.ArrayList<>(); ++ ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findNearestPoiPositions(poiManager, poiType, cacheTest, body.blockPosition(), SCAN_RANGE, Double.MAX_VALUE, PoiManager.Occupancy.HAS_SPACE, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING, 5, poiPositionsRaw); ++ Set, BlockPos>> poiPositions = new java.util.HashSet<>(poiPositionsRaw.size()); ++ for (Pair, BlockPos> pair : poiPositionsRaw) { ++ if (validPoi.test(level, pair.getSecond())) { ++ poiPositions.add(pair); ++ } ++ } ++ // Paper end - optimise POI searches + Path path = findPathToPois(body, poiPositions); + if (path != null && path.canReach()) { + BlockPos targetPos = path.getTarget(); diff --git a/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/net/minecraft/world/entity/ai/village/poi/PoiManager.java -index 436da1efde31d5f1103ecbf04cac9c82928c372a..e2b05800736d29f6061a9f2117e51c8a0d5bfeb6 100644 +index a6f79e022af17de6e58e9a80805b607d674c6b85..ab92bb9a3c5c1900e5d77fd3885e2c9dde64d339 100644 --- a/net/minecraft/world/entity/ai/village/poi/PoiManager.java +++ b/net/minecraft/world/entity/ai/village/poi/PoiManager.java -@@ -40,12 +40,137 @@ import net.minecraft.world.level.chunk.storage.SectionStorage; +@@ -41,12 +41,137 @@ import net.minecraft.world.level.chunk.storage.SectionStorage; import net.minecraft.world.level.chunk.storage.SimpleRegionStorage; import org.jspecify.annotations.Nullable; @@ -29010,8 +29594,8 @@ index 436da1efde31d5f1103ecbf04cac9c82928c372a..e2b05800736d29f6061a9f2117e51c8a + + @Override + public final void moonrise$checkConsistency(final net.minecraft.world.level.chunk.ChunkAccess chunk) { -+ final int chunkX = chunk.getPos().x; -+ final int chunkZ = chunk.getPos().z; ++ final int chunkX = chunk.getPos().x(); ++ final int chunkZ = chunk.getPos().z(); + + final int minY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(chunk); + final int maxY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxSection(chunk); @@ -29023,20 +29607,218 @@ index 436da1efde31d5f1103ecbf04cac9c82928c372a..e2b05800736d29f6061a9f2117e51c8a + // Paper end - rewrite chunk system + public PoiManager( - RegionStorageInfo info, - Path folder, -@@ -66,6 +191,7 @@ public class PoiManager extends SectionStorage { + final RegionStorageInfo info, + final Path folder, +@@ -67,6 +192,7 @@ public class PoiManager extends SectionStorage { levelHeightAccessor ); this.distanceTracker = new PoiManager.DistanceTracker(); + this.world = (net.minecraft.server.level.ServerLevel)levelHeightAccessor; // Paper - rewrite chunk system } - public @Nullable PoiRecord add(BlockPos pos, Holder type) { -@@ -198,8 +324,10 @@ public class PoiManager extends SectionStorage { + public @Nullable PoiRecord add(final BlockPos pos, final Holder type) { +@@ -88,18 +214,29 @@ public class PoiManager extends SectionStorage { + public Stream getInSquare( + final Predicate> predicate, final BlockPos center, final int radius, final PoiManager.Occupancy occupancy + ) { +- int chunkRadius = Math.floorDiv(radius, 16) + 1; +- return ChunkPos.rangeClosed(ChunkPos.containing(center), chunkRadius).flatMap(pos -> this.getInChunk(predicate, pos, occupancy)).filter(record -> { +- BlockPos pos = record.getPos(); +- return Math.abs(pos.getX() - center.getX()) <= radius && Math.abs(pos.getZ() - center.getZ()) <= radius; +- }); ++ // Paper start - optimise POI lookup ++ final List ret = new java.util.ArrayList<>(); ++ ++ ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findAnyPoiRecords( ++ (PoiManager)(Object)this, predicate, (Predicate)null, center, radius, Double.MAX_VALUE, occupancy, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING, Integer.MAX_VALUE, ret ++ ); ++ ++ return ret.stream(); ++ // Paper end - optimise POI lookup + } + + public Stream getInRange( + final Predicate> predicate, final BlockPos center, final int radius, final PoiManager.Occupancy occupancy + ) { +- int radiusSqr = radius * radius; +- return this.getInSquare(predicate, center, radius, occupancy).filter(r -> r.getPos().distSqr(center) <= radiusSqr); ++ // Paper start - optimise POI lookup ++ final List ret = new java.util.ArrayList<>(); ++ ++ ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findAnyPoiRecords( ++ (PoiManager)(Object)this, predicate, (Predicate)null, center, radius, (double)((long)radius * (long)radius), occupancy, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING, Integer.MAX_VALUE, ret ++ ); ++ ++ return ret.stream(); ++ // Paper end - optimise POI lookup + } + + @VisibleForDebug +@@ -118,7 +255,15 @@ public class PoiManager extends SectionStorage { + final int radius, + final PoiManager.Occupancy occupancy + ) { +- return this.getInRange(predicate, center, radius, occupancy).map(PoiRecord::getPos).filter(filter); ++ // Paper start - optimise POI lookup ++ final List ret = new java.util.ArrayList<>(); ++ ++ ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findAnyPoiRecords( ++ (PoiManager)(Object)this, predicate, filter, center, radius, (double)((long)radius * (long)radius), occupancy, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING, Integer.MAX_VALUE, ret ++ ); ++ ++ return ret.stream().map(PoiRecord::getPos); ++ // Paper end - optimise POI lookup + } + + public Stream, BlockPos>> findAllWithType( +@@ -128,7 +273,17 @@ public class PoiManager extends SectionStorage { + final int radius, + final PoiManager.Occupancy occupancy + ) { +- return this.getInRange(predicate, center, radius, occupancy).filter(p -> filter.test(p.getPos())).map(p -> Pair.of(p.getPoiType(), p.getPos())); ++ // Paper start - optimise POI lookup ++ final List ret = new java.util.ArrayList<>(); ++ ++ ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findAnyPoiRecords( ++ (PoiManager)(Object)this, predicate, filter, center, radius, (double)((long)radius * (long)radius), occupancy, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING, Integer.MAX_VALUE, ret ++ ); ++ ++ return ret.stream().map((final PoiRecord record) -> { ++ return Pair.of(record.getPoiType(), record.getPos()); ++ }); ++ // Paper end - optimise POI lookup + } + + public Stream, BlockPos>> findAllClosestFirstWithType( +@@ -138,7 +293,21 @@ public class PoiManager extends SectionStorage { + final int radius, + final PoiManager.Occupancy occupancy + ) { +- return this.findAllWithType(predicate, filter, center, radius, occupancy).sorted(Comparator.comparingDouble(p -> p.getSecond().distSqr(center))); ++ // Paper start - optimise POI lookup ++ final List ret = new java.util.ArrayList<>(); ++ ++ ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findAnyPoiRecords( ++ (PoiManager)(Object)this, predicate, filter, center, radius, (double)((long)radius * (long)radius), occupancy, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING, Integer.MAX_VALUE, ret ++ ); ++ ++ ret.sort((final PoiRecord record1, final PoiRecord record2) -> { ++ return ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.compareDistances(center, record1.getPos(), record2.getPos()); ++ }); ++ ++ return ret.stream().map((final PoiRecord record) -> { ++ return Pair.of(record.getPoiType(), record.getPos()); ++ }); ++ // Paper end - optimise POI lookup + } + + public Optional find( +@@ -148,21 +317,31 @@ public class PoiManager extends SectionStorage { + final int radius, + final PoiManager.Occupancy occupancy + ) { +- return this.findAll(predicate, filter, center, radius, occupancy).findFirst(); ++ // Paper start - optimise POI lookup ++ return Optional.ofNullable(ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findAnyPoiPosition((PoiManager)(Object)this, predicate, filter, center, radius, occupancy, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING)); ++ // Paper end - optimise POI lookup + } + + public Optional findClosest( + final Predicate> predicate, final BlockPos center, final int radius, final PoiManager.Occupancy occupancy + ) { +- return this.getInRange(predicate, center, radius, occupancy).map(PoiRecord::getPos).min(Comparator.comparingDouble(pos -> pos.distSqr(center))); ++ // Paper start - optimise POI lookup ++ final PoiRecord closest = ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findNearestPoiRecord( ++ (PoiManager)(Object)this, predicate, null, center, radius, (double)((long)radius * (long)radius), occupancy, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING ++ ); ++ return closest == null ? Optional.empty() : Optional.of(closest.getPos()); ++ // Paper end - optimise POI lookup + } + + public Optional, BlockPos>> findClosestWithType( + final Predicate> predicate, final BlockPos center, final int radius, final PoiManager.Occupancy occupancy + ) { +- return this.getInRange(predicate, center, radius, occupancy) +- .min(Comparator.comparingDouble(r -> r.getPos().distSqr(center))) +- .map(p -> Pair.of(p.getPoiType(), p.getPos())); ++ // Paper start - optimise POI lookup ++ final PoiRecord closest = ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findNearestPoiRecord( ++ (PoiManager)(Object)this, predicate, null, center, radius, (double)((long)radius * (long)radius), occupancy, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING ++ ); ++ return closest == null ? Optional.empty() : Optional.of(Pair.of(closest.getPoiType(), closest.getPos())); ++ // Paper end - optimise POI lookup + } + + public Optional findClosest( +@@ -172,22 +351,29 @@ public class PoiManager extends SectionStorage { + final int radius, + final PoiManager.Occupancy occupancy + ) { +- return this.getInRange(predicate, center, radius, occupancy) +- .map(PoiRecord::getPos) +- .filter(filter) +- .min(Comparator.comparingDouble(pos -> pos.distSqr(center))); ++ // Paper start - optimise POI lookup ++ final PoiRecord closest = ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findNearestPoiRecord( ++ (PoiManager)(Object)this, predicate, filter, center, radius, (double)((long)radius * (long)radius), occupancy, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING ++ ); ++ return closest == null ? Optional.empty() : Optional.of(closest.getPos()); ++ // Paper end - optimise POI lookup } - public int sectionsToVillage(SectionPos sectionPos) { + public Optional take( + final Predicate> predicate, final BiPredicate, BlockPos> filter, final BlockPos center, final int radius + ) { +- return this.getInRange(predicate, center, radius, PoiManager.Occupancy.HAS_SPACE) +- .filter(poi -> filter.test(poi.getPoiType(), poi.getPos())) +- .findFirst() +- .map(r -> { +- r.acquireTicket(); +- return r.getPos(); +- }); ++ // Paper start - optimise POI lookup ++ final PoiRecord record = ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findAnyPoiRecord( ++ (PoiManager)(Object)this, predicate, filter, center, radius, (double)((long)radius * (long)radius), PoiManager.Occupancy.HAS_SPACE, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING ++ ); ++ ++ if (record == null) { ++ return Optional.empty(); ++ } ++ ++ record.acquireTicket(); ++ return Optional.of(record.getPos()); ++ // Paper end - optimise POI lookup + } + + public Optional getRandom( +@@ -198,8 +384,21 @@ public class PoiManager extends SectionStorage { + final int radius, + final RandomSource random + ) { +- List collect = Util.toShuffledList(this.getInRange(predicate, center, radius, occupancy), random); +- return collect.stream().filter(poi -> filter.test(poi.getPos())).findFirst().map(PoiRecord::getPos); ++ // Paper start - optimise POI lookup ++ final List list = new java.util.ArrayList<>(); ++ ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findAnyPoiRecords( ++ (PoiManager)(Object)this, predicate, filter, center, radius, (double)((long)radius * (long)radius), occupancy, ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.LOAD_FOR_SEARCHING, Integer.MAX_VALUE, list ++ ); ++ ++ // the old method shuffled the list and then tried to find the first element in it that ++ // matched positionPredicate, however we moved positionPredicate into the poi search. This means we can avoid a ++ // shuffle entirely, and just pick a random element from list ++ if (list.isEmpty()) { ++ return Optional.empty(); ++ } ++ ++ return Optional.ofNullable(list.get(random.nextInt(list.size())).getPos()); ++ // Paper end - optimise POI lookup + } + + public boolean release(final BlockPos pos) { +@@ -222,8 +421,10 @@ public class PoiManager extends SectionStorage { + } + + public int sectionsToVillage(final SectionPos sectionPos) { - this.distanceTracker.runAllUpdates(); - return this.distanceTracker.getLevel(sectionPos.asLong()); + // Paper start - rewrite chunk system @@ -29045,21 +29827,21 @@ index 436da1efde31d5f1103ecbf04cac9c82928c372a..e2b05800736d29f6061a9f2117e51c8a + // Paper end - rewrite chunk system } - boolean isVillageCenter(long chunkPos) { -@@ -213,19 +341,26 @@ public class PoiManager extends SectionStorage { + private boolean isVillageCenter(final long sectionPos) { +@@ -234,19 +435,26 @@ public class PoiManager extends SectionStorage { @Override - public void tick(BooleanSupplier aheadOfTime) { -- super.tick(aheadOfTime); + public void tick(final BooleanSupplier haveTime) { +- super.tick(haveTime); - this.distanceTracker.runAllUpdates(); + this.villageDistanceTracker.propagateUpdates(); // Paper - rewrite chunk system } @Override -- protected void setDirty(long sectionPos) { +- protected void setDirty(final long sectionPos) { - super.setDirty(sectionPos); - this.distanceTracker.update(sectionPos, this.distanceTracker.getLevelFromSource(sectionPos), false); -+ public void setDirty(long sectionPos) { // Paper - public ++ public void setDirty(final long sectionPos) { // Paper - public + // Paper start - rewrite chunk system + final int chunkX = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionX(sectionPos); + final int chunkZ = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionZ(sectionPos); @@ -29073,23 +29855,23 @@ index 436da1efde31d5f1103ecbf04cac9c82928c372a..e2b05800736d29f6061a9f2117e51c8a } @Override - protected void onSectionLoad(long sectionKey) { -- this.distanceTracker.update(sectionKey, this.distanceTracker.getLevelFromSource(sectionKey), false); -+ this.updateDistanceTracking(sectionKey); // Paper - rewrite chunk system + protected void onSectionLoad(final long sectionPos) { +- this.distanceTracker.update(sectionPos, this.distanceTracker.getLevelFromSource(sectionPos), false); ++ this.updateDistanceTracking(sectionPos); // Paper - rewrite chunk system } - public void checkConsistencyWithBlocks(SectionPos sectionPos, LevelChunkSection levelChunkSection) { -@@ -264,7 +399,7 @@ public class PoiManager extends SectionStorage { - .map(sectionPos -> Pair.of(sectionPos, this.getOrLoad(sectionPos.asLong()))) - .filter(pair -> !pair.getSecond().map(PoiSection::isValid).orElse(false)) - .map(pair -> pair.getFirst().chunk()) -- .filter(chunkPos -> this.loadedChunks.add(chunkPos.toLong())) + public void checkConsistencyWithBlocks(final SectionPos sectionPos, final LevelChunkSection blockSection) { +@@ -285,7 +493,7 @@ public class PoiManager extends SectionStorage { + .map(pos -> Pair.of(pos, this.getOrLoad(pos.asLong()))) + .filter(poiSection -> !poiSection.getSecond().map(PoiSection::isValid).orElse(false)) + .map(p -> p.getFirst().chunk()) +- .filter(pos -> this.loadedChunks.add(pos.pack())) + // Paper - rewrite chunk system - .forEach(chunkPos -> level.getChunk(chunkPos.x, chunkPos.z, ChunkStatus.EMPTY)); + .forEach(pos -> reader.getChunk(pos.x(), pos.z(), ChunkStatus.EMPTY)); } diff --git a/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/net/minecraft/world/entity/ai/village/poi/PoiSection.java -index 080fcef99f5407685181c2fb7bdb41b14481f6f7..f209696e0158df5cd32e9949b3054452d6a3e5cd 100644 +index 9423fab0cefba9c1d48ab5e8f39fe91c1c3a2022..e9fd001d96ba9f80e5131a940c46562f896dac41 100644 --- a/net/minecraft/world/entity/ai/village/poi/PoiSection.java +++ b/net/minecraft/world/entity/ai/village/poi/PoiSection.java @@ -25,13 +25,27 @@ import net.minecraft.util.debug.DebugPoiInfo; @@ -29100,7 +29882,8 @@ index 080fcef99f5407685181c2fb7bdb41b14481f6f7..f209696e0158df5cd32e9949b3054452 +public class PoiSection implements ca.spottedleaf.moonrise.patches.chunk_system.level.poi.ChunkSystemPoiSection { // Paper - rewrite chunk system private static final Logger LOGGER = LogUtils.getLogger(); private final Short2ObjectMap records = new Short2ObjectOpenHashMap<>(); - private final Map, Set> byType = Maps.newHashMap(); +- private final Map, Set> byType = Maps.newHashMap(); ++ public final Map, Set> byType = Maps.newHashMap(); // Paper - public private final Runnable setDirty; private boolean isValid; @@ -29118,14 +29901,14 @@ index 080fcef99f5407685181c2fb7bdb41b14481f6f7..f209696e0158df5cd32e9949b3054452 + } + // Paper end - rewrite chunk system + - public PoiSection(Runnable setDirty) { + public PoiSection(final Runnable setDirty) { this(setDirty, true, ImmutableList.of()); } diff --git a/net/minecraft/world/entity/decoration/ArmorStand.java b/net/minecraft/world/entity/decoration/ArmorStand.java -index 8a6dfb642c89264386ad9302a8bb190ce5e23a26..79ae039d1908830e81912d230b3540a4920e2017 100644 +index b9cd4bdd48d3c9fc96838d051d18d1c264b25fee..fc4fce0b1d57a62c1e5c80a5b91c2b1a77d4bace 100644 --- a/net/minecraft/world/entity/decoration/ArmorStand.java +++ b/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -204,7 +204,7 @@ public class ArmorStand extends LivingEntity { +@@ -206,7 +206,7 @@ public class ArmorStand extends LivingEntity { @Override protected void pushEntities() { if (!this.level().paperConfig().entities.armorStands.doCollisionEntityLookups) return; // Paper - Option to prevent armor stands from doing entity lookups @@ -29135,7 +29918,7 @@ index 8a6dfb642c89264386ad9302a8bb190ce5e23a26..79ae039d1908830e81912d230b3540a4 entity.push(this); } diff --git a/net/minecraft/world/level/ClipContext.java b/net/minecraft/world/level/ClipContext.java -index 3d317880a257bf431f110048d305ddd2a06880d7..25f2e74e229fa12a2d7f1ff43c6cb12a84788ba2 100644 +index 53dc55780adc88e4857cd1fe72c81535f5e0fd26..35cf8c2caf8b17d233d77b4baa1ea161f0bbfefa 100644 --- a/net/minecraft/world/level/ClipContext.java +++ b/net/minecraft/world/level/ClipContext.java @@ -22,7 +22,7 @@ public class ClipContext { @@ -29146,9 +29929,9 @@ index 3d317880a257bf431f110048d305ddd2a06880d7..25f2e74e229fa12a2d7f1ff43c6cb12a + public final ClipContext.Fluid fluid; // Paper - optimise collisions - public private final CollisionContext collisionContext; - public ClipContext(Vec3 from, Vec3 to, ClipContext.Block block, ClipContext.Fluid fluid, Entity entity) { + public ClipContext(final Vec3 from, final Vec3 to, final ClipContext.Block block, final ClipContext.Fluid fluid, final Entity entity) { diff --git a/net/minecraft/world/level/EntityGetter.java b/net/minecraft/world/level/EntityGetter.java -index b367eb61283c0cbf4ce9cda67089de2e22c95f4f..6ae3698e8e2c84fb21c0a4facbbf1568dbd45405 100644 +index e07a7bda45146686a6ac2d20507df9fc8630514f..f84e651e2a34e76a0a71993332e4be1b7ef53424 100644 --- a/net/minecraft/world/level/EntityGetter.java +++ b/net/minecraft/world/level/EntityGetter.java @@ -15,7 +15,7 @@ import net.minecraft.world.phys.shapes.Shapes; @@ -29157,15 +29940,13 @@ index b367eb61283c0cbf4ce9cda67089de2e22c95f4f..6ae3698e8e2c84fb21c0a4facbbf1568 -public interface EntityGetter { +public interface EntityGetter extends ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter { // Paper - rewrite chunk system - List getEntities(@Nullable Entity entity, AABB area, Predicate predicate); + List getEntities(@Nullable Entity except, AABB bb, Predicate selector); - List getEntities(EntityTypeTest entityTypeTest, AABB bounds, Predicate predicate); -@@ -30,21 +30,44 @@ public interface EntityGetter { - return this.getEntities(entity, area, EntitySelector.NO_SPECTATORS); + List getEntities(final EntityTypeTest type, final AABB bb, final Predicate selector); +@@ -30,45 +30,86 @@ public interface EntityGetter { + return this.getEntities(except, bb, EntitySelector.NO_SPECTATORS); } -- default boolean isUnobstructed(@Nullable Entity entity, VoxelShape shape) { -- if (shape.isEmpty()) { + // Paper start - rewrite chunk system + @Override + default List moonrise$getHardCollidingEntities(final Entity entity, final AABB box, final Predicate predicate) { @@ -29173,35 +29954,35 @@ index b367eb61283c0cbf4ce9cda67089de2e22c95f4f..6ae3698e8e2c84fb21c0a4facbbf1568 + } + // Paper end - rewrite chunk system + -+ // Paper start - optimise collisions -+ default boolean isUnobstructed(@Nullable Entity entity, VoxelShape voxel) { -+ if (voxel.isEmpty()) { + default boolean isUnobstructed(final @Nullable Entity source, final VoxelShape shape) { ++ // Paper start - optimise collisions + if (shape.isEmpty()) { return true; - } else { -- for (Entity entity1 : this.getEntities(entity, shape.bounds())) { -- if (!entity1.isRemoved() -- && entity1.blocksBuilding -- && (entity == null || !entity1.isPassengerOfSameVehicle(entity)) -- && Shapes.joinIsNotEmpty(shape, Shapes.create(entity1.getBoundingBox()), BooleanOp.AND)) { +- for (Entity entity : this.getEntities(source, shape.bounds())) { +- if (!entity.isRemoved() +- && entity.blocksBuilding +- && (source == null || !entity.isPassengerOfSameVehicle(source)) +- && Shapes.joinIsNotEmpty(shape, Shapes.create(entity.getBoundingBox()), BooleanOp.AND)) { - return false; + } + -+ final AABB singleAABB = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation(); ++ final AABB singleAABB = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)shape).moonrise$getSingleAABBRepresentation(); + final List entities = this.getEntities( -+ entity, -+ singleAABB == null ? voxel.bounds() : singleAABB.inflate(-ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON, -ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON, -ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) ++ source, ++ singleAABB == null ? shape.bounds() : singleAABB.inflate(-ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON, -ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON, -ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) + ); + + for (int i = 0, len = entities.size(); i < len; ++i) { + final Entity otherEntity = entities.get(i); + -+ if (otherEntity.isRemoved() || !otherEntity.blocksBuilding || (entity != null && otherEntity.isPassengerOfSameVehicle(entity))) { ++ if (otherEntity.isRemoved() || !otherEntity.blocksBuilding || (source != null && otherEntity.isPassengerOfSameVehicle(source))) { + continue; + } + + if (singleAABB == null) { + final AABB entityBB = otherEntity.getBoundingBox(); -+ if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isEmpty(entityBB) || !ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersectNoEmpty(voxel, entityBB)) { ++ if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isEmpty(entityBB) || !ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersectNoEmpty(shape, entityBB)) { + continue; } } @@ -29214,16 +29995,17 @@ index b367eb61283c0cbf4ce9cda67089de2e22c95f4f..6ae3698e8e2c84fb21c0a4facbbf1568 + // Paper end - optimise collisions } - default List getEntitiesOfClass(Class entityClass, AABB area) { -@@ -52,23 +75,41 @@ public interface EntityGetter { + default List getEntitiesOfClass(final Class baseClass, final AABB bb) { + return this.getEntitiesOfClass(baseClass, bb, EntitySelector.NO_SPECTATORS); } - default List getEntityCollisions(@Nullable Entity entity, AABB collisionBox) { -- if (collisionBox.getSize() < 1.0E-7) { +- default List getEntityCollisions(final @Nullable Entity source, final AABB testArea) { +- if (testArea.getSize() < 1.0E-7) { - return List.of(); ++ default List getEntityCollisions(final @Nullable Entity source, AABB testArea) { // Paper - optimise collisions - remove final + // Paper start - optimise collisions + // first behavior change is to correctly check for empty AABB -+ if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isEmpty(collisionBox)) { ++ if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isEmpty(testArea)) { + // reduce indirection by always returning type with same class + return new java.util.ArrayList<>(); + } @@ -29231,26 +30013,26 @@ index b367eb61283c0cbf4ce9cda67089de2e22c95f4f..6ae3698e8e2c84fb21c0a4facbbf1568 + // to comply with vanilla intersection rules, expand by -epsilon so that we only get stuff we definitely collide with. + // Vanilla for hard collisions has this backwards, and they expand by +epsilon but this causes terrible problems + // specifically with boat collisions. -+ collisionBox = collisionBox.inflate(-ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON, -ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON, -ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON); ++ testArea = testArea.inflate(-ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON, -ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON, -ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON); + + final List entities; -+ if (entity != null && ((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity) entity).moonrise$isHardColliding()) { -+ entities = this.getEntities(entity, collisionBox, null); ++ if (source != null && ((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)source).moonrise$isHardColliding()) { ++ entities = this.getEntities(source, testArea, null); } else { -- Predicate predicate = entity == null ? EntitySelector.CAN_BE_COLLIDED_WITH : EntitySelector.NO_SPECTATORS.and(entity::canCollideWith); -- List entities = this.getEntities(entity, collisionBox.inflate(1.0E-7), predicate); -- if (entities.isEmpty()) { +- Predicate canCollide = source == null ? EntitySelector.CAN_BE_COLLIDED_WITH : EntitySelector.NO_SPECTATORS.and(source::canCollideWith); +- List collidingEntities = this.getEntities(source, testArea.inflate(1.0E-7), canCollide); +- if (collidingEntities.isEmpty()) { - return List.of(); - } else { -- Builder builder = ImmutableList.builderWithExpectedSize(entities.size()); +- Builder shapes = ImmutableList.builderWithExpectedSize(collidingEntities.size()); - -- for (Entity entity1 : entities) { -- builder.add(Shapes.create(entity1.getBoundingBox())); +- for (Entity entity : collidingEntities) { +- shapes.add(Shapes.create(entity.getBoundingBox())); - } -+ entities = ((ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter) this).moonrise$getHardCollidingEntities(entity, collisionBox, null); ++ entities = ((ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter)this).moonrise$getHardCollidingEntities(source, testArea, null); + } -- return builder.build(); +- return shapes.build(); + final List ret = new java.util.ArrayList<>(Math.min(25, entities.size())); + + for (int i = 0, len = entities.size(); i < len; ++i) { @@ -29260,7 +30042,7 @@ index b367eb61283c0cbf4ce9cda67089de2e22c95f4f..6ae3698e8e2c84fb21c0a4facbbf1568 + continue; + } + -+ if ((entity == null && otherEntity.canBeCollidedWith(entity)) || (entity != null && entity.canCollideWith(otherEntity))) { ++ if ((source == null && otherEntity.canBeCollidedWith(source)) || (source != null && source.canCollideWith(otherEntity))) { + ret.add(Shapes.create(otherEntity.getBoundingBox())); } } @@ -29271,10 +30053,10 @@ index b367eb61283c0cbf4ce9cda67089de2e22c95f4f..6ae3698e8e2c84fb21c0a4facbbf1568 // Paper start - Affects Spawning API diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java -index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d71c9dc768 100644 +index b014cb670dc089be42c8546afa2a86625a8c0a82..2569893356946511fc8c29b51220014519f7bad1 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java -@@ -82,6 +82,7 @@ import net.minecraft.world.level.storage.LevelData; +@@ -87,6 +87,7 @@ import net.minecraft.world.level.storage.LevelData; import net.minecraft.world.level.storage.WritableLevelData; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -29282,7 +30064,7 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 import net.minecraft.world.scores.Scoreboard; import org.apache.commons.lang3.mutable.MutableBoolean; import org.jspecify.annotations.Nullable; -@@ -96,7 +97,7 @@ import org.bukkit.craftbukkit.block.data.CraftBlockData; +@@ -101,7 +102,7 @@ import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.entity.SpawnCategory; // CraftBukkit end @@ -29291,22 +30073,22 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 public static final Codec> RESOURCE_KEY_CODEC = ResourceKey.codec(Registries.DIMENSION); public static final ResourceKey OVERWORLD = ResourceKey.create(Registries.DIMENSION, Identifier.withDefaultNamespace("overworld")); public static final ResourceKey NETHER = ResourceKey.create(Registries.DIMENSION, Identifier.withDefaultNamespace("the_nether")); -@@ -124,7 +125,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -129,7 +130,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public float rainLevel; protected float oThunderLevel; public float thunderLevel; -- public final RandomSource random = RandomSource.create(); -+ public final RandomSource random = new ca.spottedleaf.moonrise.common.util.ThreadUnsafeRandom(net.minecraft.world.level.levelgen.RandomSupport.generateUniqueSeed()); // Paper - replace random +- protected final RandomSource random = RandomSource.create(); ++ protected final RandomSource random = new ca.spottedleaf.moonrise.common.util.ThreadUnsafeRandom(net.minecraft.world.level.levelgen.RandomSupport.generateUniqueSeed()); // Paper - replace random @Deprecated - private final RandomSource threadSafeRandom = RandomSource.createThreadSafe(); + private final RandomSource soundSeedGenerator = RandomSource.createThreadSafe(); private final Holder dimensionTypeRegistration; -@@ -187,6 +188,629 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -191,6 +192,629 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public abstract ResourceKey getTypeKey(); + // Paper start - rewrite chunk system + private ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup entityLookup; -+ private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable chunkData = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>(); ++ private final ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2ReferenceHashTable chunkData = new ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2ReferenceHashTable<>(); + + @Override + public final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup moonrise$getEntityLookup() { @@ -29928,9 +30710,9 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 + // Paper end - optimise random ticking + protected Level( - WritableLevelData levelData, - ResourceKey dimension, -@@ -202,6 +826,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + final WritableLevelData levelData, + final ResourceKey dimension, +@@ -207,6 +831,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable { java.util.function.Function paperWorldConfigCreator // Paper - create paper world config ) { @@ -29943,10 +30725,10 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 + this.maxSectionY = this.maxY >> 4; + this.sectionsCount = this.maxSectionY - this.minSectionY + 1; + // Paper end - getblock optimisations - cache world height/sections - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(bukkitName); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config this.generator = generator; -@@ -224,6 +857,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -229,6 +862,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { this.registryAccess = registryAccess; this.palettedContainerFactory = PalettedContainerFactory.create(registryAccess); this.damageSources = new DamageSources(registryAccess); @@ -29954,7 +30736,7 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 } // Paper start - Cancel hit for vanished players -@@ -505,7 +1139,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -510,7 +1144,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { this.setBlocksDirty(pos, blockState, blockState1); } @@ -29963,18 +30745,18 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 this.sendBlockUpdated(pos, blockState, state, flags); } -@@ -756,6 +1390,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - // Spigot start - boolean runsNormally = this.tickRateManager().runsNormally(); +@@ -843,6 +1477,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + // Paper - Fix MC-117075 use removeAll - remove iterator in favour of indexed for loop, ensuring compile error if something uses iter incorrectly + boolean tickBlockEntities = this.tickRateManager().runsNormally(); + int tickedEntities = 0; // Paper - rewrite chunk system - var toRemove = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet(); // Paper - Fix MC-117075; use removeAll - toRemove.add(null); // Paper - Fix MC-117075 - for (this.tileTickPosition = 0; this.tileTickPosition < this.blockEntityTickers.size(); this.tileTickPosition++) { // Paper - Disable tick limiters -@@ -765,6 +1400,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - toRemove.add(tickingBlockEntity); // Paper - Fix MC-117075; use removeAll - } else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { - tickingBlockEntity.tick(); + // Paper start - Fix MC-117075 use removeAll + final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<@Nullable TickingBlockEntity> toRemove = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); + toRemove.add(null); +@@ -853,6 +1488,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + toRemove.add(ticker); // Paper - Fix MC-117075 use removeAll + } else if (tickBlockEntities && this.shouldTickBlocksAt(ticker.getPos())) { + ticker.tick(); + // Paper start - rewrite chunk system + if ((++tickedEntities & 7) == 0) { + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks(); @@ -29982,8 +30764,8 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 + // Paper end - rewrite chunk system } } - this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075 -@@ -784,6 +1424,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + +@@ -872,6 +1512,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); // Paper end - Prevent block entity and entity crashes } @@ -29991,7 +30773,7 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 } // Paper start - Option to prevent armor stands from doing entity lookups -@@ -791,7 +1432,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -879,7 +1520,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public boolean noCollision(@Nullable Entity entity, AABB box) { if (entity instanceof net.minecraft.world.entity.decoration.ArmorStand && !entity.level().paperConfig().entities.armorStands.doCollisionEntityLookups) return false; @@ -30007,7 +30789,7 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 } // Paper end - Option to prevent armor stands from doing entity lookups -@@ -926,7 +1574,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1024,7 +1672,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { if (!this.isInValidBounds(pos)) { return null; } else { @@ -30016,52 +30798,49 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 ? null : this.getChunkAt(pos).getBlockEntity(pos, LevelChunk.EntityCreationType.IMMEDIATE); } -@@ -1016,22 +1664,16 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - public List getEntities(@Nullable Entity entity, AABB boundingBox, Predicate predicate) { +@@ -1104,20 +1752,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + @Override + public List getEntities(final @Nullable Entity except, final AABB bb, final Predicate selector) { Profiler.get().incrementCounter("getEntities"); - List list = Lists.newArrayList(); -- this.getEntities().get(boundingBox, entity1 -> { -- if (entity1 != entity && predicate.test(entity1)) { -- list.add(entity1); +- List output = Lists.newArrayList(); +- this.getEntities().get(bb, entity -> { +- if (entity != except && selector.test(entity)) { +- output.add(entity); - } - }); ++ // Paper start - rewrite chunk system ++ final List ret = new java.util.ArrayList<>(); -- for (EnderDragonPart enderDragonPart : this.dragonParts()) { -- if (enderDragonPart != entity -- && enderDragonPart.parentMob != entity -- && predicate.test(enderDragonPart) -- && boundingBox.intersects(enderDragonPart.getBoundingBox())) { -- list.add(enderDragonPart); +- for (EnderDragonPart dragonPart : this.dragonParts()) { +- if (dragonPart != except && dragonPart.parentMob != except && selector.test(dragonPart) && bb.intersects(dragonPart.getBoundingBox())) { +- output.add(dragonPart); - } - } -+ // Paper start - rewrite chunk system -+ final List ret = new java.util.ArrayList<>(); ++ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(except, bb, ret, selector); -- return list; -+ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entity, boundingBox, ret, predicate); -+ -+ ca.spottedleaf.moonrise.common.PlatformHooks.get().addToGetEntities((Level)(Object)this, entity, boundingBox, predicate, ret); +- return output; ++ ca.spottedleaf.moonrise.common.PlatformHooks.get().addToGetEntities((Level)(Object)this, except, bb, selector, ret); + + return ret; + // Paper end - rewrite chunk system } @Override -@@ -1045,33 +1687,94 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - this.getEntities(entityTypeTest, bounds, predicate, output, Integer.MAX_VALUE); +@@ -1133,33 +1776,94 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + this.getEntities(type, bb, selector, output, Integer.MAX_VALUE); } - public void getEntities( -- EntityTypeTest entityTypeTest, AABB bounds, Predicate predicate, List output, int maxResults +- final EntityTypeTest type, final AABB bb, final Predicate selector, final List output, final int maxResults - ) { + // Paper start - rewrite chunk system + public void getEntities(final EntityTypeTest entityTypeTest, + final AABB boundingBox, final Predicate predicate, + final List into, final int maxCount) { Profiler.get().incrementCounter("getEntities"); -- this.getEntities().get(entityTypeTest, bounds, entity -> { -- if (predicate.test(entity)) { -- output.add(entity); +- this.getEntities().get(type, bb, e -> { +- if (selector.test(e)) { +- output.add(e); - if (output.size() >= maxResults) { - return AbortableIterationConsumer.Continuation.ABORT; - } @@ -30078,11 +30857,11 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 } + } -- if (entity instanceof EnderDragon enderDragon) { -- for (EnderDragonPart enderDragonPart : enderDragon.getSubEntities()) { -- T entity1 = entityTypeTest.tryCast(enderDragonPart); -- if (entity1 != null && predicate.test(entity1)) { -- output.add(entity1); +- if (e instanceof EnderDragon enderDragon) { +- for (EnderDragonPart subEntity : enderDragon.getSubEntities()) { +- T castSubPart = type.tryCast(subEntity); +- if (castSubPart != null && selector.test(castSubPart)) { +- output.add(castSubPart); - if (output.size() >= maxResults) { - return AbortableIterationConsumer.Continuation.ABORT; - } @@ -30160,9 +30939,9 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 } + // Paper end - rewrite chunk system - public boolean hasEntities(EntityTypeTest entityTypeTest, AABB bounds, Predicate predicate) { + public boolean hasEntities(final EntityTypeTest type, final AABB bb, final Predicate selector) { Profiler.get().incrementCounter("hasEntities"); -@@ -1363,13 +2066,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -1464,13 +2168,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { // Paper start - allow patching this logic public final int getEntityCount() { @@ -30178,15 +30957,15 @@ index a38e63a78be72438a3073ad780063c5e59723482..49aca568407523d95d5801988fe153d7 // Paper end - allow patching this logic } diff --git a/net/minecraft/world/level/LevelReader.java b/net/minecraft/world/level/LevelReader.java -index 60be05520f1093e3edc7dbfcf5bd01601d5e6e7b..fd5a38a9f24c26f8eca738f78180446d354ff3ac 100644 +index 5ae72685665701905df6047ba76e39819d9af0c6..c0074df792f578a1fd828ace9f7a64caa25f3273 100644 --- a/net/minecraft/world/level/LevelReader.java +++ b/net/minecraft/world/level/LevelReader.java -@@ -23,7 +23,16 @@ import net.minecraft.world.level.levelgen.Heightmap; +@@ -23,7 +23,17 @@ import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.phys.AABB; import org.jspecify.annotations.Nullable; --public interface LevelReader extends BlockAndTintGetter, CollisionGetter, SignalGetter, BiomeManager.NoiseBiomeSource { -+public interface LevelReader extends ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader, BlockAndTintGetter, CollisionGetter, SignalGetter, BiomeManager.NoiseBiomeSource { // Paper - rewrite chunk system +-public interface LevelReader extends BlockAndLightGetter, CollisionGetter, SignalGetter, BiomeManager.NoiseBiomeSource { ++public interface LevelReader extends BlockAndLightGetter, CollisionGetter, SignalGetter, BiomeManager.NoiseBiomeSource, ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader { // Paper - rewrite chunk system + // Paper start - rewrite chunk system + @Override + public default ChunkAccess moonrise$syncLoadNonFull(final int chunkX, final int chunkZ, final ChunkStatus status) { @@ -30196,14 +30975,15 @@ index 60be05520f1093e3edc7dbfcf5bd01601d5e6e7b..fd5a38a9f24c26f8eca738f78180446d + return ((LevelReader)this).getChunk(chunkX, chunkZ, status, true); + } + // Paper end - rewrite chunk system - @Nullable ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk); ++ + @Nullable ChunkAccess getChunk(final int chunkX, final int chunkZ, final ChunkStatus targetStatus, final boolean loadOrGenerate); @Nullable ChunkAccess getChunkIfLoadedImmediately(int x, int z); // Paper - ifLoaded api (we need this since current impl blocks if the chunk is loading) diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java -index 907956791afe10021d9fc86bab7a115ee9ff57a5..1e94326047622d8975fd1cc5f44e75104fd56946 100644 +index 0cf47956a699c679259a920572efd3bea2b19a61..0ef64fcf11d6cd0e25227fe6053ca8cb2125bfff 100644 --- a/net/minecraft/world/level/ServerExplosion.java +++ b/net/minecraft/world/level/ServerExplosion.java -@@ -62,6 +62,249 @@ public class ServerExplosion implements Explosion { +@@ -54,6 +54,249 @@ public class ServerExplosion implements Explosion { public float yield; // CraftBukkit end public boolean excludeSourceFromDamage = true; // Paper - Allow explosions to damage source @@ -30452,56 +31232,55 @@ index 907956791afe10021d9fc86bab7a115ee9ff57a5..1e94326047622d8975fd1cc5f44e7510 + // Paper end - collisions optimisations public ServerExplosion( - ServerLevel level, -@@ -136,63 +379,102 @@ public class ServerExplosion implements Explosion { + final ServerLevel level, +@@ -128,62 +371,102 @@ public class ServerExplosion implements Explosion { } private List calculateExplodedPositions() { -- Set set = new HashSet<>(); -- int i = 16; +- Set toBlowSet = new HashSet<>(); +- int size = 16; - -- for (int i1 = 0; i1 < 16; i1++) { -- for (int i2 = 0; i2 < 16; i2++) { -- for (int i3 = 0; i3 < 16; i3++) { -- if (i1 == 0 || i1 == 15 || i2 == 0 || i2 == 15 || i3 == 0 || i3 == 15) { -- double d = i1 / 15.0F * 2.0F - 1.0F; -- double d1 = i2 / 15.0F * 2.0F - 1.0F; -- double d2 = i3 / 15.0F * 2.0F - 1.0F; -- double squareRoot = Math.sqrt(d * d + d1 * d1 + d2 * d2); -- d /= squareRoot; -- d1 /= squareRoot; -- d2 /= squareRoot; -- float f = this.radius * (0.7F + this.level.random.nextFloat() * 0.6F); -- double d3 = this.center.x; -- double d4 = this.center.y; -- double d5 = this.center.z; +- for (int xx = 0; xx < 16; xx++) { +- for (int yy = 0; yy < 16; yy++) { +- for (int zz = 0; zz < 16; zz++) { +- if (xx == 0 || xx == 15 || yy == 0 || yy == 15 || zz == 0 || zz == 15) { +- double xd = xx / 15.0F * 2.0F - 1.0F; +- double yd = yy / 15.0F * 2.0F - 1.0F; +- double zd = zz / 15.0F * 2.0F - 1.0F; +- double d = Math.sqrt(xd * xd + yd * yd + zd * zd); +- xd /= d; +- yd /= d; +- zd /= d; +- float remainingPower = this.radius * (0.7F + this.level.random.nextFloat() * 0.6F); +- double xp = this.center.x; +- double yp = this.center.y; +- double zp = this.center.z; - -- for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) { -- BlockPos blockPos = BlockPos.containing(d3, d4, d5); -- BlockState blockState = this.level.getBlockState(blockPos); -- if (!blockState.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed -- FluidState fluidState = blockState.getFluidState(); // Paper - Perf: Optimize call to getFluid for explosions -- if (!this.level.isInWorldBounds(blockPos)) { +- for (float stepSize = 0.3F; remainingPower > 0.0F; remainingPower -= 0.22500001F) { +- BlockPos pos = BlockPos.containing(xp, yp, zp); +- BlockState block = this.level.getBlockState(pos); +- if (!block.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed +- FluidState fluid = block.getFluidState(); // Paper - Perf: Optimize call to getFluid for explosions +- if (!this.level.isInWorldBounds(pos)) { - break; - } + // Paper start - collision optimisations + final ObjectArrayList ret = new ObjectArrayList<>(); -- Optional blockExplosionResistance = this.damageCalculator -- .getBlockExplosionResistance(this, this.level, blockPos, blockState, fluidState); -- if (blockExplosionResistance.isPresent()) { -- f -= (blockExplosionResistance.get() + 0.3F) * 0.3F; +- Optional resistance = this.damageCalculator.getBlockExplosionResistance(this, this.level, pos, block, fluid); +- if (resistance.isPresent()) { +- remainingPower -= (resistance.get() + 0.3F) * 0.3F; - } + final Vec3 center = this.center; -- if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockPos, blockState, f)) { -- set.add(blockPos); +- if (remainingPower > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, pos, block, remainingPower)) { +- toBlowSet.add(pos); - // Paper start - prevent headless pistons from forming -- if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowHeadlessPistons && blockState.is(net.minecraft.world.level.block.Blocks.MOVING_PISTON)) { -- net.minecraft.world.level.block.entity.BlockEntity extension = this.level.getBlockEntity(blockPos); +- if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowHeadlessPistons && block.is(net.minecraft.world.level.block.Blocks.MOVING_PISTON)) { +- net.minecraft.world.level.block.entity.BlockEntity extension = this.level.getBlockEntity(pos); - if (extension instanceof net.minecraft.world.level.block.piston.PistonMovingBlockEntity blockEntity && blockEntity.isSourcePiston()) { -- net.minecraft.core.Direction direction = blockState.getValue(net.minecraft.world.level.block.piston.PistonHeadBlock.FACING); -- set.add(blockPos.relative(direction.getOpposite())); +- net.minecraft.core.Direction direction = block.getValue(net.minecraft.world.level.block.piston.PistonHeadBlock.FACING); +- toBlowSet.add(pos.relative(direction.getOpposite())); - } + final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache = this.directMappedBlockCache; + @@ -30536,7 +31315,7 @@ index 907956791afe10021d9fc86bab7a115ee9ff57a5..1e94326047622d8975fd1cc5f44e7510 + + ray += 3; + -+ float power = this.radius * (0.7F + this.level.random.nextFloat() * 0.6F); ++ float power = this.radius * (0.7F + this.level.getRandom().nextFloat() * 0.6F); + + do { + final int blockX = Mth.floor(currX); @@ -30581,9 +31360,9 @@ index 907956791afe10021d9fc86bab7a115ee9ff57a5..1e94326047622d8975fd1cc5f44e7510 - // Paper end - prevent headless pistons from forming } -- d3 += d * 0.3F; -- d4 += d1 * 0.3F; -- d5 += d2 * 0.3F; +- xp += xd * 0.3F; +- yp += yd * 0.3F; +- zp += zd * 0.3F; + // Paper end - prevent headless pistons from forming } } @@ -30597,13 +31376,13 @@ index 907956791afe10021d9fc86bab7a115ee9ff57a5..1e94326047622d8975fd1cc5f44e7510 + } while (power > 0.0f); } -- return new ObjectArrayList<>(set); +- return new ObjectArrayList<>(toBlowSet); + return ret; + // Paper end - collision optimisations } private void hurtEntities() { -@@ -350,6 +632,14 @@ public class ServerExplosion implements Explosion { +@@ -340,6 +623,14 @@ public class ServerExplosion implements Explosion { } public int explode() { @@ -30616,10 +31395,10 @@ index 907956791afe10021d9fc86bab7a115ee9ff57a5..1e94326047622d8975fd1cc5f44e7510 + this.mutablePos = new BlockPos.MutableBlockPos(); + // Paper end - collision optimisations this.level.gameEvent(this.source, GameEvent.EXPLODE, this.center); - List list = this.calculateExplodedPositions(); + List toBlow = this.calculateExplodedPositions(); this.hurtEntities(); -@@ -364,6 +654,13 @@ public class ServerExplosion implements Explosion { - this.createFire(list); +@@ -354,6 +645,13 @@ public class ServerExplosion implements Explosion { + this.createFire(toBlow); } + // Paper start - collision optimisations @@ -30629,10 +31408,10 @@ index 907956791afe10021d9fc86bab7a115ee9ff57a5..1e94326047622d8975fd1cc5f44e7510 + this.directMappedBlockCache = null; + this.mutablePos = null; + // Paper end - collision optimisations - return list.size(); + return toBlow.size(); } -@@ -447,12 +744,12 @@ public class ServerExplosion implements Explosion { +@@ -436,12 +734,12 @@ public class ServerExplosion implements Explosion { // Paper start - Optimize explosions private float getBlockDensity(Vec3 vec3d, Entity entity) { if (!this.level.paperConfig().environment.optimizeExplosions) { @@ -30648,10 +31427,10 @@ index 907956791afe10021d9fc86bab7a115ee9ff57a5..1e94326047622d8975fd1cc5f44e7510 } diff --git a/net/minecraft/world/level/TicketStorage.java b/net/minecraft/world/level/TicketStorage.java -index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262183a9ce2 100644 +index 312d2eeda6b413d3b2d10ee58bd154bbddc88451..3d2fc3809ec8a1d8bda47da4fd631bb0fe712ca5 100644 --- a/net/minecraft/world/level/TicketStorage.java +++ b/net/minecraft/world/level/TicketStorage.java -@@ -29,7 +29,7 @@ import net.minecraft.world.level.saveddata.SavedDataType; +@@ -30,7 +30,7 @@ import net.minecraft.world.level.saveddata.SavedDataType; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; @@ -30660,10 +31439,10 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 private static final int INITIAL_TICKET_LIST_CAPACITY = 4; private static final Logger LOGGER = LogUtils.getLogger(); private static final Codec> TICKET_ENTRY = Codec.mapPair(ChunkPos.CODEC.fieldOf("chunk_pos"), Ticket.CODEC).codec(); -@@ -38,16 +38,30 @@ public class TicketStorage extends SavedData { - .apply(instance, TicketStorage::fromPacked) +@@ -40,16 +40,30 @@ public class TicketStorage extends SavedData { + public static final SavedDataType TYPE = new SavedDataType<>( + Identifier.withDefaultNamespace("chunk_tickets"), TicketStorage::new, CODEC, DataFixTypes.SAVED_DATA_FORCED_CHUNKS ); - public static final SavedDataType TYPE = new SavedDataType<>("chunks", TicketStorage::new, CODEC, DataFixTypes.SAVED_DATA_FORCED_CHUNKS); - public final Long2ObjectOpenHashMap> tickets; + // Paper - rewrite chunk system private final Long2ObjectOpenHashMap> deactivatedTickets; @@ -30686,7 +31465,7 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 + } + // Paper end - rewrite chunk system + - private TicketStorage(Long2ObjectOpenHashMap> tickets, Long2ObjectOpenHashMap> deactivatedTickets) { + private TicketStorage(final Long2ObjectOpenHashMap> tickets, final Long2ObjectOpenHashMap> deactivatedTickets) { - this.tickets = tickets; + // Paper - rewrite chunk system this.deactivatedTickets = deactivatedTickets; @@ -30695,8 +31474,8 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 } public TicketStorage() { -@@ -76,8 +90,32 @@ public class TicketStorage extends SavedData { - return list; +@@ -78,8 +92,32 @@ public class TicketStorage extends SavedData { + return tickets; } + // Paper start - rewrite chunk system @@ -30714,7 +31493,7 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 + final long pos = entry.getLongKey(); + final java.util.Collection chunkTickets = entry.getValue(); + -+ final ChunkPos chunkPos = new ChunkPos(pos); ++ final ChunkPos chunkPos = ChunkPos.unpack(pos); + + for (final Ticket ticket : chunkTickets) { + consumer.accept(chunkPos, ticket); @@ -30723,21 +31502,21 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 + } + // Paper end - rewrite chunk system + - private void forEachTicket(BiConsumer action) { -- forEachTicket(action, this.tickets); -+ this.redirectRegularTickets(action, null); // Paper - rewrite chunk system - forEachTicket(action, this.deactivatedTickets); + private void forEachTicket(final BiConsumer output) { +- forEachTicket(output, this.tickets); ++ this.redirectRegularTickets(output, null); // Paper - rewrite chunk system + forEachTicket(output, this.deactivatedTickets); } -@@ -102,35 +140,34 @@ public class TicketStorage extends SavedData { +@@ -104,35 +142,34 @@ public class TicketStorage extends SavedData { } - public void setLoadingChunkUpdatedListener(TicketStorage.@Nullable ChunkUpdated loadingChunkUpdatedListener) { + public void setLoadingChunkUpdatedListener(final TicketStorage.@Nullable ChunkUpdated loadingChunkUpdatedListener) { - this.loadingChunkUpdatedListener = loadingChunkUpdatedListener; + // Paper - rewrite chunk system } - public void setSimulationChunkUpdatedListener(TicketStorage.@Nullable ChunkUpdated simulationChunkUpdatedListener) { + public void setSimulationChunkUpdatedListener(final TicketStorage.@Nullable ChunkUpdated simulationChunkUpdatedListener) { - this.simulationChunkUpdatedListener = simulationChunkUpdatedListener; + // Paper - rewrite chunk system } @@ -30748,8 +31527,8 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 } public boolean shouldKeepDimensionActive() { -- for (List list : this.tickets.values()) { -- for (Ticket ticket : list) { +- for (List group : this.tickets.values()) { +- for (Ticket ticket : group) { - if (ticket.getType().shouldKeepDimensionActive()) { - return true; - } @@ -30758,61 +31537,61 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 - - return false; + // Paper start - rewrite chunk system -+ final ca.spottedleaf.concurrentutil.map.ConcurrentLong2LongChainedHashTable ticketCounters = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager ++ final ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2LongHashTable ticketCounters = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager + .getTicketCounters(ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType.COUNTER_TYPE_KEEP_DIMENSION_ACTIVE); + return ticketCounters != null && !ticketCounters.isEmpty(); + // Paper end - rewrite chunk system } - public List getTickets(long chunkPos) { -- return this.tickets.getOrDefault(chunkPos, List.of()); + public List getTickets(final long key) { +- return this.tickets.getOrDefault(key, List.of()); + // Paper start - rewrite chunk system + return ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager -+ .getTicketsAt(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(chunkPos), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(chunkPos)); ++ .getTicketsAt(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(key), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(key)); + // Paper end - rewrite chunk system } - private List getOrCreateTickets(long chunkPos) { -- return this.tickets.computeIfAbsent(chunkPos, l -> new ObjectArrayList<>(4)); + private List getOrCreateTickets(final long key) { +- return this.tickets.computeIfAbsent(key, k -> new ObjectArrayList<>(4)); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public void addTicketWithRadius(TicketType ticketType, ChunkPos chunkPos, int radius) { -@@ -143,37 +180,14 @@ public class TicketStorage extends SavedData { + public void addTicketWithRadius(final TicketType type, final ChunkPos chunkPos, final int radius) { +@@ -145,37 +182,14 @@ public class TicketStorage extends SavedData { } - public boolean addTicket(long chunkPos, Ticket ticket) { -- List tickets = this.getOrCreateTickets(chunkPos); + public boolean addTicket(final long key, final Ticket ticket) { +- List tickets = this.getOrCreateTickets(key); - -- for (Ticket ticket1 : tickets) { -- if (isTicketSameTypeAndLevel(ticket, ticket1)) { -- ticket1.resetTicksLeft(); +- for (Ticket t : tickets) { +- if (isTicketSameTypeAndLevel(ticket, t)) { +- t.resetTicksLeft(); - this.setDirty(); - return false; - } - } - -- int ticketLevelAt = getTicketLevelAt(tickets, true); -- int ticketLevelAt1 = getTicketLevelAt(tickets, false); +- int oldSimulationTicketLevel = getTicketLevelAt(tickets, true); +- int oldLoadingTicketLevel = getTicketLevelAt(tickets, false); - tickets.add(ticket); - if (SharedConstants.DEBUG_VERBOSE_SERVER_EVENTS) { -- LOGGER.debug("ATI {} {}", new ChunkPos(chunkPos), ticket); +- LOGGER.debug("ATI {} {}", ChunkPos.unpack(key), ticket); - } - -- if (ticket.getType().doesSimulate() && ticket.getTicketLevel() < ticketLevelAt && this.simulationChunkUpdatedListener != null) { -- this.simulationChunkUpdatedListener.update(chunkPos, ticket.getTicketLevel(), true); +- if (ticket.getType().doesSimulate() && ticket.getTicketLevel() < oldSimulationTicketLevel && this.simulationChunkUpdatedListener != null) { +- this.simulationChunkUpdatedListener.update(key, ticket.getTicketLevel(), true); - } - -- if (ticket.getType().doesLoad() && ticket.getTicketLevel() < ticketLevelAt1 && this.loadingChunkUpdatedListener != null) { -- this.loadingChunkUpdatedListener.update(chunkPos, ticket.getTicketLevel(), true); +- if (ticket.getType().doesLoad() && ticket.getTicketLevel() < oldLoadingTicketLevel && this.loadingChunkUpdatedListener != null) { +- this.loadingChunkUpdatedListener.update(key, ticket.getTicketLevel(), true); - } - - if (ticket.getType().equals(TicketType.FORCED)) { -- this.chunksWithForcedTickets.add(chunkPos); +- this.chunksWithForcedTickets.add(key); - } + // Paper start - rewrite chunk system + final boolean ret = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager -+ .addTicketAtLevel(ticket.getType(), chunkPos, ticket.getTicketLevel(), ((ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicket)ticket).moonrise$getIdentifier()); ++ .addTicketAtLevel(ticket.getType(), key, ticket.getTicketLevel(), ((ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicket)ticket).moonrise$getIdentifier()); this.setDirty(); - return true; @@ -30821,47 +31600,47 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 + // Paper end - rewrite chunk system } - private static boolean isTicketSameTypeAndLevel(Ticket first, Ticket second) { -@@ -219,53 +233,20 @@ public class TicketStorage extends SavedData { + private static boolean isTicketSameTypeAndLevel(final Ticket ticket, final Ticket t) { +@@ -221,53 +235,20 @@ public class TicketStorage extends SavedData { } - public boolean removeTicket(long chunkPos, Ticket ticket) { -- List list = this.tickets.get(chunkPos); -- if (list == null) { + public boolean removeTicket(final long key, final Ticket ticket) { +- List tickets = this.tickets.get(key); +- if (tickets == null) { - return false; - } else { -- boolean flag = false; -- Iterator iterator = list.iterator(); +- boolean found = false; +- Iterator iterator = tickets.iterator(); - - while (iterator.hasNext()) { -- Ticket ticket1 = iterator.next(); -- if (isTicketSameTypeAndLevel(ticket, ticket1)) { +- Ticket t = iterator.next(); +- if (isTicketSameTypeAndLevel(ticket, t)) { - iterator.remove(); - if (SharedConstants.DEBUG_VERBOSE_SERVER_EVENTS) { -- LOGGER.debug("RTI {} {}", new ChunkPos(chunkPos), ticket1); +- LOGGER.debug("RTI {} {}", ChunkPos.unpack(key), t); - } - -- flag = true; +- found = true; - break; - } - } -+ // Paper start - rewrite chunk system -+ final boolean ret = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager -+ .removeTicketAtLevel(ticket.getType(), chunkPos, ticket.getTicketLevel(), ((ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicket)ticket).moonrise$getIdentifier()); - -- if (!flag) { +- +- if (!found) { - return false; - } else { -- if (list.isEmpty()) { -- this.tickets.remove(chunkPos); +- if (tickets.isEmpty()) { +- this.tickets.remove(key); - } - - if (ticket.getType().doesSimulate() && this.simulationChunkUpdatedListener != null) { -- this.simulationChunkUpdatedListener.update(chunkPos, getTicketLevelAt(list, true), false); +- this.simulationChunkUpdatedListener.update(key, getTicketLevelAt(tickets, true), false); - } -- ++ // Paper start - rewrite chunk system ++ final boolean ret = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager ++ .removeTicketAtLevel(ticket.getType(), key, ticket.getTicketLevel(), ((ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicket)ticket).moonrise$getIdentifier()); + - if (ticket.getType().doesLoad() && this.loadingChunkUpdatedListener != null) { -- this.loadingChunkUpdatedListener.update(chunkPos, getTicketLevelAt(list, false), false); +- this.loadingChunkUpdatedListener.update(key, getTicketLevelAt(tickets, false), false); - } - - if (ticket.getType().equals(TicketType.FORCED)) { @@ -30880,17 +31659,17 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 } private void updateForcedChunks() { -- this.chunksWithForcedTickets = this.getAllChunksWithTicketThat(ticket -> ticket.getType().equals(TicketType.FORCED)); +- this.chunksWithForcedTickets = this.getAllChunksWithTicketThat(t -> t.getType().equals(TicketType.FORCED)); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public String getTicketDebugString(long chunkPos, boolean requireSimulation) { -@@ -275,14 +256,7 @@ public class TicketStorage extends SavedData { + public String getTicketDebugString(final long key, final boolean simulation) { +@@ -277,14 +258,7 @@ public class TicketStorage extends SavedData { } - public void purgeStaleTickets(ChunkMap map) { + public void purgeStaleTickets(final ChunkMap chunkMap) { - this.removeTicketIf((ticket, chunkPos) -> { -- if (this.canTicketExpire(map, ticket, chunkPos)) { +- if (this.canTicketExpire(chunkMap, ticket, chunkPos)) { - ticket.decreaseTicksLeft(); - return ticket.isTimedOut(); - } else { @@ -30901,7 +31680,7 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 this.setDirty(); } -@@ -298,82 +272,15 @@ public class TicketStorage extends SavedData { +@@ -300,82 +274,15 @@ public class TicketStorage extends SavedData { } public void deactivateTicketsOnClosing() { @@ -30909,101 +31688,101 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 + // Paper - rewrite chunk system } - public void removeTicketIf(TicketStorage.TicketPredicate predicate, @Nullable Long2ObjectOpenHashMap> tickets) { -- ObjectIterator>> objectIterator = this.tickets.long2ObjectEntrySet().fastIterator(); -- boolean flag = false; + public void removeTicketIf(final TicketStorage.TicketPredicate predicate, final @Nullable Long2ObjectOpenHashMap> removedTickets) { +- ObjectIterator>> ticketsPerChunkIterator = this.tickets.long2ObjectEntrySet().fastIterator(); +- boolean removedForced = false; - -- while (objectIterator.hasNext()) { -- Entry> entry = objectIterator.next(); -- Iterator iterator = entry.getValue().iterator(); -- long longKey = entry.getLongKey(); -- boolean flag1 = false; -- boolean flag2 = false; +- while (ticketsPerChunkIterator.hasNext()) { +- Entry> entry = ticketsPerChunkIterator.next(); +- Iterator chunkTicketsIterator = entry.getValue().iterator(); +- long chunkPos = entry.getLongKey(); +- boolean removedSimulation = false; +- boolean removedLoading = false; - -- while (iterator.hasNext()) { -- Ticket ticket = iterator.next(); -- if (predicate.test(ticket, longKey)) { -- if (tickets != null) { -- List list = tickets.computeIfAbsent(longKey, chunkPos -> new ObjectArrayList<>(entry.getValue().size())); -- list.add(ticket); +- while (chunkTicketsIterator.hasNext()) { +- Ticket ticket = chunkTicketsIterator.next(); +- if (predicate.test(ticket, chunkPos)) { +- if (removedTickets != null) { +- List tickets = removedTickets.computeIfAbsent(chunkPos, k -> new ObjectArrayList<>(entry.getValue().size())); +- tickets.add(ticket); - } - -- iterator.remove(); +- chunkTicketsIterator.remove(); - if (ticket.getType().doesLoad()) { -- flag2 = true; +- removedLoading = true; - } - - if (ticket.getType().doesSimulate()) { -- flag1 = true; +- removedSimulation = true; - } - - if (ticket.getType().equals(TicketType.FORCED)) { -- flag = true; +- removedForced = true; - } - } - } - -- if (flag2 || flag1) { -- if (flag2 && this.loadingChunkUpdatedListener != null) { -- this.loadingChunkUpdatedListener.update(longKey, getTicketLevelAt(entry.getValue(), false), false); +- if (removedLoading || removedSimulation) { +- if (removedLoading && this.loadingChunkUpdatedListener != null) { +- this.loadingChunkUpdatedListener.update(chunkPos, getTicketLevelAt(entry.getValue(), false), false); - } - -- if (flag1 && this.simulationChunkUpdatedListener != null) { -- this.simulationChunkUpdatedListener.update(longKey, getTicketLevelAt(entry.getValue(), true), false); +- if (removedSimulation && this.simulationChunkUpdatedListener != null) { +- this.simulationChunkUpdatedListener.update(chunkPos, getTicketLevelAt(entry.getValue(), true), false); - } - - this.setDirty(); - if (entry.getValue().isEmpty()) { -- objectIterator.remove(); +- ticketsPerChunkIterator.remove(); - } - } - } - -- if (flag) { +- if (removedForced) { - this.updateForcedChunks(); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public void replaceTicketLevelOfType(int level, TicketType ticketType) { -- List> list = new ArrayList<>(); + public void replaceTicketLevelOfType(final int newLevel, final TicketType ticketType) { +- List> affectedTickets = new ArrayList<>(); - - for (Entry> entry : this.tickets.long2ObjectEntrySet()) { - for (Ticket ticket : entry.getValue()) { - if (ticket.getType() == ticketType) { -- list.add(Pair.of(ticket, entry.getLongKey())); +- affectedTickets.add(Pair.of(ticket, entry.getLongKey())); - } - } - } - -- for (Pair pair : list) { -- Long _long = pair.getSecond(); +- for (Pair pair : affectedTickets) { +- Long key = pair.getSecond(); - Ticket ticketx = pair.getFirst(); -- this.removeTicket(_long, ticketx); +- this.removeTicket(key, ticketx); - TicketType type = ticketx.getType(); -- this.addTicket(_long, new Ticket(type, level)); +- this.addTicket(key, new Ticket(type, newLevel)); - } + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public boolean updateChunkForced(ChunkPos chunkPos, boolean add) { -@@ -382,22 +289,29 @@ public class TicketStorage extends SavedData { + public boolean updateChunkForced(final ChunkPos chunkPos, final boolean forced) { +@@ -384,22 +291,29 @@ public class TicketStorage extends SavedData { } public LongSet getForceLoadedChunks() { - return this.chunksWithForcedTickets; - } + // Paper start - rewrite chunk system -+ final ca.spottedleaf.concurrentutil.map.ConcurrentLong2LongChainedHashTable forced = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler() ++ final ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2LongHashTable forced = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler() + .chunkHolderManager.getTicketCounters(ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType.COUNTER_TYPE_FORCED); -- private LongSet getAllChunksWithTicketThat(Predicate predicate) { -- LongOpenHashSet set = new LongOpenHashSet(); +- private LongSet getAllChunksWithTicketThat(final Predicate ticketCheck) { +- LongOpenHashSet chunks = new LongOpenHashSet(); - - for (Entry> entry : Long2ObjectMaps.fastIterable(this.tickets)) { - for (Ticket ticket : entry.getValue()) { -- if (predicate.test(ticket)) { -- set.add(entry.getLongKey()); +- if (ticketCheck.test(ticket)) { +- chunks.add(entry.getLongKey()); - break; - } - } @@ -31011,7 +31790,7 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 + return new it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet(); } -- return set; +- return chunks; + // note: important to presize correctly using size/loadfactor to avoid awful write performance + // think: iteration over our map has the same hash strategy, and if ret is not sized + // correctly then every (ret.table.length) may collide. During resize, open hashed tables @@ -31025,89 +31804,89 @@ index ad5c9c32be4be9324a313fbd9e099c9dd3f68253..b1c663187720d308c4cf3a5352879262 + // Paper end - rewrite chunk system + } + -+ private LongSet getAllChunksWithTicketThat(Predicate predicate) { ++ private LongSet getAllChunksWithTicketThat(final Predicate ticketCheck) { + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } @FunctionalInterface -@@ -421,7 +335,7 @@ public class TicketStorage extends SavedData { +@@ -423,7 +337,7 @@ public class TicketStorage extends SavedData { } public void removeAllPluginRegionTickets(TicketType ticketType, int ticketLevel, org.bukkit.plugin.Plugin ticketIdentifier) { -- removeTicketIf((ticket, chunkKey) -> ticket.getType() == ticketType && ticket.getTicketLevel() == ticketLevel && ticket.getIdentifier() == ticketIdentifier, null); +- removeTicketIf((ticket, _) -> ticket.getType() == ticketType && ticket.getTicketLevel() == ticketLevel && ticket.getIdentifier() == ticketIdentifier, null); + this.chunkMap.level.moonrise$getChunkTaskScheduler().chunkHolderManager.removeAllTicketsFor(ticketType, ticketLevel, ticketIdentifier); // Paper - rewrite chunk system } // Paper end } diff --git a/net/minecraft/world/level/biome/Biome.java b/net/minecraft/world/level/biome/Biome.java -index dfc24fd23e26010274e02cc26666c1801ef811c0..a253b02bb6e07ba4cdbd9320445a6b6c3e54ada2 100644 +index 7645e677e331f6aea404fb5ae1264690ffb1d212..96f2e3359774460091177068302ff0faf4671373 100644 --- a/net/minecraft/world/level/biome/Biome.java +++ b/net/minecraft/world/level/biome/Biome.java -@@ -123,20 +123,7 @@ public final class Biome { +@@ -128,20 +128,7 @@ public final class Biome { @Deprecated - public float getTemperature(BlockPos pos, int seaLevel) { -- long packedBlockPos = pos.asLong(); -- Long2FloatLinkedOpenHashMap map = this.temperatureCache.get(); -- float f = map.get(packedBlockPos); -- if (!Float.isNaN(f)) { -- return f; + public float getTemperature(final BlockPos pos, final int seaLevel) { +- long key = pos.asLong(); +- Long2FloatLinkedOpenHashMap cache = this.temperatureCache.get(); +- float cached = cache.get(key); +- if (!Float.isNaN(cached)) { +- return cached; - } else { -- float heightAdjustedTemperature = this.getHeightAdjustedTemperature(pos, seaLevel); -- if (map.size() == 1024) { -- map.removeFirstFloat(); +- float temp = this.getHeightAdjustedTemperature(pos, seaLevel); +- if (cache.size() == 1024) { +- cache.removeFirstFloat(); - } - -- map.put(packedBlockPos, heightAdjustedTemperature); -- return heightAdjustedTemperature; +- cache.put(key, temp); +- return temp; - } + return this.getHeightAdjustedTemperature(pos, seaLevel); // Paper - optimise random ticking } - public boolean shouldFreeze(LevelReader level, BlockPos pos) { + public boolean shouldFreeze(final LevelReader level, final BlockPos pos) { diff --git a/net/minecraft/world/level/biome/BiomeManager.java b/net/minecraft/world/level/biome/BiomeManager.java -index 8d98cba3830dc5dfb5cae9a6f5fedfffee0d2cd8..73962e79a0f3d892e3155443a1b84508b0f4042e 100644 +index 9404f0b0b03dce54e2c9ad6ca2c36354888cc9f5..a0d7f6f8ecbc320267bdad27315fc2e2e1ff3702 100644 --- a/net/minecraft/world/level/biome/BiomeManager.java +++ b/net/minecraft/world/level/biome/BiomeManager.java -@@ -98,8 +98,7 @@ public class BiomeManager { +@@ -100,8 +100,7 @@ public class BiomeManager { } - private static double getFiddle(long seed) { -- double d = Math.floorMod(seed >> 24, 1024) / 1024.0; -- return (d - 0.5) * 0.9; -+ return (double)(((seed >> 24) & (1024 - 1)) - (1024/2)) * (0.9 / 1024.0); // Paper - avoid floorMod, fp division, and fp subtraction + private static double getFiddle(final long rval) { +- double uniform = Math.floorMod(rval >> 24, 1024) / 1024.0; +- return (uniform - 0.5) * 0.9; ++ return (double)(((rval >> 24) & (1024 - 1)) - (1024/2)) * (0.9 / 1024.0); // Paper - avoid floorMod, fp division, and fp subtraction } public interface NoiseBiomeSource { diff --git a/net/minecraft/world/level/block/Block.java b/net/minecraft/world/level/block/Block.java -index fffb4a9bcbf87254bdc51a32577d188d169b6a51..94b4143449c99ee35db44ab8e2a766d924aa6410 100644 +index 7678701a578d2dc45b35da072a0bb2c027522043..41aab9a526be77b400bed7a74614eceec95345b5 100644 --- a/net/minecraft/world/level/block/Block.java +++ b/net/minecraft/world/level/block/Block.java -@@ -355,7 +355,7 @@ public class Block extends BlockBehaviour implements ItemLike { +@@ -366,7 +366,7 @@ public class Block extends BlockBehaviour implements ItemLike { } - public static boolean isShapeFullBlock(VoxelShape shape) { + public static boolean isShapeFullBlock(final VoxelShape shape) { - return SHAPE_FULL_BLOCK_CACHE.getUnchecked(shape); + return ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)shape).moonrise$isFullBlock(); // Paper - optimise collisions } - public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { + public void animateTick(final BlockState state, final Level level, final BlockPos pos, final RandomSource random) { diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java -index fd626256ffcf0372f85666e2ee1bd5c7bdcb105a..c5953d5751e655e121e459aaab35e7719948e69e 100644 +index e66b04c89c5155f660e3274ebb205cfb72190b1b..ca77680b645c352456744b148e0ff4ac31b05aa2 100644 --- a/net/minecraft/world/level/block/state/BlockBehaviour.java +++ b/net/minecraft/world/level/block/state/BlockBehaviour.java -@@ -415,7 +415,7 @@ public abstract class BlockBehaviour implements FeatureElement { +@@ -435,7 +435,7 @@ public abstract class BlockBehaviour implements FeatureElement { return this.properties.destroyTime; } -- public abstract static class BlockStateBase extends StateHolder { -+ public abstract static class BlockStateBase extends StateHolder implements ca.spottedleaf.moonrise.patches.starlight.blockstate.StarlightAbstractBlockState, ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState { // Paper - rewrite chunk system // Paper - optimise collisions +- public abstract static class BlockStateBase extends StateHolder implements TypedInstance { ++ public abstract static class BlockStateBase extends StateHolder implements TypedInstance, ca.spottedleaf.moonrise.patches.starlight.blockstate.StarlightAbstractBlockState, ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState { // Paper - rewrite chunk system // Paper - optimise collisions private static final Direction[] DIRECTIONS = Direction.values(); - private static final VoxelShape[] EMPTY_OCCLUSION_SHAPES = Util.make(new VoxelShape[DIRECTIONS.length], shape -> Arrays.fill(shape, Shapes.empty())); - private static final VoxelShape[] FULL_BLOCK_OCCLUSION_SHAPES = Util.make( -@@ -452,6 +452,76 @@ public abstract class BlockBehaviour implements FeatureElement { + private static final VoxelShape[] EMPTY_OCCLUSION_SHAPES = Util.make(new VoxelShape[DIRECTIONS.length], s -> Arrays.fill(s, Shapes.empty())); + private static final VoxelShape[] FULL_BLOCK_OCCLUSION_SHAPES = Util.make(new VoxelShape[DIRECTIONS.length], s -> Arrays.fill(s, Shapes.block())); +@@ -470,6 +470,76 @@ public abstract class BlockBehaviour implements FeatureElement { private boolean propagatesSkylightDown; - private int lightBlock; + private int lightDampening; + // Paper start - rewrite chunk system + private boolean isConditionallyFullOpaque; @@ -31179,13 +31958,13 @@ index fd626256ffcf0372f85666e2ee1bd5c7bdcb105a..c5953d5751e655e121e459aaab35e771 + } + // Paper end - optimise collisions + - protected BlockStateBase(Block owner, Reference2ObjectArrayMap, Comparable> values, MapCodec propertiesCodec) { - super(owner, values, propertiesCodec); + protected BlockStateBase(final Block owner, final Property[] propertyKeys, final Comparable[] propertyValues) { + super(owner, propertyKeys, propertyValues); BlockBehaviour.Properties properties = owner.properties; -@@ -529,6 +599,41 @@ public abstract class BlockBehaviour implements FeatureElement { +@@ -547,6 +617,41 @@ public abstract class BlockBehaviour implements FeatureElement { this.propagatesSkylightDown = this.owner.propagatesSkylightDown(this.asState()); - this.lightBlock = this.owner.getLightBlock(this.asState()); + this.lightDampening = this.owner.getLightDampening(this.asState()); + // Paper start - rewrite chunk system + this.isConditionallyFullOpaque = this.canOcclude & this.useShapeForLightOcclusion; + // Paper end - rewrite chunk system @@ -31224,27 +32003,68 @@ index fd626256ffcf0372f85666e2ee1bd5c7bdcb105a..c5953d5751e655e121e459aaab35e771 } public Block getBlock() { +diff --git a/net/minecraft/world/level/block/state/StateDefinition.java b/net/minecraft/world/level/block/state/StateDefinition.java +index 792f3016554dbb69ca1c67c03ea59fa2351e3db5..c9d21990d1506087b7f7dbbfcf4127c4c99d14d2 100644 +--- a/net/minecraft/world/level/block/state/StateDefinition.java ++++ b/net/minecraft/world/level/block/state/StateDefinition.java +@@ -66,10 +66,19 @@ public class StateDefinition> { + return codec; + } + ++ // Paper start - optimise blockstate property access ++ private static > ImmutableList initStateTables(final ImmutableList states) { ++ if (!states.isEmpty()) { ++ ((ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccessStateHolder)(StateHolder)states.get(0)).moonrise$init(states); ++ } ++ return states; ++ } ++ // Paper end - optimise blockstate property access ++ + private static > ImmutableList createSingletonState(final O owner, final StateDefinition.Factory factory) { + S singletonState = (S)factory.create(owner, EMPTY_KEYS, EMPTY_VALUES); + singletonState.initializeNeighbors((S[][])emptyNeighbors()); +- return ImmutableList.of(singletonState); ++ return initStateTables(ImmutableList.of(singletonState)); // Paper - optimise blockstate property access + } + + private static > ImmutableList createSinglePropertyStates( +@@ -99,7 +108,7 @@ public class StateDefinition> { + blockState.initializeNeighbors(neighbours); + } + +- return states.build(); ++ return initStateTables(states.build()); // Paper - optimise blockstate property access + } + + private static > ImmutableList createMultiPropertyStates( +@@ -125,7 +134,7 @@ public class StateDefinition> { + + StateDefinition.StateCollection stateCollection = new StateDefinition.StateCollection<>(statesByValues, new HashMap<>()); + statesByValues.forEach((valuesx, state) -> state.initializeNeighbors(stateCollection.fillNeighborsForState(propertyKeys, valuesx))); +- return states.build(); ++ return initStateTables(states.build()); // Paper - optimise blockstate property access + } + + private static > S[][] emptyNeighbors() { diff --git a/net/minecraft/world/level/block/state/StateHolder.java b/net/minecraft/world/level/block/state/StateHolder.java -index abb2189902af888ac30510d5088ea77fb9e0c542..c020d1944a25e2cd247e30a97c35bf93731d694a 100644 +index 7d0d5e8e6c951be025183f7b1712d34aeb48b110..b72861c0164ebb13b6fc3e78cd7ce0a2c484f22d 100644 --- a/net/minecraft/world/level/block/state/StateHolder.java +++ b/net/minecraft/world/level/block/state/StateHolder.java -@@ -15,7 +15,7 @@ import java.util.stream.Collectors; +@@ -13,21 +13,49 @@ import java.util.stream.Stream; import net.minecraft.world.level.block.state.properties.Property; import org.jspecify.annotations.Nullable; -public abstract class StateHolder { -+public abstract class StateHolder implements ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccessStateHolder { // Paper - optimise blockstate property access ++public abstract class StateHolder implements ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccessStateHolder { // Paper - optimise blockstate property access + private static final int VALUE_NOT_FOUND = -1; public static final String NAME_TAG = "Name"; public static final String PROPERTIES_TAG = "Properties"; - public static final Function, Comparable>, String> PROPERTY_ENTRY_TO_STRING_FUNCTION = new Function, Comparable>, String>() { -@@ -34,14 +34,28 @@ public abstract class StateHolder { - } - }; protected final O owner; -- private final Reference2ObjectArrayMap, Comparable> values; -+ private Reference2ObjectArrayMap, Comparable> values; // Paper - optimise blockstate property access - remove final - private Map, S[]> neighbours; - protected final MapCodec propertiesCodec; +- private final Property[] propertyKeys; +- private final Comparable[] propertyValues; ++ private Property[] propertyKeys; // Paper - optimise blockstate property access - remove final ++ private Comparable[] propertyValues; // Paper - optimise blockstate property access - remove final + private S[][] neighbors; + // Paper start - optimise blockstate property access + protected ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.util.ZeroCollidingReferenceStateTable optimisedTable; @@ -31254,38 +32074,64 @@ index abb2189902af888ac30510d5088ea77fb9e0c542..c020d1944a25e2cd247e30a97c35bf93 + public final long moonrise$getTableIndex() { + return this.tableIndex; + } ++ ++ @Override ++ public final void moonrise$init(final Collection states) { ++ this.optimisedTable.loadInTable(states); ++ ++ // de-duplicate the tables and remove values, properties, neighbours arrays ++ for (final S neighbour : states) { ++ ((StateHolder)(Object)(StateHolder)neighbour).optimisedTable = this.optimisedTable; ++ ((StateHolder)(Object)(StateHolder)neighbour).propertyKeys = null; ++ ((StateHolder)(Object)(StateHolder)neighbour).propertyValues = null; ++ ((StateHolder)(Object)(StateHolder)neighbour).neighbors = null; ++ } ++ } + // Paper end - optimise blockstate property access + - protected StateHolder(O owner, Reference2ObjectArrayMap, Comparable> values, MapCodec propertiesCodec) { + protected StateHolder(final O owner, final Property[] propertyKeys, final Comparable[] propertyValues) { + assert propertyKeys.length == propertyValues.length; + this.owner = owner; - this.values = values; - this.propertiesCodec = propertiesCodec; + this.propertyKeys = propertyKeys; + this.propertyValues = propertyValues; ++ + // Paper start - optimise blockstate property access -+ this.optimisedTable = new ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.util.ZeroCollidingReferenceStateTable<>(this.values.keySet()); -+ this.tableIndex = this.optimisedTable.getIndex((StateHolder)(Object)this); ++ this.optimisedTable = new ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.util.ZeroCollidingReferenceStateTable<>(propertyKeys); ++ this.tableIndex = this.optimisedTable.getIndex((StateHolder)(Object)this, propertyKeys, propertyValues); + // Paper end - optimise blockstate property access } - public > S cycle(Property property) { -@@ -77,20 +91,21 @@ public abstract class StateHolder { + public > S cycle(final Property property) { +@@ -63,7 +91,7 @@ public abstract class StateHolder { } public Collection> getProperties() { -- return Collections.unmodifiableCollection(this.values.keySet()); +- return List.of(this.propertyKeys); + return this.optimisedTable.getProperties(); // Paper - optimise blockstate property access } - public boolean hasProperty(Property property) { -- return this.values.containsKey(property); + private int valueIndex(final Property property) { +@@ -77,21 +105,21 @@ public abstract class StateHolder { + } + + public boolean hasProperty(final Property property) { +- return this.valueIndex(property) != -1; + return property != null && this.optimisedTable.hasProperty(property); // Paper - optimise blockstate property access } - public > T getValue(Property property) { -- Comparable comparable = this.values.get(property); -- if (comparable == null) { + private > @Nullable T getNullableValue(final Property property) { +- int index = this.valueIndex(property); +- return index == -1 ? null : property.getValueClass().cast(this.propertyValues[index]); ++ return property == null ? null : this.optimisedTable.get(this.tableIndex, property); // Paper - optimise blockstate property access + } + + public > T getValue(final Property property) { +- T value = this.getNullableValue(property); +- if (value == null) { - throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner); - } else { -- return property.getValueClass().cast(comparable); +- return value; + // Paper start - optimise blockstate property access + final T ret = this.optimisedTable.get(this.tableIndex, property); + if (ret != null) { @@ -31295,22 +32141,16 @@ index abb2189902af888ac30510d5088ea77fb9e0c542..c020d1944a25e2cd247e30a97c35bf93 + // Paper end - optimise blockstate property access } - public > Optional getOptionalValue(Property property) { -@@ -102,22 +117,30 @@ public abstract class StateHolder { - } - - private > @Nullable T getNullableValue(Property property) { -- Comparable comparable = this.values.get(property); -- return comparable == null ? null : property.getValueClass().cast(comparable); -+ return property == null ? null : this.optimisedTable.get(this.tableIndex, property); // Paper - optimise blockstate property access + public > Optional getOptionalValue(final Property property) { +@@ -103,17 +131,26 @@ public abstract class StateHolder { } - public , V extends T> S setValue(Property property, V value) { -- Comparable comparable = this.values.get(property); -- if (comparable == null) { + public , V extends T> S setValue(final Property property, final V value) { +- int index = this.valueIndex(property); +- if (index == -1) { - throw new IllegalArgumentException("Cannot set property " + property + " as it does not exist in " + this.owner); - } else { -- return this.setValueInternal(property, value, comparable); +- return this.setValueInternal(property, index, value); + // Paper start - optimise blockstate property access + final S ret = this.optimisedTable.set(this.tableIndex, property, value); + if (ret != null) { @@ -31320,9 +32160,9 @@ index abb2189902af888ac30510d5088ea77fb9e0c542..c020d1944a25e2cd247e30a97c35bf93 + // Paper end - optimise blockstate property access } - public , V extends T> S trySetValue(Property property, V value) { -- Comparable comparable = this.values.get(property); -- return (S)(comparable == null ? this : this.setValueInternal(property, value, comparable)); + public , V extends T> S trySetValue(final Property property, final V value) { +- int index = this.valueIndex(property); +- return (S)(index == -1 ? this : this.setValueInternal(property, index, value)); + // Paper start - optimise blockstate property access + if (property == null) { + return (S)(StateHolder)(Object)this; @@ -31335,63 +32175,28 @@ index abb2189902af888ac30510d5088ea77fb9e0c542..c020d1944a25e2cd247e30a97c35bf93 + // Paper end - optimise blockstate property access } - private , V extends T> S setValueInternal(Property property, V value, Comparable comparable) { -@@ -134,21 +157,27 @@ public abstract class StateHolder { - } - - public void populateNeighbours(Map, Comparable>, S> possibleStateMap) { -- if (this.neighbours != null) { -- throw new IllegalStateException(); -- } else { -- Map, S[]> map = new Reference2ObjectArrayMap<>(this.values.size()); -- -- for (Entry, Comparable> entry : this.values.entrySet()) { -- Property property = entry.getKey(); -- map.put( -- property, -- (S[]) property.getPossibleValues().stream().map(comparable -> possibleStateMap.get(this.makeNeighbourValues(property, comparable))).toArray() -- ); -- } -+ // Paper start - optimise blockstate property access -+ final Map, Comparable>, S> map = possibleStateMap; -+ if (this.optimisedTable.isLoaded()) { -+ return; -+ } -+ this.optimisedTable.loadInTable(map); - -- this.neighbours = map; -+ // de-duplicate the tables -+ for (final Map.Entry, Comparable>, S> entry : map.entrySet()) { -+ final S value = entry.getValue(); -+ ((StateHolder)value).optimisedTable = this.optimisedTable; - } -+ -+ // remove values arrays -+ for (final Map.Entry, Comparable>, S> entry : map.entrySet()) { -+ final S value = entry.getValue(); -+ ((StateHolder)value).values = null; -+ } -+ -+ return; -+ // Paper end optimise blockstate property access + private , V extends T> S setValueInternal(final Property property, final int propertyIndex, final V value) { +@@ -134,12 +171,15 @@ public abstract class StateHolder { } - private Map, Comparable> makeNeighbourValues(Property property, Comparable value) { -@@ -158,7 +187,11 @@ public abstract class StateHolder { + public boolean isSingletonState() { +- return this.propertyKeys.length == 0; ++ return this.optimisedTable.isSingletonState(); // Paper - optimise blockstate property access } - public Map, Comparable> getValues() { -- return this.values; + public Stream> getValues() { +- int length = this.propertyKeys.length; +- return length == 0 ? Stream.empty() : IntStream.range(0, length).mapToObj(i -> createValue(this.propertyKeys[i], this.propertyValues[i])); + // Paper start - optimise blockstate property access -+ ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.util.ZeroCollidingReferenceStateTable table = this.optimisedTable; -+ // We have to use this.values until the table is loaded -+ return table.isLoaded() ? table.getMapView(this.tableIndex) : this.values; ++ return this.optimisedTable.getProperties().stream().map((final Property prop) -> { ++ return createValue(prop, StateHolder.this.getValue(prop)); ++ }); + // Paper end - optimise blockstate property access } - protected static > Codec codec(Codec ownerCodec, Function defaultStateGetter) { + public static > Property.Value createValue(final Property propertyKey, final Comparable propertyValue) { // Paper - public diff --git a/net/minecraft/world/level/block/state/properties/BooleanProperty.java b/net/minecraft/world/level/block/state/properties/BooleanProperty.java -index 40c83ff614169be8ab988f3ab476eca93acee28d..654f14e0fd1920ec94300649719c2460918899e2 100644 +index 87b922c2aa4ef44a2a3469314f599dd63ee97baa..eac3775777060809b0e2ed118c12e647c4de8194 100644 --- a/net/minecraft/world/level/block/state/properties/BooleanProperty.java +++ b/net/minecraft/world/level/block/state/properties/BooleanProperty.java @@ -3,13 +3,23 @@ package net.minecraft.world.level.block.state.properties; @@ -31413,14 +32218,14 @@ index 40c83ff614169be8ab988f3ab476eca93acee28d..654f14e0fd1920ec94300649719c2460 + } + // Paper end - optimise blockstate property access + - private BooleanProperty(String name) { + private BooleanProperty(final String name) { super(name, Boolean.class); + this.moonrise$setById(BY_ID); // Paper - optimise blockstate property access } @Override diff --git a/net/minecraft/world/level/block/state/properties/EnumProperty.java b/net/minecraft/world/level/block/state/properties/EnumProperty.java -index c56728863a084a5e1f6e6d9489d00bb0c83af168..1d785b7bb046ef291342efa3ede6cdeb460f12fb 100644 +index b718c4f81740b34e4321654d26e45f4c5bdb2450..09ccb96400aa25bd7fbcc1564fcc9b2b0e518bd6 100644 --- a/net/minecraft/world/level/block/state/properties/EnumProperty.java +++ b/net/minecraft/world/level/block/state/properties/EnumProperty.java @@ -10,11 +10,39 @@ import java.util.function.Predicate; @@ -31461,19 +32266,19 @@ index c56728863a084a5e1f6e6d9489d00bb0c83af168..1d785b7bb046ef291342efa3ede6cdeb + } + // Paper end - optimise blockstate property access + - private EnumProperty(String name, Class clazz, List values) { + private EnumProperty(final String name, final Class clazz, final List values) { super(name, clazz); if (values.isEmpty()) { @@ -37,6 +65,7 @@ public final class EnumProperty & StringRepresentable> extends - this.names = builder.buildOrThrow(); + this.names = names.buildOrThrow(); } + this.init(); // Paper - optimise blockstate property access } @Override diff --git a/net/minecraft/world/level/block/state/properties/IntegerProperty.java b/net/minecraft/world/level/block/state/properties/IntegerProperty.java -index 28a15908420cb239c317d58f7e3a1df3c6278b33..b7543eb5a8f87bc7bd275ed9d46a68072c1e57b5 100644 +index f837d8029127e74f7181aa183e341a47ee8125a9..b0f90ce47b60cc775cece33ec7d2d30101574b16 100644 --- a/net/minecraft/world/level/block/state/properties/IntegerProperty.java +++ b/net/minecraft/world/level/block/state/properties/IntegerProperty.java @@ -5,11 +5,33 @@ import java.util.List; @@ -31508,7 +32313,7 @@ index 28a15908420cb239c317d58f7e3a1df3c6278b33..b7543eb5a8f87bc7bd275ed9d46a6807 + } + // Paper end - optimise blockstate property access + - private IntegerProperty(String name, int min, int max) { + private IntegerProperty(final String name, final int min, final int max) { super(name, Integer.class); if (min < 0) { @@ -21,6 +43,7 @@ public final class IntegerProperty extends Property { @@ -31520,7 +32325,7 @@ index 28a15908420cb239c317d58f7e3a1df3c6278b33..b7543eb5a8f87bc7bd275ed9d46a6807 @Override diff --git a/net/minecraft/world/level/block/state/properties/Property.java b/net/minecraft/world/level/block/state/properties/Property.java -index aa836c652aecda582772c016e62abfce32d32fe5..1200466d8718fb35205894fc09584d5104e56f43 100644 +index 8970be0f45631e0710ccfbf45a25caf7717d0988..22d309410f5752a5f6b356e4f5cbda86444423f6 100644 --- a/net/minecraft/world/level/block/state/properties/Property.java +++ b/net/minecraft/world/level/block/state/properties/Property.java @@ -10,7 +10,7 @@ import java.util.stream.Stream; @@ -31564,36 +32369,36 @@ index aa836c652aecda582772c016e62abfce32d32fe5..1200466d8718fb35205894fc09584d51 + public abstract int moonrise$getIdFor(final T value); + // Paper end - optimise blockstate property access + - protected Property(String name, Class clazz) { + protected Property(final String name, final Class clazz) { this.clazz = clazz; this.name = name; + this.id = ID_GENERATOR.getAndIncrement(); // Paper - optimise blockstate property access } - public Property.Value value(T value) { + public Property.Value value(final T value) { diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java -index 9dc13a5b6cc1afc118a261802c71da25ae577fe5..a49a06662de4062a77112e358f536d45d65bf91f 100644 +index 28a0f11af688c8bca4b1132b4de2977ee32e19db..c974b1c276d29610cb59566afaad19f5bcf0c602 100644 --- a/net/minecraft/world/level/chunk/ChunkAccess.java +++ b/net/minecraft/world/level/chunk/ChunkAccess.java @@ -57,7 +57,7 @@ import net.minecraft.world.ticks.TickContainerAccess; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; --public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, LightChunk, StructureAccess { -+public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, LightChunk, StructureAccess, ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk { // Paper - rewrite chunk system +-public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeManager.NoiseBiomeSource { ++public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeManager.NoiseBiomeSource, ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk { // Paper - rewrite chunk system public static final int NO_FILLED_SECTION = -1; private static final Logger LOGGER = LogUtils.getLogger(); private static final LongSet EMPTY_REFERENCE_SET = new LongOpenHashSet(); -@@ -72,7 +72,7 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh +@@ -72,7 +72,7 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeM protected final UpgradeData upgradeData; - protected @Nullable BlendingData blendingData; + protected final @Nullable BlendingData blendingData; public final Map heightmaps = Maps.newEnumMap(Heightmap.Types.class); - protected ChunkSkyLightSources skyLightSources; + // Paper - rewrite chunk system private final Map structureStarts = Maps.newHashMap(); private final Map structuresRefences = Maps.newHashMap(); protected final Map pendingBlockEntities = Maps.newHashMap(); -@@ -84,6 +84,57 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh +@@ -84,6 +84,57 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeM public org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer(ChunkAccess.DATA_TYPE_REGISTRY); // CraftBukkit end @@ -31649,9 +32454,9 @@ index 9dc13a5b6cc1afc118a261802c71da25ae577fe5..a49a06662de4062a77112e358f536d45 + // Paper end - get block chunk optimisation + public ChunkAccess( - ChunkPos chunkPos, - UpgradeData upgradeData, -@@ -101,7 +152,7 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + final ChunkPos chunkPos, + final UpgradeData upgradeData, +@@ -101,7 +152,7 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeM this.inhabitedTime = inhabitedTime; this.postProcessing = new ShortList[levelHeightAccessor.getSectionsCount()]; this.blendingData = blendingData; @@ -31660,7 +32465,7 @@ index 9dc13a5b6cc1afc118a261802c71da25ae577fe5..a49a06662de4062a77112e358f536d45 if (sections != null) { if (this.sections.length == sections.length) { System.arraycopy(sections, 0, this.sections, 0, this.sections.length); -@@ -111,6 +162,16 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh +@@ -111,6 +162,16 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeM } replaceMissingSections(containerFactory, this.sections); @@ -31676,25 +32481,25 @@ index 9dc13a5b6cc1afc118a261802c71da25ae577fe5..a49a06662de4062a77112e358f536d45 + // Paper end - get block chunk optimisation } - private static void replaceMissingSections(PalettedContainerFactory containerFactory, LevelChunkSection[] sections) { -@@ -433,18 +494,22 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + private static void replaceMissingSections(final PalettedContainerFactory containerFactory, final LevelChunkSection[] sections) { +@@ -433,18 +494,22 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeM @Override - public Holder getNoiseBiome(int x, int y, int z) { + public Holder getNoiseBiome(final int quartX, final int quartY, final int quartZ) { - try { -- int quartPosMinY = QuartPos.fromBlock(this.getMinY()); -- int i = quartPosMinY + QuartPos.fromBlock(this.getHeight()) - 1; -- int i1 = Mth.clamp(y, quartPosMinY, i); -- int sectionIndex = this.getSectionIndex(QuartPos.toBlock(i1)); -- return this.sections[sectionIndex].getNoiseBiome(x & 3, i1 & 3, z & 3); +- int quartMinY = QuartPos.fromBlock(this.getMinY()); +- int quartMaxY = quartMinY + QuartPos.fromBlock(this.getHeight()) - 1; +- int clampedQuartY = Mth.clamp(quartY, quartMinY, quartMaxY); +- int sectionIndex = this.getSectionIndex(QuartPos.toBlock(clampedQuartY)); +- return this.sections[sectionIndex].getNoiseBiome(quartX & 3, clampedQuartY & 3, quartZ & 3); - } catch (Throwable var8) { -- CrashReport crashReport = CrashReport.forThrowable(var8, "Getting biome"); -- CrashReportCategory crashReportCategory = crashReport.addCategory("Biome being got"); -- crashReportCategory.setDetail("Location", () -> CrashReportCategory.formatLocation(this, x, y, z)); -- throw new ReportedException(crashReport); +- CrashReport report = CrashReport.forThrowable(var8, "Getting biome"); +- CrashReportCategory category = report.addCategory("Biome being got"); +- category.setDetail("Location", () -> CrashReportCategory.formatLocation(this, quartX, quartY, quartZ)); +- throw new ReportedException(report); + // Paper start - get block chunk optimisation -+ int sectionY = (y >> 2) - this.minSection; -+ int rel = y & 3; ++ int sectionY = (quartY >> 2) - this.minSection; ++ int rel = quartY & 3; + + final LevelChunkSection[] sections = this.sections; + @@ -31706,12 +32511,12 @@ index 9dc13a5b6cc1afc118a261802c71da25ae577fe5..a49a06662de4062a77112e358f536d45 + rel = 3; } + -+ return sections[sectionY].getNoiseBiome(x & 3, rel, z & 3); ++ return sections[sectionY].getNoiseBiome(quartX & 3, rel, quartZ & 3); + // Paper end - get block chunk optimisation } + // CraftBukkit start - public void setBiome(int x, int y, int z, Holder biome) { -@@ -493,12 +558,12 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh +@@ -494,12 +559,12 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeM } public void initializeLightSources() { @@ -31725,31 +32530,31 @@ index 9dc13a5b6cc1afc118a261802c71da25ae577fe5..a49a06662de4062a77112e358f536d45 + return null; // Paper - rewrite chunk system } - public static ProblemReporter.PathElement problemPath(ChunkPos pos) { + public static ProblemReporter.PathElement problemPath(final ChunkPos pos) { diff --git a/net/minecraft/world/level/chunk/ChunkGenerator.java b/net/minecraft/world/level/chunk/ChunkGenerator.java -index faf8f53bdcaa69fbbd816739dd4f1e812ced2165..7b98964eb77a619bdaaf5a9d331e3cfe080b52a4 100644 +index 1b0eb74970c4d89ce8de4ca7bb86d420e396540d..a5d61f84dbf2937162abdfc1692f0848b971a4ce 100644 --- a/net/minecraft/world/level/chunk/ChunkGenerator.java +++ b/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -116,7 +116,7 @@ public abstract class ChunkGenerator { +@@ -119,7 +119,7 @@ public abstract class ChunkGenerator { return CompletableFuture.supplyAsync(() -> { - chunk.fillBiomesFromNoise(this.biomeSource, randomState.sampler()); - return chunk; + protoChunk.fillBiomesFromNoise(this.biomeSource, randomState.sampler()); + return protoChunk; - }, Util.backgroundExecutor().forName("init_biomes")); -+ }, Runnable::run); // Paper - rewrite chunk system ++ }, Runnable::run); // Paper - rewrite chunk system } public abstract void applyCarvers( -@@ -314,7 +314,7 @@ public abstract class ChunkGenerator { - return Pair.of(placement.getLocatePos(chunkPos), holder); +@@ -316,7 +316,7 @@ public abstract class ChunkGenerator { + return Pair.of(config.getLocatePos(chunkTarget), structure); } -- ChunkAccess chunk = level.getChunk(chunkPos.x, chunkPos.z, ChunkStatus.STRUCTURE_STARTS); -+ ChunkAccess chunk = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader)level).moonrise$syncLoadNonFull(chunkPos.x, chunkPos.z, ChunkStatus.STRUCTURE_STARTS); // Paper - rewrite chunk system - StructureStart startForStructure = structureManager.getStartForStructure(SectionPos.bottomOf(chunk), holder.value(), chunk); - if (startForStructure != null && startForStructure.isValid() && (!skipKnownStructures || tryAddReference(structureManager, startForStructure))) { - return Pair.of(placement.getLocatePos(startForStructure.getChunkPos()), holder); +- ChunkAccess chunk = level.getChunk(chunkTarget.x(), chunkTarget.z(), ChunkStatus.STRUCTURE_STARTS); ++ ChunkAccess chunk = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader)level).moonrise$syncLoadNonFull(chunkTarget.x(), chunkTarget.z(), ChunkStatus.STRUCTURE_STARTS); // Paper - rewrite chunk system + StructureStart start = structureManager.getStartForStructure(SectionPos.bottomOf(chunk), structure.value(), chunk); + if (start != null && start.isValid() && (!createReference || tryAddReference(structureManager, start))) { + return Pair.of(config.getLocatePos(start.getChunkPos()), structure); diff --git a/net/minecraft/world/level/chunk/EmptyLevelChunk.java b/net/minecraft/world/level/chunk/EmptyLevelChunk.java -index 4925c35f89b2e9ec099c66168621521129dd9035..bb8c2e1fb91a5921b5fe35c78c584f81f0189e13 100644 +index 0fa297225d75ce30f9c8cf231c0da17a41fa3099..2ad29bdbcad25e0fde49fced56b97f6f52ce0109 100644 --- a/net/minecraft/world/level/chunk/EmptyLevelChunk.java +++ b/net/minecraft/world/level/chunk/EmptyLevelChunk.java @@ -14,7 +14,7 @@ import net.minecraft.world.level.material.FluidState; @@ -31760,7 +32565,7 @@ index 4925c35f89b2e9ec099c66168621521129dd9035..bb8c2e1fb91a5921b5fe35c78c584f81 +public class EmptyLevelChunk extends LevelChunk implements ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk { // Paper - rewrite chunk system private final Holder biome; - public EmptyLevelChunk(Level level, ChunkPos pos, Holder biome) { + public EmptyLevelChunk(final Level level, final ChunkPos pos, final Holder biome) { @@ -22,6 +22,40 @@ public class EmptyLevelChunk extends LevelChunk { this.biome = biome; } @@ -31800,10 +32605,10 @@ index 4925c35f89b2e9ec099c66168621521129dd9035..bb8c2e1fb91a5921b5fe35c78c584f81 + // Paper end - rewrite chunk system + @Override - public BlockState getBlockState(BlockPos pos) { + public BlockState getBlockState(final BlockPos pos) { return Blocks.VOID_AIR.defaultBlockState(); diff --git a/net/minecraft/world/level/chunk/HashMapPalette.java b/net/minecraft/world/level/chunk/HashMapPalette.java -index 0841918bd2c7b95151baa5cfabc9d44b37b0c588..5b9346b25aa01db9f3c36e243f46846b87a6eb75 100644 +index fde0a0e4b2a3b2bd4f2ac893703c70dd55b1ddc2..3ee35885ea1f997bb5a36dd8d5f83b168883ba61 100644 --- a/net/minecraft/world/level/chunk/HashMapPalette.java +++ b/net/minecraft/world/level/chunk/HashMapPalette.java @@ -8,10 +8,17 @@ import net.minecraft.network.FriendlyByteBuf; @@ -31822,11 +32627,11 @@ index 0841918bd2c7b95151baa5cfabc9d44b37b0c588..5b9346b25aa01db9f3c36e243f46846b + } + // Paper end - optimise palette reads + - public HashMapPalette(int bits, List values) { + public HashMapPalette(final int bits, final List values) { this(bits); values.forEach(this.values::add); diff --git a/net/minecraft/world/level/chunk/ImposterProtoChunk.java b/net/minecraft/world/level/chunk/ImposterProtoChunk.java -index 8c5585c5a961b03853849f92e2811425391e5405..ba91dc3b8b4d70af32b7d9323f22c1fafabe913f 100644 +index 54d57f80b1cca351cac2aa59477a0e57334816e7..94652b00e18ffcbe02f1ad0578d671e2a1d1d16a 100644 --- a/net/minecraft/world/level/chunk/ImposterProtoChunk.java +++ b/net/minecraft/world/level/chunk/ImposterProtoChunk.java @@ -29,7 +29,7 @@ import net.minecraft.world.ticks.BlackholeTickAccess; @@ -31885,22 +32690,22 @@ index 8c5585c5a961b03853849f92e2811425391e5405..ba91dc3b8b4d70af32b7d9323f22c1fa + // Paper end - rewrite chunk system + @Override - public @Nullable BlockEntity getBlockEntity(BlockPos pos) { + public @Nullable BlockEntity getBlockEntity(final BlockPos pos) { return this.wrapped.getBlockEntity(pos); diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index ca8b498c707b29e2c1524cc07d01dd19ac110053..ea6a9306cf7b0c1b0a4f69eb2f1d604c95efb967 100644 +index 85470ac2128087f212f7a58cccbc308524a43ea2..b754f87698ff263c63f2a3ee20661b82e9d16e2c 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java -@@ -63,7 +63,7 @@ import net.minecraft.world.ticks.TickContainerAccess; +@@ -64,7 +64,7 @@ import net.minecraft.world.ticks.TickContainerAccess; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; -public class LevelChunk extends ChunkAccess implements DebugValueSource { +public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk, ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk, ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk { // Paper - rewrite chunk system // Paper - get block chunk optimisation - static final Logger LOGGER = LogUtils.getLogger(); + private static final Logger LOGGER = LogUtils.getLogger(); private static final TickingBlockEntity NULL_TICKER = new TickingBlockEntity() { @Override -@@ -102,6 +102,39 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { +@@ -103,6 +103,39 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { // Paper start boolean loadedTicketLevel; // Paper end @@ -31938,9 +32743,9 @@ index ca8b498c707b29e2c1524cc07d01dd19ac110053..ea6a9306cf7b0c1b0a4f69eb2f1d604c + } + // Paper end - get block chunk optimisation - public LevelChunk(Level level, ChunkPos pos) { + public LevelChunk(final Level level, final ChunkPos pos) { this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null); -@@ -131,6 +164,14 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { +@@ -132,6 +165,14 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { this.postLoad = postLoad; this.blockTicks = blockTicks; this.fluidTicks = fluidTicks; @@ -31954,38 +32759,38 @@ index ca8b498c707b29e2c1524cc07d01dd19ac110053..ea6a9306cf7b0c1b0a4f69eb2f1d604c + // Paper end - get block chunk optimisation } - public LevelChunk(ServerLevel level, ProtoChunk chunk, LevelChunk.@Nullable PostLoadProcessor postLoad) { -@@ -168,13 +209,19 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { + public LevelChunk(final ServerLevel level, final ProtoChunk protoChunk, final LevelChunk.@Nullable PostLoadProcessor postLoad) { +@@ -169,13 +210,19 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { } } -- this.skyLightSources = chunk.skyLightSources; +- this.skyLightSources = protoChunk.skyLightSources; + // Paper - rewrite chunk system - this.setLightCorrect(chunk.isLightCorrect()); + this.setLightCorrect(protoChunk.isLightCorrect()); this.markUnsaved(); this.needsDecoration = true; // CraftBukkit // CraftBukkit start - this.persistentDataContainer = chunk.persistentDataContainer; // SPIGOT-6814: copy PDC to account for 1.17 to 1.18 chunk upgrading. + this.persistentDataContainer = protoChunk.persistentDataContainer; // SPIGOT-6814: copy PDC to account for 1.17 to 1.18 chunk upgrading. // CraftBukkit end + // Paper start - rewrite chunk system -+ this.starlight$setBlockNibbles(((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getBlockNibbles()); -+ this.starlight$setSkyNibbles(((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getSkyNibbles()); -+ this.starlight$setSkyEmptinessMap(((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getSkyEmptinessMap()); -+ this.starlight$setBlockEmptinessMap(((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getBlockEmptinessMap()); ++ this.starlight$setBlockNibbles(((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)protoChunk).starlight$getBlockNibbles()); ++ this.starlight$setSkyNibbles(((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)protoChunk).starlight$getSkyNibbles()); ++ this.starlight$setSkyEmptinessMap(((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)protoChunk).starlight$getSkyEmptinessMap()); ++ this.starlight$setBlockEmptinessMap(((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)protoChunk).starlight$getBlockEmptinessMap()); + // Paper end - rewrite chunk system } - public void setUnsavedListener(LevelChunk.UnsavedListener unsavedListener) { -@@ -345,7 +392,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { - if (LightEngine.hasDifferentLightProperties(blockState, state)) { - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("updateSkyLightSources"); -- this.skyLightSources.update(this, i, y, i2); + public void setUnsavedListener(final LevelChunk.UnsavedListener unsavedListener) { +@@ -346,7 +393,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { + if (LightEngine.hasDifferentLightProperties(oldState, state)) { + ProfilerFiller profiler = Profiler.get(); + profiler.push("updateSkyLightSources"); +- this.skyLightSources.update(this, localX, y, localZ); + // Paper - rewrite chunk system - profilerFiller.popPush("queueCheckLight"); + profiler.popPush("queueCheckLight"); this.level.getChunkSource().getLightEngine().checkBlock(pos); - profilerFiller.pop(); -@@ -588,11 +635,12 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { + profiler.pop(); +@@ -584,11 +631,12 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { // CraftBukkit start public void loadCallback() { @@ -31999,7 +32804,7 @@ index ca8b498c707b29e2c1524cc07d01dd19ac110053..ea6a9306cf7b0c1b0a4f69eb2f1d604c if (server != null) { /* * If it's a new world, the first few chunks are generated inside -@@ -601,6 +649,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { +@@ -597,6 +645,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { */ org.bukkit.Chunk bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(bukkitChunk, this.needsDecoration)); @@ -32007,7 +32812,7 @@ index ca8b498c707b29e2c1524cc07d01dd19ac110053..ea6a9306cf7b0c1b0a4f69eb2f1d604c if (this.needsDecoration) { this.needsDecoration = false; -@@ -627,13 +676,15 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { +@@ -623,13 +672,15 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { } public void unloadCallback() { @@ -32025,7 +32830,7 @@ index ca8b498c707b29e2c1524cc07d01dd19ac110053..ea6a9306cf7b0c1b0a4f69eb2f1d604c // Paper start this.loadedTicketLevel = false; // Paper end -@@ -641,8 +692,31 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { +@@ -637,8 +688,31 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource { @Override public boolean isUnsaved() { @@ -32065,12 +32870,12 @@ index ca8b498c707b29e2c1524cc07d01dd19ac110053..ea6a9306cf7b0c1b0a4f69eb2f1d604c + this.postProcessingDone = true; // Paper - rewrite chunk system } - private @Nullable BlockEntity promotePendingBlockEntity(BlockPos pos, CompoundTag tag) { + private @Nullable BlockEntity promotePendingBlockEntity(final BlockPos pos, final CompoundTag tag) { diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java -index c721edb770a46403da38a5cdcff241a1c50cb32d..5a76746a27d04cc095585655f2ef19ac10528071 100644 +index edfd4c7b089e69c1237db71fa0f13d6ed9565394..398018ad7037669a756f804403fd76cf333a06de 100644 --- a/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -9,7 +9,7 @@ import net.minecraft.world.level.biome.Climate; +@@ -10,7 +10,7 @@ import net.minecraft.world.level.biome.Climate; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; @@ -32079,7 +32884,7 @@ index c721edb770a46403da38a5cdcff241a1c50cb32d..5a76746a27d04cc095585655f2ef19ac public static final int SECTION_WIDTH = 16; public static final int SECTION_HEIGHT = 16; public static final int SECTION_SIZE = 4096; -@@ -20,6 +20,30 @@ public class LevelChunkSection { +@@ -22,6 +22,30 @@ public class LevelChunkSection { public final PalettedContainer states; private PalettedContainer> biomes; // CraftBukkit - read/write @@ -32107,11 +32912,11 @@ index c721edb770a46403da38a5cdcff241a1c50cb32d..5a76746a27d04cc095585655f2ef19ac + } + // Paper end - block counting + - private LevelChunkSection(LevelChunkSection section) { - this.nonEmptyBlockCount = section.nonEmptyBlockCount; - this.tickingBlockCount = section.tickingBlockCount; -@@ -59,6 +83,45 @@ public class LevelChunkSection { - return this.setBlockState(x, y, z, state, true); + private LevelChunkSection(final LevelChunkSection source) { + this.nonEmptyBlockCount = source.nonEmptyBlockCount; + this.fluidCount = source.fluidCount; +@@ -62,6 +86,45 @@ public class LevelChunkSection { + return this.setBlockState(sectionX, sectionY, sectionZ, state, true); } + // Paper start - block counting @@ -32153,51 +32958,35 @@ index c721edb770a46403da38a5cdcff241a1c50cb32d..5a76746a27d04cc095585655f2ef19ac + } + // Paper end - block counting + - public BlockState setBlockState(int x, int y, int z, BlockState state, boolean useLocks) { - BlockState blockState; - if (useLocks) { -@@ -76,7 +139,7 @@ public class LevelChunkSection { + public BlockState setBlockState(final int sectionX, final int sectionY, final int sectionZ, final BlockState state, final boolean checkThreading) { + BlockState previous; + if (checkThreading) { +@@ -100,6 +163,8 @@ public class LevelChunkSection { } } -- if (!fluidState.isEmpty()) { -+ if (!!fluidState.isRandomlyTicking()) { // Paper - block counting - this.tickingFluidCount--; - } - -@@ -87,10 +150,12 @@ public class LevelChunkSection { - } - } - -- if (!fluidState1.isEmpty()) { -+ if (!!fluidState1.isRandomlyTicking()) { // Paper - block counting - this.tickingFluidCount++; - } - -+ this.updateBlockCallback(x, y, z, state, blockState); // Paper - block counting ++ this.updateBlockCallback(sectionX, sectionY, sectionZ, state, previous); // Paper - block counting + - return blockState; + return previous; } -@@ -111,35 +176,70 @@ public class LevelChunkSection { +@@ -124,42 +189,71 @@ public class LevelChunkSection { } public void recalcBlockCounts() { - class BlockCounter implements PalettedContainer.CountConsumer { - public int nonEmptyBlockCount; +- public int fluidCount; - public int tickingBlockCount; - public int tickingFluidCount; - -- @Override -- public void accept(BlockState state, int count) { -- FluidState fluidState = state.getFluidState(); -- if (!state.isAir()) { -- this.nonEmptyBlockCount += count; -- if (state.isRandomlyTicking()) { -- this.tickingBlockCount += count; +- BlockCounter() { +- Objects.requireNonNull(LevelChunkSection.this); +- super(); + // Paper start - block counting + // reset, then recalculate + this.nonEmptyBlockCount = (short)0; ++ this.fluidCount = (short)0; + this.tickingBlockCount = (short)0; + this.tickingFluidCount = (short)0; + this.specialCollidingBlocks = (short)0; @@ -32215,8 +33004,14 @@ index c721edb770a46403da38a5cdcff241a1c50cb32d..5a76746a27d04cc095585655f2ef19ac + counts.put(0, FULL_LIST); + } else { + counts = ((ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage)storage).moonrise$countEntries(); -+ } -+ + } + +- @Override +- public void accept(final BlockState state, final int count) { +- if (!state.isAir()) { +- this.nonEmptyBlockCount += count; +- if (state.isRandomlyTicking()) { +- this.tickingBlockCount += count; + for (final java.util.Iterator> iterator = counts.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) { + final it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry entry = iterator.next(); + final int paletteIdx = entry.getIntKey(); @@ -32234,28 +33029,30 @@ index c721edb770a46403da38a5cdcff241a1c50cb32d..5a76746a27d04cc095585655f2ef19ac + } + this.nonEmptyBlockCount += (short)paletteCount; + if (state.isRandomlyTicking()) { -+ this.tickingBlockCount += (short)paletteCount; ++ this.tickingBlockCount += (short) paletteCount; + final short[] raw = coordinates.elements(); + final int rawLen = raw.length; + + final ca.spottedleaf.moonrise.common.list.ShortList tickingBlocks = this.tickingBlocks; + -+ tickingBlocks.setMinCapacity(Math.min((rawLen + tickingBlocks.size()) * 3 / 2, 16*16*16)); ++ tickingBlocks.setMinCapacity(Math.min((rawLen + tickingBlocks.size()) * 3 / 2, 16 * 16 * 16)); + + java.util.Objects.checkFromToIndex(0, paletteCount, raw.length); + for (int i = 0; i < paletteCount; ++i) { + tickingBlocks.add(raw[i]); } - } ++ } -- if (!fluidState.isEmpty()) { -- this.nonEmptyBlockCount += count; -- if (fluidState.isRandomlyTicking()) { -- this.tickingFluidCount += count; +- FluidState fluid = state.getFluidState(); +- if (!fluid.isEmpty()) { +- this.fluidCount += count; +- if (fluid.isRandomlyTicking()) { +- this.tickingFluidCount += count; +- } + final FluidState fluid = state.getFluidState(); + + if (!fluid.isEmpty()) { -+ //this.nonEmptyBlockCount += count; // fix vanilla bug: make non-empty block count correct ++ this.fluidCount += (short)paletteCount; + if (fluid.isRandomlyTicking()) { + this.tickingFluidCount += (short)paletteCount; } @@ -32266,16 +33063,17 @@ index c721edb770a46403da38a5cdcff241a1c50cb32d..5a76746a27d04cc095585655f2ef19ac - BlockCounter blockCounter = new BlockCounter(); - this.states.count(blockCounter); - this.nonEmptyBlockCount = (short)blockCounter.nonEmptyBlockCount; +- this.fluidCount = (short)blockCounter.fluidCount; - this.tickingBlockCount = (short)blockCounter.tickingBlockCount; - this.tickingFluidCount = (short)blockCounter.tickingFluidCount; + // Paper end - block counting } public PalettedContainer getStates() { -@@ -156,6 +256,11 @@ public class LevelChunkSection { - PalettedContainer> palettedContainer = this.biomes.recreate(); - palettedContainer.read(buffer); - this.biomes = palettedContainer; +@@ -177,6 +271,11 @@ public class LevelChunkSection { + PalettedContainer> biomes = this.biomes.recreate(); + biomes.read(buffer); + this.biomes = biomes; + // Paper start - block counting + this.isClient = true; + // force has special colliding blocks to be true @@ -32283,9 +33081,9 @@ index c721edb770a46403da38a5cdcff241a1c50cb32d..5a76746a27d04cc095585655f2ef19ac + // Paper end - block counting } - public void readBiomes(FriendlyByteBuf buffer) { + public void readBiomes(final FriendlyByteBuf buffer) { diff --git a/net/minecraft/world/level/chunk/LinearPalette.java b/net/minecraft/world/level/chunk/LinearPalette.java -index 31acbee00a7c044e062cb1251dc3ccbbf6fc59c0..c93302f2457c71caae6f56c59d3a66357a0a6cd6 100644 +index 51761bba718b5bc58524454137d7e526c74ce5d3..58f03cf8b32b141dc7df72568ef902eed53f4a5d 100644 --- a/net/minecraft/world/level/chunk/LinearPalette.java +++ b/net/minecraft/world/level/chunk/LinearPalette.java @@ -7,11 +7,18 @@ import net.minecraft.network.FriendlyByteBuf; @@ -32305,11 +33103,11 @@ index 31acbee00a7c044e062cb1251dc3ccbbf6fc59c0..c93302f2457c71caae6f56c59d3a6635 + } + // Paper end - optimise palette reads + - private LinearPalette(int bits, List values) { + private LinearPalette(final int bits, final List paletteEntries) { this.values = (T[])(new Object[1 << bits]); this.bits = bits; diff --git a/net/minecraft/world/level/chunk/Palette.java b/net/minecraft/world/level/chunk/Palette.java -index 8ac6109c37cd83c87e4257ed2f33b2352d072702..43679751e0251f9ccab9d617be67285472f60885 100644 +index 89f2176747094340565e579d6106b3a702150b4b..2c53d464df5f141b83b3a589de597d82eeb42ddb 100644 --- a/net/minecraft/world/level/chunk/Palette.java +++ b/net/minecraft/world/level/chunk/Palette.java @@ -5,7 +5,7 @@ import java.util.function.Predicate; @@ -32318,11 +33116,11 @@ index 8ac6109c37cd83c87e4257ed2f33b2352d072702..43679751e0251f9ccab9d617be672854 -public interface Palette { +public interface Palette extends ca.spottedleaf.moonrise.patches.fast_palette.FastPalette { // Paper - optimise palette reads - int idFor(T state, PaletteResize resizeHandler); + int idFor(T value, PaletteResize resizeHandler); - boolean maybeHas(Predicate filter); + boolean maybeHas(Predicate predicate); diff --git a/net/minecraft/world/level/chunk/PalettedContainer.java b/net/minecraft/world/level/chunk/PalettedContainer.java -index 889d2db9ae9a8d5ca7b150431f1d3c4892a241d9..0740775f2fa49c4313c1de6dd00f0a39fe2e0463 100644 +index 7337cbd5ee2cba2cd0cf21f2b9628e9f8e1e70fc..edf2990bfbe3d6b64eed85446eef207d2506efc5 100644 --- a/net/minecraft/world/level/chunk/PalettedContainer.java +++ b/net/minecraft/world/level/chunk/PalettedContainer.java @@ -24,7 +24,7 @@ import org.jspecify.annotations.Nullable; @@ -32334,8 +33132,8 @@ index 889d2db9ae9a8d5ca7b150431f1d3c4892a241d9..0740775f2fa49c4313c1de6dd00f0a39 private final Strategy strategy; //private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused -@@ -63,20 +63,50 @@ public class PalettedContainer implements PaletteResize, PalettedContainer - .comapFlatMap(packedData -> unpacker.read(strategy, (PalettedContainerRO.PackedData)packedData), container -> container.pack(strategy)); +@@ -64,20 +64,50 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + ); } + // Paper start - optimise palette reads @@ -32365,58 +33163,58 @@ index 889d2db9ae9a8d5ca7b150431f1d3c4892a241d9..0740775f2fa49c4313c1de6dd00f0a39 + } + // Paper end - optimise palette reads + - private PalettedContainer(Strategy strategy, Configuration configuration, BitStorage storage, Palette palette) { + private PalettedContainer(final Strategy strategy, final Configuration dataConfiguration, final BitStorage storage, final Palette palette) { this.strategy = strategy; - this.data = new PalettedContainer.Data<>(configuration, storage, palette); + this.data = new PalettedContainer.Data<>(dataConfiguration, storage, palette); + this.updateData(this.data); // Paper - optimise palette reads } - private PalettedContainer(PalettedContainer other) { - this.strategy = other.strategy; - this.data = other.data.copy(); + private PalettedContainer(final PalettedContainer source) { + this.strategy = source.strategy; + this.data = source.data.copy(); + this.updateData(this.data); // Paper - optimise palette reads } - public PalettedContainer(T defaultValue, Strategy strategy) { + public PalettedContainer(final T initialValue, final Strategy strategy) { this.strategy = strategy; this.data = this.createOrReuseData(null, 0); - this.data.palette.idFor(defaultValue, this); + this.data.palette.idFor(initialValue, this); + this.updateData(this.data); // Paper - optimise palette reads } - private PalettedContainer.Data createOrReuseData(PalettedContainer.@Nullable Data data, int bits) { -@@ -98,6 +128,7 @@ public class PalettedContainer implements PaletteResize, PalettedContainer - PalettedContainer.Data data1 = this.createOrReuseData(data, bits); - data1.copyFrom(data.palette, data.storage); - this.data = data1; + private PalettedContainer.Data createOrReuseData(final PalettedContainer.@Nullable Data oldData, final int targetBits) { +@@ -99,6 +129,7 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + PalettedContainer.Data newData = this.createOrReuseData(oldData, bits); + newData.copyFrom(oldData.palette, oldData.storage); + this.data = newData; + this.updateData(this.data); // Paper - optimise palette reads - return data1.palette.idFor(addedValue, PaletteResize.noResizeExpected()); + return newData.palette.idFor(lastAddedValue, PaletteResize.noResizeExpected()); } -@@ -119,9 +150,12 @@ public class PalettedContainer implements PaletteResize, PalettedContainer +@@ -120,9 +151,12 @@ public class PalettedContainer implements PaletteResize, PalettedContainer } - private T getAndSet(int index, T state) { -- int i = this.data.palette.idFor(state, this); -- int andSet = this.data.storage.getAndSet(index, i); -- return this.data.palette.valueFor(andSet); + private T getAndSet(final int index, final T value) { +- int id = this.data.palette.idFor(value, this); +- int oldId = this.data.storage.getAndSet(index, id); +- return this.data.palette.valueFor(oldId); + // Paper start - optimise palette reads -+ final int paletteIdx = this.data.palette.idFor(state, this); ++ final int paletteIdx = this.data.palette.idFor(value, this); + final PalettedContainer.Data data = this.data; + final int prev = data.storage.getAndSet(index, paletteIdx); + return this.readPalette(data, prev); + // Paper end - optimise palette reads } - public synchronized void set(int x, int y, int z, T state) { // Paper - synchronize -@@ -144,9 +178,11 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + public synchronized void set(final int x, final int y, final int z, final T value) { // Paper - synchronize +@@ -145,9 +179,11 @@ public class PalettedContainer implements PaletteResize, PalettedContainer return this.get(this.strategy.getIndex(x, y, z)); } -- protected T get(int index) { +- protected T get(final int index) { - PalettedContainer.Data data = this.data; - return data.palette.valueFor(data.storage.get(index)); -+ public T get(int index) { // Paper - public ++ public T get(final int index) { // Paper - public + // Paper start - optimise palette reads + final PalettedContainer.Data data = this.data; + return this.readPalette(data, data.storage.get(index)); @@ -32424,19 +33222,19 @@ index 889d2db9ae9a8d5ca7b150431f1d3c4892a241d9..0740775f2fa49c4313c1de6dd00f0a39 } @Override -@@ -166,6 +202,7 @@ public class PalettedContainer implements PaletteResize, PalettedContainer - data.palette.read(buffer, this.strategy.globalMap()); - buffer.readFixedSizeLongArray(data.storage.getRaw()); - this.data = data; +@@ -167,6 +203,7 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + newData.palette.read(buffer, this.strategy.globalMap()); + buffer.readFixedSizeLongArray(newData.storage.getRaw()); + this.data = newData; + this.updateData(this.data); // Paper - optimise palette reads } finally { this.release(); } -@@ -315,7 +352,44 @@ public class PalettedContainer implements PaletteResize, PalettedContainer - void accept(T state, int count); +@@ -316,7 +353,44 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + void accept(final T entry, final int count); } -- record Data(Configuration configuration, BitStorage storage, Palette palette) { +- private record Data(Configuration configuration, BitStorage storage, Palette palette) { + // Paper start - optimise palette reads + public static final class Data implements ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData { + @@ -32475,24 +33273,24 @@ index 889d2db9ae9a8d5ca7b150431f1d3c4892a241d9..0740775f2fa49c4313c1de6dd00f0a39 + } + // Paper end - optimise palette reads + - public void copyFrom(Palette palette, BitStorage bitStorage) { - PaletteResize paletteResize = PaletteResize.noResizeExpected(); + public void copyFrom(final Palette oldPalette, final BitStorage oldStorage) { + PaletteResize dummyResizer = PaletteResize.noResizeExpected(); diff --git a/net/minecraft/world/level/chunk/ProtoChunk.java b/net/minecraft/world/level/chunk/ProtoChunk.java -index ff2a34c83fbcc27e87a85bcaab411b042c85dc0b..fd923d89d4456f0b13d97ef6fa90815a27e2e734 100644 +index e2208bc7b58b0241192b1b79e3ac1aa0bb35f354..d3bde6eb0edd24d5e5352388788d30938712a9c7 100644 --- a/net/minecraft/world/level/chunk/ProtoChunk.java +++ b/net/minecraft/world/level/chunk/ProtoChunk.java @@ -154,7 +154,7 @@ public class ProtoChunk extends ChunkAccess { } - if (LightEngine.hasDifferentLightProperties(blockState, state)) { -- this.skyLightSources.update(this, relativeBlockPosX, y, relativeBlockPosZ); + if (LightEngine.hasDifferentLightProperties(oldState, state)) { +- this.skyLightSources.update(this, localX, y, localZ); + // Paper - rewrite chunk system this.lightEngine.checkBlock(pos); } } diff --git a/net/minecraft/world/level/chunk/SingleValuePalette.java b/net/minecraft/world/level/chunk/SingleValuePalette.java -index 18a006890c466b84c32537745ea249662ac2aaf0..e0ed8499a90eb350cc483001ea10f2c8e9173875 100644 +index 7a9f737d03b5bf61c857e28539d7e09cb36be46f..c180673b37b07282419e315e8449e2ab73bbbc98 100644 --- a/net/minecraft/world/level/chunk/SingleValuePalette.java +++ b/net/minecraft/world/level/chunk/SingleValuePalette.java @@ -8,9 +8,21 @@ import net.minecraft.network.VarInt; @@ -32515,16 +33313,16 @@ index 18a006890c466b84c32537745ea249662ac2aaf0..e0ed8499a90eb350cc483001ea10f2c8 + } + // Paper end - optimise palette reads + - public SingleValuePalette(List values) { - if (!values.isEmpty()) { - Validate.isTrue(values.size() <= 1, "Can't initialize SingleValuePalette with %d values.", (long)values.size()); + public SingleValuePalette(final List paletteEntries) { + if (!paletteEntries.isEmpty()) { + Validate.isTrue(paletteEntries.size() <= 1, "Can't initialize SingleValuePalette with %d values.", (long)paletteEntries.size()); @@ -28,6 +40,11 @@ public class SingleValuePalette implements Palette { - return resizeHandler.onResize(1, state); + return resizeHandler.onResize(1, value); } else { - this.value = state; + this.value = value; + // Paper start - optimise palette reads + if (this.rawPalette != null) { -+ this.rawPalette[0] = state; ++ this.rawPalette[0] = value; + } + // Paper end - optimise palette reads return 0; @@ -32532,8 +33330,8 @@ index 18a006890c466b84c32537745ea249662ac2aaf0..e0ed8499a90eb350cc483001ea10f2c8 } @@ -53,6 +70,11 @@ public class SingleValuePalette implements Palette { @Override - public void read(FriendlyByteBuf buffer, IdMap map) { - this.value = map.byIdOrThrow(buffer.readVarInt()); + public void read(final FriendlyByteBuf buffer, final IdMap globalMap) { + this.value = globalMap.byIdOrThrow(buffer.readVarInt()); + // Paper start - optimise palette reads + if (this.rawPalette != null) { + this.rawPalette[0] = this.value; @@ -32543,20 +33341,20 @@ index 18a006890c466b84c32537745ea249662ac2aaf0..e0ed8499a90eb350cc483001ea10f2c8 @Override diff --git a/net/minecraft/world/level/chunk/status/ChunkPyramid.java b/net/minecraft/world/level/chunk/status/ChunkPyramid.java -index 9c6f4aa173fa25f9c8a3852d91a4585e069236b6..b14001afe0bf841dac7d0a1d1568fd10f6086237 100644 +index 4f71c44e4fbf48e0225ce2617c128a9c188351d6..967fd05cf5db5ae2d255a72ba15c6cfaa0e79ec5 100644 --- a/net/minecraft/world/level/chunk/status/ChunkPyramid.java +++ b/net/minecraft/world/level/chunk/status/ChunkPyramid.java -@@ -54,7 +54,7 @@ public record ChunkPyramid(ImmutableList steps) { - .step(ChunkStatus.CARVERS, builder -> builder) - .step(ChunkStatus.FEATURES, builder -> builder) - .step(ChunkStatus.INITIALIZE_LIGHT, builder -> builder.setTask(ChunkStatusTasks::initializeLight)) -- .step(ChunkStatus.LIGHT, builder -> builder.addRequirement(ChunkStatus.INITIALIZE_LIGHT, 1).setTask(ChunkStatusTasks::light)) -+ .step(ChunkStatus.LIGHT, builder -> builder.setTask(ChunkStatusTasks::light)) // Paper - rewrite chunk system - starlight does not need neighbours - .step(ChunkStatus.SPAWN, builder -> builder) - .step(ChunkStatus.FULL, builder -> builder.setTask(ChunkStatusTasks::full)) +@@ -48,7 +48,7 @@ public record ChunkPyramid(ImmutableList steps) { + .step(ChunkStatus.CARVERS, s -> s) + .step(ChunkStatus.FEATURES, s -> s) + .step(ChunkStatus.INITIALIZE_LIGHT, s -> s.setTask(ChunkStatusTasks::initializeLight)) +- .step(ChunkStatus.LIGHT, s -> s.addRequirement(ChunkStatus.INITIALIZE_LIGHT, 1).setTask(ChunkStatusTasks::light)) ++ .step(ChunkStatus.LIGHT, s -> s.setTask(ChunkStatusTasks::light)) // Paper - rewrite chunk system - starlight does not need neighbours + .step(ChunkStatus.SPAWN, s -> s) + .step(ChunkStatus.FULL, s -> s.setTask(ChunkStatusTasks::full)) .build(); diff --git a/net/minecraft/world/level/chunk/status/ChunkStatus.java b/net/minecraft/world/level/chunk/status/ChunkStatus.java -index 204b99f48ba8e2fd724a43ede73abc41a4147872..a04f20297c984a31216cbeb1c8d049728c9b6a65 100644 +index eb09a55d08e8e910f10e6bd54a1f24bcdbb51c69..1927341832adaf70e1130e205a39f4bfe13df15b 100644 --- a/net/minecraft/world/level/chunk/status/ChunkStatus.java +++ b/net/minecraft/world/level/chunk/status/ChunkStatus.java @@ -12,7 +12,7 @@ import net.minecraft.resources.Identifier; @@ -32568,7 +33366,7 @@ index 204b99f48ba8e2fd724a43ede73abc41a4147872..a04f20297c984a31216cbeb1c8d04972 public static final int MAX_STRUCTURE_DISTANCE = 8; private static final EnumSet WORLDGEN_HEIGHTMAPS = EnumSet.of(Heightmap.Types.OCEAN_FLOOR_WG, Heightmap.Types.WORLD_SURFACE_WG); public static final EnumSet FINAL_HEIGHTMAPS = EnumSet.of( -@@ -53,8 +53,70 @@ public class ChunkStatus { +@@ -55,8 +55,70 @@ public class ChunkStatus { return list; } @@ -32626,7 +33424,7 @@ index 204b99f48ba8e2fd724a43ede73abc41a4147872..a04f20297c984a31216cbeb1c8d04972 + // Paper end - rewrite chunk system + @VisibleForTesting - protected ChunkStatus(@Nullable ChunkStatus parent, EnumSet heightmapsAfter, ChunkType chunkType) { + protected ChunkStatus(final @Nullable ChunkStatus parent, final EnumSet heightmapsAfter, final ChunkType chunkType) { + // Paper start - rewrite chunk system + this.isParallelCapable = false; + this.writeRadius = -1; @@ -32640,27 +33438,27 @@ index 204b99f48ba8e2fd724a43ede73abc41a4147872..a04f20297c984a31216cbeb1c8d04972 this.chunkType = chunkType; this.heightmapsAfter = heightmapsAfter; diff --git a/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java b/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java -index 98e85820ac5aad77a003be4b6f7a59346524ae81..d620862e95abf9f098bb53a76825883f2b1f4bbf 100644 +index 8d3eb23eb607c862e15fb9e2e7f71a2019b02d04..6eb1a3a83c5bae8935c042396d676f7a3450e135 100644 --- a/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java +++ b/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java -@@ -193,7 +193,7 @@ public class ChunkStatusTasks { +@@ -192,7 +192,7 @@ public class ChunkStatusTasks { } else { - wrapped = new LevelChunk(serverLevel, protoChunk, chunk1 -> { - try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(chunk.problemPath(), LOGGER)) { -- postLoadProtoChunk(serverLevel, TagValueInput.create(scopedCollector, serverLevel.registryAccess(), protoChunk.getEntities())); -+ postLoadProtoChunk(serverLevel, TagValueInput.create(scopedCollector, serverLevel.registryAccess(), protoChunk.getEntities()), protoChunk.getPos()); // Paper - rewrite chunk system + levelChunk = new LevelChunk(level, protoChunk, lc -> { + try (ProblemReporter.ScopedCollector reporter = new ProblemReporter.ScopedCollector(chunk.problemPath(), LOGGER)) { +- postLoadProtoChunk(level, TagValueInput.create(reporter, level.registryAccess(), protoChunk.getEntities())); ++ postLoadProtoChunk(level, TagValueInput.create(reporter, level.registryAccess(), protoChunk.getEntities()), protoChunk.getPos()); // Paper - rewrite chunk system } }); - generationChunkHolder.replaceProtoChunk(new ImposterProtoChunk(wrapped, false)); -@@ -209,12 +209,12 @@ public class ChunkStatusTasks { - }, worldGenContext.mainThreadExecutor()); + holder.replaceProtoChunk(new ImposterProtoChunk(levelChunk, false)); +@@ -208,12 +208,12 @@ public class ChunkStatusTasks { + }, context.mainThreadExecutor()); } -- public static void postLoadProtoChunk(ServerLevel level, ValueInput.ValueInputList input) { -+ public static void postLoadProtoChunk(ServerLevel level, ValueInput.ValueInputList input, ChunkPos pos) { // Paper - rewrite chunk system - add ChunkPos param - if (!input.isEmpty()) { +- public static void postLoadProtoChunk(final ServerLevel level, final ValueInput.ValueInputList entities) { ++ public static void postLoadProtoChunk(final ServerLevel level, final ValueInput.ValueInputList entities, final ChunkPos pos) { // Paper - rewrite chunk system - add ChunkPos param + if (!entities.isEmpty()) { // Paper start - duplicate uuid resolving - level.addWorldGenChunkEntities(EntityType.loadEntitiesRecursive(input, level, EntitySpawnReason.LOAD).filter((entity) -> { + level.addWorldGenChunkEntities(EntityType.loadEntitiesRecursive(entities, level, EntitySpawnReason.LOAD).filter((entity) -> { return !checkDupeUUID(level, entity); - })); + }), pos); // Paper - rewrite chunk system @@ -32668,7 +33466,7 @@ index 98e85820ac5aad77a003be4b6f7a59346524ae81..d620862e95abf9f098bb53a76825883f } } diff --git a/net/minecraft/world/level/chunk/status/ChunkStep.java b/net/minecraft/world/level/chunk/status/ChunkStep.java -index e29ad470f868fd26259bc0c2dbd6e0c1eb97bae4..aa8c40d49756611537e2698b7f2a861c303ef842 100644 +index 3e381c26fe9a646c73704515f57ba09d9533f37e..06d5de6ccdd07b1645f656170863b419d8d23fc5 100644 --- a/net/minecraft/world/level/chunk/status/ChunkStep.java +++ b/net/minecraft/world/level/chunk/status/ChunkStep.java @@ -11,9 +11,50 @@ import net.minecraft.world.level.chunk.ChunkAccess; @@ -32720,13 +33518,13 @@ index e29ad470f868fd26259bc0c2dbd6e0c1eb97bae4..aa8c40d49756611537e2698b7f2a861c + } + // Paper end - rewrite chunk system + -+ // Paper start - rewerite chunk system - convert record to class ++ // Paper end - rewerite chunk system - convert record to class + - public int getAccumulatedRadiusOf(ChunkStatus status) { + public int getAccumulatedRadiusOf(final ChunkStatus status) { return status == this.targetStatus ? 0 : this.accumulatedDependencies.getRadiusOf(status); } -@@ -40,6 +81,56 @@ public record ChunkStep( - return chunk; +@@ -39,6 +80,56 @@ public record ChunkStep( + return newCenterChunk; } + // Paper start - rewerite chunk system - convert record to class @@ -32783,10 +33581,10 @@ index e29ad470f868fd26259bc0c2dbd6e0c1eb97bae4..aa8c40d49756611537e2698b7f2a861c private final ChunkStatus status; private final @Nullable ChunkStep parent; diff --git a/net/minecraft/world/level/chunk/storage/IOWorker.java b/net/minecraft/world/level/chunk/storage/IOWorker.java -index 17c91f6aa109b3cba51637e8499602d47e7a8649..b2b7c20a25bdefd42e8a36eb29935b8d5b24925d 100644 +index cc5d366f7b485f98901572b1aaf57d8b650d521f..7adb4be57bd9fdc9980b5b7e91e9d9ca4d24a726 100644 --- a/net/minecraft/world/level/chunk/storage/IOWorker.java +++ b/net/minecraft/world/level/chunk/storage/IOWorker.java -@@ -31,7 +31,7 @@ public class IOWorker implements ChunkScanAccess, AutoCloseable { +@@ -31,7 +31,7 @@ public class IOWorker implements AutoCloseable, ChunkScanAccess { private static final Logger LOGGER = LogUtils.getLogger(); private final AtomicBoolean shutdownRequested = new AtomicBoolean(); private final PriorityConsecutiveExecutor consecutiveExecutor; @@ -32796,10 +33594,10 @@ index 17c91f6aa109b3cba51637e8499602d47e7a8649..b2b7c20a25bdefd42e8a36eb29935b8d private final Long2ObjectLinkedOpenHashMap> regionCacheForBlender = new Long2ObjectLinkedOpenHashMap<>(); private static final int REGION_CACHE_SIZE = 1024; diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java -index f5d24254c84876f85d89e33381fae484817acf04..99bbae1f8f681950f410b2bfe9af037164d8dcb7 100644 +index 728ec122b7af090427cc7511a168336d539835a1..5149635679ec03d881e63effbd79fc193d7e52c8 100644 --- a/net/minecraft/world/level/chunk/storage/RegionFile.java +++ b/net/minecraft/world/level/chunk/storage/RegionFile.java -@@ -22,7 +22,7 @@ import net.minecraft.world.level.ChunkPos; +@@ -23,7 +23,7 @@ import net.minecraft.world.level.ChunkPos; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; @@ -32808,7 +33606,7 @@ index f5d24254c84876f85d89e33381fae484817acf04..99bbae1f8f681950f410b2bfe9af0371 private static final Logger LOGGER = LogUtils.getLogger(); private static final int SECTOR_BYTES = 4096; @VisibleForTesting -@@ -45,6 +45,21 @@ public class RegionFile implements AutoCloseable { +@@ -46,6 +46,21 @@ public class RegionFile implements AutoCloseable { @VisibleForTesting protected final RegionBitmap usedSectors = new RegionBitmap(); @@ -32827,34 +33625,31 @@ index f5d24254c84876f85d89e33381fae484817acf04..99bbae1f8f681950f410b2bfe9af0371 + } + // Paper end - rewrite chunk system + - public RegionFile(RegionStorageInfo info, Path path, Path externalFileDir, boolean sync) throws IOException { + public RegionFile(final RegionStorageInfo info, final Path path, final Path externalFileDir, final boolean sync) throws IOException { this(info, path, externalFileDir, RegionFileVersion.getCompressionFormat(), sync); // Paper - Configurable region compression format } -@@ -199,7 +214,17 @@ public class RegionFile implements AutoCloseable { - } +@@ -201,6 +216,15 @@ public class RegionFile implements AutoCloseable { } -- private @Nullable DataInputStream createExternalChunkInputStream(ChunkPos chunkPos, byte versionByte) throws IOException { -+ @Nullable -+ private DataInputStream createExternalChunkInputStream(ChunkPos chunkPos, byte versionByte) throws IOException { + private @Nullable DataInputStream createExternalChunkInputStream(final ChunkPos pos, final byte versionId) throws IOException { + // Paper start - rewrite chunk system -+ final DataInputStream is = this.createExternalChunkInputStream0(chunkPos, versionByte); ++ final DataInputStream is = this.createExternalChunkInputStream0(pos, versionId); + if (is == null) { + return is; + } + return new ca.spottedleaf.moonrise.patches.chunk_system.util.stream.ExternalChunkStreamMarker(is); + } -+ private @Nullable DataInputStream createExternalChunkInputStream0(ChunkPos chunkPos, byte versionByte) throws IOException { ++ private @Nullable DataInputStream createExternalChunkInputStream0(final ChunkPos pos, final byte versionId) throws IOException { + // Paper end - rewrite chunk system - Path externalChunkPath = this.getExternalChunkPath(chunkPos); - if (!Files.isRegularFile(externalChunkPath)) { - LOGGER.error("External chunk path {} is not file", externalChunkPath); -@@ -394,9 +419,28 @@ public class RegionFile implements AutoCloseable { + Path externalFile = this.getExternalChunkPath(pos); + if (!Files.isRegularFile(externalFile)) { + LOGGER.error("External chunk path {} is not file", externalFile); +@@ -395,9 +419,28 @@ public class RegionFile implements AutoCloseable { } } -- class ChunkBuffer extends ByteArrayOutputStream { -+ class ChunkBuffer extends ByteArrayOutputStream implements ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemChunkBuffer { // Paper - rewrite chunk system +- private class ChunkBuffer extends ByteArrayOutputStream { ++ private class ChunkBuffer extends ByteArrayOutputStream implements ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemChunkBuffer { // Paper - rewrite chunk system private final ChunkPos pos; + // Paper start - rewrite chunk system @@ -32877,19 +33672,19 @@ index f5d24254c84876f85d89e33381fae484817acf04..99bbae1f8f681950f410b2bfe9af0371 + // Paper end - rewrite chunk system + public ChunkBuffer(final ChunkPos pos) { + Objects.requireNonNull(RegionFile.this); super(8096); - super.write(0); -@@ -413,7 +457,7 @@ public class RegionFile implements AutoCloseable { - int i = this.count - 5 + 1; - JvmProfiler.INSTANCE.onRegionFileWrite(RegionFile.this.info, this.pos, RegionFile.this.version, i); - byteBuffer.putInt(0, i); -- RegionFile.this.write(this.pos, byteBuffer); -+ if (this.writeOnClose) { RegionFile.this.write(this.pos, byteBuffer); } // Paper - rewrite chunk system +@@ -415,7 +458,7 @@ public class RegionFile implements AutoCloseable { + int streamLength = this.count - 5 + 1; + JvmProfiler.INSTANCE.onRegionFileWrite(RegionFile.this.info, this.pos, RegionFile.this.version, streamLength); + result.putInt(0, streamLength); +- RegionFile.this.write(this.pos, result); ++ if (this.writeOnClose) { RegionFile.this.write(this.pos, result); } // Paper - rewrite chunk system } } diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -index 95a717797dc1bba5595e8305f21b193b2e1702cc..5d9168242f7a7ca4dd888f32fecb27457fd89456 100644 +index 4473503da2459016859b2d72ecd7825df23f4be4..e9316cc08fe4cb4af28583a8cb1ec658243bffd0 100644 --- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java @@ -15,7 +15,7 @@ import net.minecraft.util.FileUtil; @@ -32901,11 +33696,11 @@ index 95a717797dc1bba5595e8305f21b193b2e1702cc..5d9168242f7a7ca4dd888f32fecb2745 public static final String ANVIL_EXTENSION = ".mca"; private static final int MAX_CACHE_SIZE = 256; public final Long2ObjectLinkedOpenHashMap regionCache = new Long2ObjectLinkedOpenHashMap<>(); -@@ -23,29 +23,218 @@ public final class RegionFileStorage implements AutoCloseable { +@@ -23,30 +23,222 @@ public final class RegionFileStorage implements AutoCloseable { private final Path folder; private final boolean sync; -- RegionFileStorage(RegionStorageInfo info, Path folder, boolean sync) { +- RegionFileStorage(final RegionStorageInfo info, final Path folder, final boolean sync) { + // Paper start - rewrite chunk system + private static final int REGION_SHIFT = 5; + private static final int MAX_NON_EXISTING_CACHE = 1024 * 4; @@ -32942,17 +33737,17 @@ index 95a717797dc1bba5595e8305f21b193b2e1702cc..5d9168242f7a7ca4dd888f32fecb2745 + + @Override + public final boolean moonrise$doesRegionFileNotExistNoIO(final int chunkX, final int chunkZ) { -+ return !this.doesRegionFilePossiblyExist(ChunkPos.asLong(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT)); ++ return !this.doesRegionFilePossiblyExist(ChunkPos.pack(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT)); + } + + @Override + public synchronized final RegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ) { -+ return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT)); ++ return this.regionCache.getAndMoveToFirst(ChunkPos.pack(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT)); + } + + @Override + public synchronized final RegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException { -+ final long key = ChunkPos.asLong(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT); ++ final long key = ChunkPos.pack(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT); + + RegionFile ret = this.regionCache.getAndMoveToFirst(key); + if (ret != null) { @@ -32963,7 +33758,9 @@ index 95a717797dc1bba5595e8305f21b193b2e1702cc..5d9168242f7a7ca4dd888f32fecb2745 + return null; + } + -+ if (this.regionCache.size() >= io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize) { // Paper ++ final int cacheSize = io.papermc.paper.configuration.GlobalConfiguration.get() == null ? 256 : io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize; // Paper - Sanitise RegionFileCache and make configurable - Config not available during initial FileFixerUpper run ++ ++ if (this.regionCache.size() >= cacheSize) { + this.regionCache.removeLast().close(); + } + @@ -33082,45 +33879,48 @@ index 95a717797dc1bba5595e8305f21b193b2e1702cc..5d9168242f7a7ca4dd888f32fecb2745 + } + // Paper end - rewrite chunk system + -+ protected RegionFileStorage(RegionStorageInfo info, Path folder, boolean sync) { // Paper - protected ++ protected RegionFileStorage(final RegionStorageInfo info, final Path folder, final boolean sync) { // Paper - protected this.folder = folder; this.sync = sync; this.info = info; } - @org.jetbrains.annotations.Contract("_, false -> !null") @Nullable private RegionFile getRegionFile(ChunkPos chunkPos, boolean existingOnly) throws IOException { // CraftBukkit -- long packedChunkPos = ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ()); -- RegionFile regionFile = this.regionCache.getAndMoveToFirst(packedChunkPos); -- if (regionFile != null) { -- return regionFile; + @org.jetbrains.annotations.Contract("_, false -> !null") private @Nullable RegionFile getRegionFile(final ChunkPos pos, boolean existingOnly) throws IOException { // CraftBukkit +- long key = ChunkPos.pack(pos.getRegionX(), pos.getRegionZ()); +- RegionFile region = this.regionCache.getAndMoveToFirst(key); +- if (region != null) { +- return region; - } else { -- if (this.regionCache.size() >= io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize) { // Paper - Sanitise RegionFileCache and make configurable +- int cacheSize = io.papermc.paper.configuration.GlobalConfiguration.get() == null ? 256 : io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize; // Paper - Sanitise RegionFileCache and make configurable - Config not available during initial FileFixerUpper run +- if (this.regionCache.size() >= cacheSize) { // Paper - Sanitise RegionFileCache and make configurable + // Paper start - rewrite chunk system + if (existingOnly) { -+ return this.moonrise$getRegionFileIfExists(chunkPos.x, chunkPos.z); ++ return this.moonrise$getRegionFileIfExists(pos.x(), pos.z()); + } + synchronized (this) { -+ final long key = ChunkPos.asLong(chunkPos.x >> REGION_SHIFT, chunkPos.z >> REGION_SHIFT); ++ final long key = ChunkPos.pack(pos.x() >> REGION_SHIFT, pos.z() >> REGION_SHIFT); + + RegionFile ret = this.regionCache.getAndMoveToFirst(key); + if (ret != null) { + return ret; + } + -+ if (this.regionCache.size() >= io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize) { // Paper ++ final int cacheSize = io.papermc.paper.configuration.GlobalConfiguration.get() == null ? 256 : io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize; // Paper - Sanitise RegionFileCache and make configurable - Config not available during initial FileFixerUpper run ++ ++ if (this.regionCache.size() >= cacheSize) { this.regionCache.removeLast().close(); } -+ final Path regionPath = this.folder.resolve(getRegionFileName(chunkPos.x, chunkPos.z)); ++ final Path regionPath = this.folder.resolve(getRegionFileName(pos.x(), pos.z())); + + this.createRegionFile(key); + FileUtil.createDirectoriesSafe(this.folder); -- Path path = this.folder.resolve("r." + chunkPos.getRegionX() + "." + chunkPos.getRegionZ() + ".mca"); -- if (existingOnly && !java.nio.file.Files.exists(path)) return null; // CraftBukkit -- RegionFile regionFile1 = new RegionFile(this.info, path, this.folder, this.sync); -- this.regionCache.putAndMoveToFirst(packedChunkPos, regionFile1); -- return regionFile1; +- Path file = this.folder.resolve("r." + pos.getRegionX() + "." + pos.getRegionZ() + ".mca"); +- if (existingOnly && !java.nio.file.Files.exists(file)) return null; // CraftBukkit +- RegionFile newRegion = new RegionFile(this.info, file, this.folder, this.sync); +- this.regionCache.putAndMoveToFirst(key, newRegion); +- return newRegion; + + ret = new RegionFile(this.info, regionPath, this.folder, this.sync); + @@ -33131,36 +33931,36 @@ index 95a717797dc1bba5595e8305f21b193b2e1702cc..5d9168242f7a7ca4dd888f32fecb2745 + // Paper end - rewrite chunk system } - public @Nullable CompoundTag read(ChunkPos chunkPos) throws IOException { -@@ -83,9 +272,15 @@ public final class RegionFileStorage implements AutoCloseable { + public @Nullable CompoundTag read(final ChunkPos pos) throws IOException { +@@ -84,9 +276,15 @@ public final class RegionFileStorage implements AutoCloseable { } } -- protected void write(ChunkPos chunkPos, @Nullable CompoundTag chunkData) throws IOException { -+ public void write(ChunkPos chunkPos, @Nullable CompoundTag chunkData) throws IOException { // Paper - rewrite chunk system - public +- protected void write(final ChunkPos pos, final @Nullable CompoundTag value) throws IOException { ++ public void write(final ChunkPos pos, final @Nullable CompoundTag value) throws IOException { // Paper - rewrite chunk system - public if (!SharedConstants.DEBUG_DONT_SAVE_WORLD) { -- RegionFile regionFile = this.getRegionFile(chunkPos, false); // CraftBukkit -+ RegionFile regionFile = this.getRegionFile(chunkPos, chunkData == null); // CraftBukkit // Paper - rewrite chunk system +- RegionFile region = this.getRegionFile(pos, false); // CraftBukkit ++ RegionFile region = this.getRegionFile(pos, value == null); // CraftBukkit // Paper - rewrite chunk system + // Paper start - rewrite chunk system -+ if (regionFile == null) { ++ if (region == null) { + // if the RegionFile doesn't exist, no point in deleting from it + return; + } + // Paper end - rewrite chunk system - if (chunkData == null) { - regionFile.clear(chunkPos); + if (value == null) { + region.clear(pos); } else { -@@ -98,23 +293,36 @@ public final class RegionFileStorage implements AutoCloseable { +@@ -99,23 +297,36 @@ public final class RegionFileStorage implements AutoCloseable { @Override public void close() throws IOException { -- ExceptionCollector exceptionCollector = new ExceptionCollector<>(); +- ExceptionCollector exception = new ExceptionCollector<>(); - - for (RegionFile regionFile : this.regionCache.values()) { - try { - regionFile.close(); - } catch (IOException var5) { -- exceptionCollector.add(var5); +- exception.add(var5); + // Paper start - rewrite chunk system + synchronized (this) { + final ExceptionCollector exceptionCollector = new ExceptionCollector<>(); @@ -33174,7 +33974,7 @@ index 95a717797dc1bba5595e8305f21b193b2e1702cc..5d9168242f7a7ca4dd888f32fecb2745 + exceptionCollector.throwIfPresent(); } - -- exceptionCollector.throwIfPresent(); +- exception.throwIfPresent(); + // Paper end - rewrite chunk system } @@ -33199,7 +33999,7 @@ index 95a717797dc1bba5595e8305f21b193b2e1702cc..5d9168242f7a7ca4dd888f32fecb2745 public RegionStorageInfo info() { diff --git a/net/minecraft/world/level/chunk/storage/SectionStorage.java b/net/minecraft/world/level/chunk/storage/SectionStorage.java -index 7514737caf11630433752206fd614ee221f3523e..42699c82321233eaa3647ca0ac4d1f24b6a6227f 100644 +index b0b7af7e541eef20063c11ee94d7a6ceaec1626a..18be8647455dee182622f3bbefa9122547fe86ca 100644 --- a/net/minecraft/world/level/chunk/storage/SectionStorage.java +++ b/net/minecraft/world/level/chunk/storage/SectionStorage.java @@ -40,10 +40,10 @@ import net.minecraft.world.level.LevelHeightAccessor; @@ -33208,14 +34008,20 @@ index 7514737caf11630433752206fd614ee221f3523e..42699c82321233eaa3647ca0ac4d1f24 -public class SectionStorage implements AutoCloseable { +public class SectionStorage implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.level.storage.ChunkSystemSectionStorage { // Paper - rewrite chunk system - static final Logger LOGGER = LogUtils.getLogger(); + private static final Logger LOGGER = LogUtils.getLogger(); private static final String SECTIONS_TAG = "Sections"; - private final SimpleRegionStorage simpleRegionStorage; + // Paper - rewrite chunk system private final Long2ObjectMap> storage = new Long2ObjectOpenHashMap<>(); private final LongLinkedOpenHashSet dirtyChunks = new LongLinkedOpenHashSet(); private final Codec

    codec; -@@ -57,6 +57,18 @@ public class SectionStorage implements AutoCloseable { +@@ -52,11 +52,23 @@ public class SectionStorage implements AutoCloseable { + private final Function factory; + private final RegistryAccess registryAccess; + private final ChunkIOErrorReporter errorReporter; +- protected final LevelHeightAccessor levelHeightAccessor; ++ public final LevelHeightAccessor levelHeightAccessor; // Paper - public + private final LongSet loadedChunks = new LongOpenHashSet(); private final Long2ObjectMap>>> pendingLoads = new Long2ObjectOpenHashMap<>(); private final Object loadLock = new Object(); @@ -33232,11 +34038,11 @@ index 7514737caf11630433752206fd614ee221f3523e..42699c82321233eaa3647ca0ac4d1f24 + // Paper end - rewrite chunk system + public SectionStorage( - SimpleRegionStorage simpleRegionStorage, - Codec

    codec, + final SimpleRegionStorage simpleRegionStorage, + final Codec

    codec, @@ -67,7 +79,7 @@ public class SectionStorage implements AutoCloseable { - ChunkIOErrorReporter errorReporter, - LevelHeightAccessor levelHeightAccessor + final ChunkIOErrorReporter errorReporter, + final LevelHeightAccessor levelHeightAccessor ) { - this.simpleRegionStorage = simpleRegionStorage; + // Paper - rewrite chunk system @@ -33250,37 +34056,49 @@ index 7514737caf11630433752206fd614ee221f3523e..42699c82321233eaa3647ca0ac4d1f24 + this.regionStorage = ((ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemSimpleRegionStorage)simpleRegionStorage).moonrise$getRegionStorage(); // Paper - rewrite chunk system } - protected void tick(BooleanSupplier aheadOfTime) { -@@ -187,65 +200,15 @@ public class SectionStorage implements AutoCloseable { + protected void tick(final BooleanSupplier haveTime) { +@@ -117,11 +130,11 @@ public class SectionStorage implements AutoCloseable { + return !this.dirtyChunks.isEmpty(); } - private CompletableFuture>> tryRead(ChunkPos chunkPos) { +- protected @Nullable Optional get(final long sectionPos) { ++ public @Nullable Optional get(final long sectionPos) { // Paper - public + return this.storage.get(sectionPos); + } + +- protected Optional getOrLoad(final long sectionPos) { ++ public Optional getOrLoad(final long sectionPos) { // Paper - public + if (this.outsideStoredRange(sectionPos)) { + return Optional.empty(); + } else { +@@ -187,63 +200,15 @@ public class SectionStorage implements AutoCloseable { + } + + private CompletableFuture>> tryRead(final ChunkPos chunkPos) { - RegistryOps registryOps = this.registryAccess.createSerializationContext(NbtOps.INSTANCE); - return this.simpleRegionStorage - .read(chunkPos) - .thenApplyAsync( -- optional -> optional.map( -- compoundTag -> SectionStorage.PackedChunk.parse(this.codec, registryOps, compoundTag, this.simpleRegionStorage, this.levelHeightAccessor) -- ), +- result -> result.map(tag -> SectionStorage.PackedChunk.parse(this.codec, registryOps, tag, this.simpleRegionStorage, this.levelHeightAccessor)), - Util.backgroundExecutor().forName("parseSection") - ) -- .exceptionally(cause -> { -- if (cause instanceof CompletionException) { -- cause = cause.getCause(); +- .exceptionally(throwable -> { +- if (throwable instanceof CompletionException) { +- throwable = throwable.getCause(); - } - -- if (cause instanceof IOException ioException) { -- LOGGER.error("Error reading chunk {} data from disk", chunkPos, ioException); -- this.errorReporter.reportChunkLoadFailure(ioException, this.simpleRegionStorage.storageInfo(), chunkPos); +- if (throwable instanceof IOException e) { +- LOGGER.error("Error reading chunk {} data from disk", chunkPos, e); +- this.errorReporter.reportChunkLoadFailure(e, this.simpleRegionStorage.storageInfo(), chunkPos); - return Optional.empty(); - } else { -- throw new CompletionException(cause); +- throw new CompletionException(throwable); - } - }); + throw new IllegalStateException("Only chunk system can write state, offending class:" + this.getClass().getName()); // Paper - rewrite chunk system } - private void unpackChunk(ChunkPos pos, SectionStorage.@Nullable PackedChunk

    packedChunk) { + private void unpackChunk(final ChunkPos pos, final SectionStorage.@Nullable PackedChunk

    packedChunk) { - if (packedChunk == null) { - for (int sectionY = this.levelHeightAccessor.getMinSectionY(); sectionY <= this.levelHeightAccessor.getMaxSectionY(); sectionY++) { - this.storage.put(getKey(pos, sectionY), Optional.empty()); @@ -33288,12 +34106,12 @@ index 7514737caf11630433752206fd614ee221f3523e..42699c82321233eaa3647ca0ac4d1f24 - } else { - boolean versionChanged = packedChunk.versionChanged(); - -- for (int sectionY1 = this.levelHeightAccessor.getMinSectionY(); sectionY1 <= this.levelHeightAccessor.getMaxSectionY(); sectionY1++) { -- long key = getKey(pos, sectionY1); -- Optional optional = Optional.ofNullable(packedChunk.sectionsByY.get(sectionY1)) -- .map(object -> this.unpacker.apply((P)object, () -> this.setDirty(key))); -- this.storage.put(key, optional); -- optional.ifPresent(object -> { +- for (int sectionY = this.levelHeightAccessor.getMinSectionY(); sectionY <= this.levelHeightAccessor.getMaxSectionY(); sectionY++) { +- long key = getKey(pos, sectionY); +- Optional section = Optional.ofNullable(packedChunk.sectionsByY.get(sectionY)) +- .map(packed -> this.unpacker.apply((P)packed, () -> this.setDirty(key))); +- this.storage.put(key, section); +- section.ifPresent(s -> { - this.onSectionLoad(key); - if (versionChanged) { - this.setDirty(key); @@ -33304,32 +34122,32 @@ index 7514737caf11630433752206fd614ee221f3523e..42699c82321233eaa3647ca0ac4d1f24 + throw new IllegalStateException("Only chunk system can load in state, offending class:" + this.getClass().getName()); // Paper - rewrite chunk system } - private void writeChunk(ChunkPos pos) { + private void writeChunk(final ChunkPos chunkPos) { - RegistryOps registryOps = this.registryAccess.createSerializationContext(NbtOps.INSTANCE); -- Dynamic dynamic = this.writeChunk(pos, registryOps); -- Tag tag = dynamic.getValue(); -- if (tag instanceof CompoundTag compoundTag) { -- this.simpleRegionStorage.write(pos, compoundTag).exceptionally(throwable -> { -- this.errorReporter.reportChunkSaveFailure(throwable, this.simpleRegionStorage.storageInfo(), pos); +- Dynamic tag = this.writeChunk(chunkPos, registryOps); +- Tag value = tag.getValue(); +- if (value instanceof CompoundTag compoundTag) { +- this.simpleRegionStorage.write(chunkPos, compoundTag).exceptionally(throwable -> { +- this.errorReporter.reportChunkSaveFailure(throwable, this.simpleRegionStorage.storageInfo(), chunkPos); - return null; - }); - } else { -- LOGGER.error("Expected compound tag, got {}", tag); +- LOGGER.error("Expected compound tag, got {}", value); - } + throw new IllegalStateException("Only chunk system can write state, offending class:" + this.getClass().getName()); // Paper - rewrite chunk system } - private Dynamic writeChunk(ChunkPos pos, DynamicOps ops) { -@@ -281,7 +244,7 @@ public class SectionStorage implements AutoCloseable { - protected void onSectionLoad(long sectionKey) { + private Dynamic writeChunk(final ChunkPos chunkPos, final DynamicOps ops) { +@@ -279,7 +244,7 @@ public class SectionStorage implements AutoCloseable { + protected void onSectionLoad(final long sectionPos) { } -- protected void setDirty(long sectionPos) { -+ public void setDirty(long sectionPos) { // Paper - public - Optional optional = this.storage.get(sectionPos); - if (optional != null && !optional.isEmpty()) { - this.dirtyChunks.add(ChunkPos.asLong(SectionPos.x(sectionPos), SectionPos.z(sectionPos))); -@@ -298,7 +261,7 @@ public class SectionStorage implements AutoCloseable { +- protected void setDirty(final long sectionPos) { ++ public void setDirty(final long sectionPos) { // Paper - public + Optional r = this.storage.get(sectionPos); + if (r != null && !r.isEmpty()) { + this.dirtyChunks.add(ChunkPos.pack(SectionPos.x(sectionPos), SectionPos.z(sectionPos))); +@@ -296,7 +261,7 @@ public class SectionStorage implements AutoCloseable { @Override public void close() throws IOException { @@ -33337,49 +34155,40 @@ index 7514737caf11630433752206fd614ee221f3523e..42699c82321233eaa3647ca0ac4d1f24 + this.moonrise$close(); // Paper - rewrite chunk system } - record PackedChunk(Int2ObjectMap sectionsByY, boolean versionChanged) { + private record PackedChunk(Int2ObjectMap sectionsByY, boolean versionChanged) { diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -index 7fe16da65d6cf5019fa854324d43e0f97bb0b016..b2363fa364be15d0a28f5966615131282b7aaa8f 100644 +index 480623c30777fd230bf8428b16364db09806562b..22a83f312d357176a5489e5b6b71c3ef3e737df7 100644 --- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java +++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -@@ -143,7 +143,7 @@ public record SerializableChunkData( - long longOr1 = tag.getLongOr("InhabitedTime", 0L); - ChunkStatus chunkStatus = tag.read("Status", ChunkStatus.CODEC).orElse(ChunkStatus.EMPTY); - UpgradeData upgradeData = tag.getCompound("UpgradeData").map(compoundTag1 -> new UpgradeData(compoundTag1, level)).orElse(UpgradeData.EMPTY); -- boolean booleanOr = tag.getBooleanOr("isLightOn", false); -+ boolean booleanOr = chunkStatus.isOrAfter(ChunkStatus.LIGHT) && (tag.get("isLightOn") != null && tag.getIntOr(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_VERSION_TAG, -1) == ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_LIGHT_VERSION); // Paper - starlight - BlendingData.Packed packed = tag.read("blending_data", BlendingData.Packed.CODEC).orElse(null); - BelowZeroRetrogen belowZeroRetrogen = tag.read("below_zero_retrogen", BelowZeroRetrogen.CODEC).orElse(null); - long[] longs = tag.getLongArray("carving_mask").orElse(null); -@@ -183,7 +183,7 @@ public record SerializableChunkData( - for (int i2 = 0; i2 < listOrEmpty1.size(); i2++) { - Optional compound = listOrEmpty1.getCompound(i2); - if (!compound.isEmpty()) { -- CompoundTag compoundTag = compound.get(); -+ CompoundTag compoundTag = compound.get(); final CompoundTag sectionData = compoundTag; // Paper - OBFHELPER - int byteOr = compoundTag.getByteOr("Y", (byte)0); - LevelChunkSection levelChunkSection; - if (byteOr >= level.getMinSectionY() && byteOr <= level.getMaxSectionY()) { -@@ -208,7 +208,17 @@ public record SerializableChunkData( - - DataLayer dataLayer = compoundTag.getByteArray("BlockLight").map(DataLayer::new).orElse(null); - DataLayer dataLayer1 = compoundTag.getByteArray("SkyLight").map(DataLayer::new).orElse(null); -- list5.add(new SerializableChunkData.SectionData(byteOr, levelChunkSection, dataLayer, dataLayer1)); +@@ -145,7 +145,7 @@ public record SerializableChunkData( + long inhabitedTime = chunkData.getLongOr("InhabitedTime", 0L); + ChunkStatus status = chunkData.read("Status", ChunkStatus.CODEC).orElse(ChunkStatus.EMPTY); + UpgradeData upgradeData = chunkData.getCompound("UpgradeData").map(tag -> new UpgradeData(tag, levelHeight)).orElse(UpgradeData.EMPTY); +- boolean lightCorrect = chunkData.getBooleanOr("isLightOn", false); ++ boolean lightCorrect = status.isOrAfter(ChunkStatus.LIGHT) && (chunkData.get("isLightOn") != null && chunkData.getIntOr(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_VERSION_TAG, -1) == ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_LIGHT_VERSION); // Paper - starlight + BlendingData.Packed blendingData = chunkData.read("blending_data", BlendingData.Packed.CODEC).orElse(null); + BelowZeroRetrogen belowZeroRetrogen = chunkData.read("below_zero_retrogen", BelowZeroRetrogen.CODEC).orElse(null); + long[] carvingMask = chunkData.getLongArray("carving_mask").orElse(null); +@@ -210,7 +210,17 @@ public record SerializableChunkData( + + DataLayer blockLight = sectionTag.getByteArray("BlockLight").map(DataLayer::new).orElse(null); + DataLayer skyLight = sectionTag.getByteArray("SkyLight").map(DataLayer::new).orElse(null); +- sectionData.add(new SerializableChunkData.SectionData(y, section, blockLight, skyLight)); + // Paper start - starlight -+ SerializableChunkData.SectionData serializableChunkData = new SerializableChunkData.SectionData(byteOr, levelChunkSection, dataLayer, dataLayer1); -+ if (sectionData.contains(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.BLOCKLIGHT_STATE_TAG)) { -+ ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)serializableChunkData).starlight$setBlockLightState(sectionData.getIntOr(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.BLOCKLIGHT_STATE_TAG, 0)); ++ SerializableChunkData.SectionData serializableChunkData = new SerializableChunkData.SectionData(y, section, blockLight, skyLight); ++ if (sectionTag.contains(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.BLOCKLIGHT_STATE_TAG)) { ++ ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)serializableChunkData).starlight$setBlockLightState(sectionTag.getIntOr(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.BLOCKLIGHT_STATE_TAG, 0)); + } + -+ if (sectionData.contains(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.SKYLIGHT_STATE_TAG)) { -+ ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)serializableChunkData).starlight$setSkyLightState(sectionData.getIntOr(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.SKYLIGHT_STATE_TAG, 0)); ++ if (sectionTag.contains(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.SKYLIGHT_STATE_TAG)) { ++ ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)serializableChunkData).starlight$setSkyLightState(sectionTag.getIntOr(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.SKYLIGHT_STATE_TAG, 0)); + } -+ list5.add(serializableChunkData); ++ sectionData.add(serializableChunkData); + // Paper end - starlight } } -@@ -236,6 +246,59 @@ public record SerializableChunkData( +@@ -238,6 +248,59 @@ public record SerializableChunkData( } } @@ -33436,55 +34245,53 @@ index 7fe16da65d6cf5019fa854324d43e0f97bb0b016..b2363fa364be15d0a28f596661513128 + } + // Paper end - starlight + - public ProtoChunk read(ServerLevel level, PoiManager poiManager, RegionStorageInfo regionStorageInfo, ChunkPos pos) { + public ProtoChunk read(final ServerLevel level, final PoiManager poiManager, final RegionStorageInfo regionInfo, final ChunkPos pos) { if (!Objects.equals(pos, this.chunkPos)) { LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", pos, pos, this.chunkPos); -@@ -254,7 +317,7 @@ public record SerializableChunkData( - SectionPos sectionPos = SectionPos.of(pos, sectionData.y); - if (sectionData.chunkSection != null) { - levelChunkSections[level.getSectionIndexFromSectionY(sectionData.y)] = sectionData.chunkSection; -- poiManager.checkConsistencyWithBlocks(sectionPos, sectionData.chunkSection); -+ //poiManager.checkConsistencyWithBlocks(sectionPos, sectionData.chunkSection); // Paper - rewrite chunk system +@@ -256,7 +319,7 @@ public record SerializableChunkData( + SectionPos sectionPos = SectionPos.of(pos, section.y); + if (section.chunkSection != null) { + sections[level.getSectionIndexFromSectionY(section.y)] = section.chunkSection; +- poiManager.checkConsistencyWithBlocks(sectionPos, section.chunkSection); ++ //poiManager.checkConsistencyWithBlocks(sectionPos, section.chunkSection); // Paper - rewrite chunk system } - boolean flag1 = sectionData.blockLight != null; -@@ -346,7 +409,7 @@ public record SerializableChunkData( + boolean hasBlockLight = section.blockLight != null; +@@ -341,7 +404,7 @@ public record SerializableChunkData( } if (chunkType == ChunkType.LEVELCHUNK) { -- return new ImposterProtoChunk((LevelChunk)chunkAccess, false); -+ return this.loadStarlightLightData(level, new ImposterProtoChunk((LevelChunk)chunkAccess, false)); // Paper - starlight +- return new ImposterProtoChunk((LevelChunk)chunk, false); ++ return this.loadStarlightLightData(level, new ImposterProtoChunk((LevelChunk)chunk, false)); // Paper - starlight } else { - ProtoChunk protoChunk1 = (ProtoChunk)chunkAccess; + ProtoChunk protoChunkx = (ProtoChunk)chunk; -@@ -362,7 +425,7 @@ public record SerializableChunkData( - protoChunk1.setCarvingMask(new CarvingMask(this.carvingMask, chunkAccess.getMinY())); +@@ -357,7 +420,7 @@ public record SerializableChunkData( + protoChunkx.setCarvingMask(new CarvingMask(this.carvingMask, chunk.getMinY())); } -- return protoChunk1; -+ return this.loadStarlightLightData(level, protoChunk1); // Paper - starlight +- return protoChunkx; ++ return this.loadStarlightLightData(level, protoChunkx); // Paper - starlight } } -@@ -375,22 +438,48 @@ public record SerializableChunkData( - throw new IllegalArgumentException("Chunk can't be serialized: " + chunk); +@@ -371,21 +434,44 @@ public record SerializableChunkData( } else { ChunkPos pos = chunk.getPos(); -- List list = new ArrayList<>(); -+ List list = new ArrayList<>(); final List sectionsList = list; // Paper - starlight - OBFHELPER - LevelChunkSection[] sections = chunk.getSections(); - LevelLightEngine lightEngine = level.getChunkSource().getLightEngine(); - -- for (int lightSection = lightEngine.getMinLightSection(); lightSection < lightEngine.getMaxLightSection(); lightSection++) { -- int sectionIndexFromSectionY = chunk.getSectionIndexFromSectionY(lightSection); -- boolean flag = sectionIndexFromSectionY >= 0 && sectionIndexFromSectionY < sections.length; -- DataLayer dataLayerData = lightEngine.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(pos, lightSection)); -- DataLayer dataLayerData1 = lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(pos, lightSection)); -- DataLayer dataLayer = dataLayerData != null && !dataLayerData.isEmpty() ? dataLayerData.copy() : null; -- DataLayer dataLayer1 = dataLayerData1 != null && !dataLayerData1.isEmpty() ? dataLayerData1.copy() : null; -- if (flag || dataLayer != null || dataLayer1 != null) { -- LevelChunkSection levelChunkSection = flag ? sections[sectionIndexFromSectionY].copy() : null; -- list.add(new SerializableChunkData.SectionData(lightSection, levelChunkSection, dataLayer, dataLayer1)); + List sectionData = new ArrayList<>(); +- LevelChunkSection[] chunkSections = chunk.getSections(); +- LevelLightEngine lightEngine = level.getChunkSource().getLightEngine(); +- +- for (int sectionY = lightEngine.getMinLightSection(); sectionY < lightEngine.getMaxLightSection(); sectionY++) { +- int sectionIndex = chunk.getSectionIndexFromSectionY(sectionY); +- boolean hasSection = sectionIndex >= 0 && sectionIndex < chunkSections.length; +- DataLayer sourceBlockLight = lightEngine.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(pos, sectionY)); +- DataLayer sourceSkyLight = lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(pos, sectionY)); +- DataLayer blockLight = sourceBlockLight != null && !sourceBlockLight.isEmpty() ? sourceBlockLight.copy() : null; +- DataLayer skyLight = sourceSkyLight != null && !sourceSkyLight.isEmpty() ? sourceSkyLight.copy() : null; +- if (hasSection || blockLight != null || skyLight != null) { +- LevelChunkSection section = hasSection ? chunkSections[sectionIndex].copy() : null; +- sectionData.add(new SerializableChunkData.SectionData(sectionY, section, blockLight, skyLight)); + // Paper start - starlight + final int minLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinLightSection(level); + final int maxLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxLightSection(level); @@ -33506,71 +34313,62 @@ index 7fe16da65d6cf5019fa854324d43e0f97bb0b016..b2363fa364be15d0a28f596661513128 + continue; } + -+ final SerializableChunkData.SectionData sectionData = new SerializableChunkData.SectionData( ++ final SerializableChunkData.SectionData section = new SerializableChunkData.SectionData( + lightSection, chunkSection, + blockNibble == null ? null : (blockNibble.data == null ? null : new DataLayer(blockNibble.data)), + skyNibble == null ? null : (skyNibble.data == null ? null : new DataLayer(skyNibble.data)) + ); + + if (blockNibble != null) { -+ ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$setBlockLightState(blockNibble.state); ++ ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)section).starlight$setBlockLightState(blockNibble.state); + } + + if (skyNibble != null) { -+ ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$setSkyLightState(skyNibble.state); ++ ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)section).starlight$setSkyLightState(skyNibble.state); + } + -+ sectionsList.add(sectionData); ++ sectionData.add(section); } + // Paper end - starlight - List list1 = new ArrayList<>(chunk.getBlockEntitiesPos().size()); + List blockEntities = new ArrayList<>(chunk.getBlockEntitiesPos().size()); -@@ -477,7 +566,7 @@ public record SerializableChunkData( - Codec>> codec1 = this.containerFactory.biomeContainerCodec(); - - for (SerializableChunkData.SectionData sectionData : this.sectionData) { -- CompoundTag compoundTag1 = new CompoundTag(); -+ CompoundTag compoundTag1 = new CompoundTag(); final CompoundTag sectionNBT = compoundTag1; // Paper - starlight - OBFHELPER - LevelChunkSection levelChunkSection = sectionData.chunkSection; - if (levelChunkSection != null) { - compoundTag1.store("block_states", codec, levelChunkSection.getStates()); -@@ -492,6 +581,19 @@ public record SerializableChunkData( - compoundTag1.putByteArray("SkyLight", sectionData.skyLight.getData()); +@@ -487,6 +573,19 @@ public record SerializableChunkData( + sectionTag.putByteArray("SkyLight", section.skyLight.getData()); } + // Paper start - starlight -+ final int blockState = ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$getBlockLightState(); -+ final int skyState = ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$getSkyLightState(); ++ final int blockState = ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)section).starlight$getBlockLightState(); ++ final int skyState = ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)section).starlight$getSkyLightState(); + + if (blockState > 0) { -+ sectionNBT.putInt(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.BLOCKLIGHT_STATE_TAG, blockState); ++ sectionTag.putInt(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.BLOCKLIGHT_STATE_TAG, blockState); + } + + if (skyState > 0) { -+ sectionNBT.putInt(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.SKYLIGHT_STATE_TAG, skyState); ++ sectionTag.putInt(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.SKYLIGHT_STATE_TAG, skyState); + } + // Paper end - starlight + - if (!compoundTag1.isEmpty()) { - compoundTag1.putByte("Y", (byte)sectionData.y); - listTag.add(compoundTag1); -@@ -526,6 +628,14 @@ public record SerializableChunkData( - compoundTag.put("ChunkBukkitValues", this.persistentDataContainer); + if (!sectionTag.isEmpty()) { + sectionTag.putByte("Y", (byte)section.y); + sectionTags.add(sectionTag); +@@ -521,6 +620,14 @@ public record SerializableChunkData( + tag.put("ChunkBukkitValues", this.persistentDataContainer); } // CraftBukkit end + // Paper start - starlight + if (this.lightCorrect && !this.chunkStatus.isBefore(net.minecraft.world.level.chunk.status.ChunkStatus.LIGHT)) { + // clobber vanilla value to force vanilla to relight -+ compoundTag.putBoolean("isLightOn", false); ++ tag.putBoolean("isLightOn", false); + // store our light version -+ compoundTag.putInt(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_VERSION_TAG, ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_LIGHT_VERSION); ++ tag.putInt(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_VERSION_TAG, ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_LIGHT_VERSION); + } + // Paper end - starlight - return compoundTag; + return tag; } -@@ -670,6 +780,66 @@ public record SerializableChunkData( +@@ -668,6 +775,66 @@ public record SerializableChunkData( } } @@ -33639,10 +34437,10 @@ index 7fe16da65d6cf5019fa854324d43e0f97bb0b016..b2363fa364be15d0a28f596661513128 } } diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java -index ab5ee9d343ef3b3930eadf9bc17c202215c15121..ba620dafb862dd59fff85da0619dc66cc1c2cb20 100644 +index 0cde642ffaf523b44bbb33884b4fc7cad0483c95..c206c74acc80c281d06a669eb668239dc61018eb 100644 --- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java +++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java -@@ -19,8 +19,9 @@ import net.minecraft.util.datafix.DataFixTypes; +@@ -18,25 +18,39 @@ import net.minecraft.util.datafix.DataFixTypes; import net.minecraft.world.level.ChunkPos; import org.jspecify.annotations.Nullable; @@ -33653,15 +34451,6 @@ index ab5ee9d343ef3b3930eadf9bc17c202215c15121..ba620dafb862dd59fff85da0619dc66c + private final RegionFileStorage storage; // Paper - rewrite chunk system private final DataFixer fixerUpper; private final DataFixTypes dataFixType; - private final Supplier legacyFixer; -@@ -34,16 +35,29 @@ public class SimpleRegionStorage implements AutoCloseable { - ) { - this.fixerUpper = fixerUpper; - this.dataFixType = dataFixType; -- this.worker = new IOWorker(info, folder, sync); -+ this.storage = new IOWorker(info, folder, sync).storage; // Paper - rewrite chunk system - this.legacyFixer = Suppliers.memoize(legacyFixer::get); - } + // Paper start - rewrite chunk system + @Override @@ -33670,68 +34459,53 @@ index ab5ee9d343ef3b3930eadf9bc17c202215c15121..ba620dafb862dd59fff85da0619dc66c + } + // Paper end - rewrite chunk system + - public boolean isOldChunkAround(ChunkPos chunkPos, int radius) { -- return this.worker.isOldChunkAround(chunkPos, radius); + public SimpleRegionStorage( + final RegionStorageInfo info, final Path folder, final DataFixer fixerUpper, final boolean syncWrites, final DataFixTypes dataFixType + ) { + this.fixerUpper = fixerUpper; + this.dataFixType = dataFixType; +- this.worker = new IOWorker(info, folder, syncWrites); ++ this.storage = new IOWorker(info, folder, syncWrites).storage; // Paper - rewrite chunk system + } + + public boolean isOldChunkAround(final ChunkPos pos, final int range) { +- return this.worker.isOldChunkAround(pos, range); + return true; // Paper - rewrite chunk system } - public CompletableFuture> read(ChunkPos chunkPos) { -- return this.worker.loadAsync(chunkPos); + public CompletableFuture> read(final ChunkPos pos) { +- return this.worker.loadAsync(pos); + // Paper start - rewrite chunk system + try { -+ return CompletableFuture.completedFuture(Optional.ofNullable(this.storage.read(chunkPos))); ++ return CompletableFuture.completedFuture(Optional.ofNullable(this.storage.read(pos))); + } catch (final Throwable throwable) { + return CompletableFuture.failedFuture(throwable); + } + // Paper end - rewrite chunk system } - public CompletableFuture write(ChunkPos chunkPos, CompoundTag data) { -@@ -64,7 +78,14 @@ public class SimpleRegionStorage implements AutoCloseable { + public CompletableFuture write(final ChunkPos pos, final CompoundTag value) { +@@ -55,7 +69,14 @@ public class SimpleRegionStorage implements AutoCloseable { + } + return nbt; }; - // Paper end - guard against possible chunk pos desync - this.markChunkDone(chunkPos); -- return this.worker.store(chunkPos, guardedPosCheck); // Paper - guard against possible chunk pos desync +- return this.worker.store(pos, guardedPosCheck); + // Paper - rewrite chunk system + try { -+ this.storage.write(chunkPos, guardedPosCheck.get()); ++ this.storage.write(pos, guardedPosCheck.get()); + return CompletableFuture.completedFuture(null); + } catch (final Throwable throwable) { + return CompletableFuture.failedFuture(throwable); + } + // Paper end - rewrite chunk system + // Paper end - guard against possible chunk pos desync } - public CompoundTag upgradeChunkTag(CompoundTag tag, int fallbackVersion, @Nullable CompoundTag contextTag, net.minecraft.world.level.@Nullable LevelAccessor levelAccessor) { // CraftBukkit -@@ -73,7 +94,12 @@ public class SimpleRegionStorage implements AutoCloseable { - return tag; - } else { - try { -- tag = this.legacyFixer.get().applyFix(tag); -+ // Paper start - rewrite chunk system -+ final net.minecraft.world.level.chunk.storage.LegacyTagFixer legacyFixer = this.legacyFixer.get(); -+ synchronized (legacyFixer) { -+ tag = legacyFixer.applyFix(tag); -+ } -+ // Paper end - rewrite chunk system - // Spigot start - SPIGOT-6806: Quick and dirty way to prevent below zero generation in old chunks, by setting the status to heightmap instead of empty - boolean stopBelowZero = false; - final boolean chunkStorage = this.dataFixType == net.minecraft.util.datafix.DataFixTypes.CHUNK; -@@ -123,23 +149,46 @@ public class SimpleRegionStorage implements AutoCloseable { - } - - protected void markChunkDone(ChunkPos chunkPos) { -- this.legacyFixer.get().markChunkDone(chunkPos); -+ // Paper start - rewrite chunk system -+ final net.minecraft.world.level.chunk.storage.LegacyTagFixer legacyFixer = this.legacyFixer.get(); -+ synchronized (legacyFixer) { -+ legacyFixer.markChunkDone(chunkPos); -+ } -+ // Paper end - rewrite chunk system +@@ -119,19 +140,37 @@ public class SimpleRegionStorage implements AutoCloseable { } - public CompletableFuture synchronize(boolean flushStorage) { -- return this.worker.synchronize(flushStorage); + public CompletableFuture synchronize(final boolean flush) { +- return this.worker.synchronize(flush); + // Paper start - rewrite chunk system + try { + this.storage.flush(); @@ -33770,7 +34544,7 @@ index ab5ee9d343ef3b3930eadf9bc17c202215c15121..ba620dafb862dd59fff85da0619dc66c } } diff --git a/net/minecraft/world/level/entity/EntityTickList.java b/net/minecraft/world/level/entity/EntityTickList.java -index 3861282e2e55c28bd4fa0169373ce5d8b914802e..79a34ce6c888a611115719412d9a9bbcc0be498a 100644 +index b857b705d919c529f8ea62c3cece22fff9f832ae..a30409eddfaded878a3ac54bb526fd2e4b887c57 100644 --- a/net/minecraft/world/level/entity/EntityTickList.java +++ b/net/minecraft/world/level/entity/EntityTickList.java @@ -9,51 +9,38 @@ import net.minecraft.world.entity.Entity; @@ -33790,39 +34564,39 @@ index 3861282e2e55c28bd4fa0169373ce5d8b914802e..79a34ce6c888a611115719412d9a9bbc - this.passive.put(entry.getIntKey(), entry.getValue()); - } - -- Int2ObjectMap map = this.active; +- Int2ObjectMap tmp = this.active; - this.active = this.passive; -- this.passive = map; +- this.passive = tmp; - } + // Paper - rewrite chunk system } - public void add(Entity entity) { + public void add(final Entity entity) { this.ensureActiveIsNotIterated(); - this.active.put(entity.getId(), entity); + this.entities.add(entity); // Paper - rewrite chunk system } - public void remove(Entity entity) { + public void remove(final Entity entity) { this.ensureActiveIsNotIterated(); - this.active.remove(entity.getId()); + this.entities.remove(entity); // Paper - rewrite chunk system } - public boolean contains(Entity entity) { + public boolean contains(final Entity entity) { - return this.active.containsKey(entity.getId()); + return this.entities.contains(entity); // Paper - rewrite chunk system } - public void forEach(Consumer entity) { + public void forEach(final Consumer output) { - if (this.iterated != null) { - throw new UnsupportedOperationException("Only one concurrent iteration supported"); - } else { - this.iterated = this.active; - - try { -- for (Entity entity1 : this.active.values()) { -- entity.accept(entity1); +- for (Entity entity : this.active.values()) { +- output.accept(entity); - } - } finally { - this.iterated = null; @@ -33832,7 +34606,7 @@ index 3861282e2e55c28bd4fa0169373ce5d8b914802e..79a34ce6c888a611115719412d9a9bbc + final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.Iterator iterator = this.entities.iterator(); + try { + while (iterator.hasNext()) { -+ entity.accept(iterator.next()); ++ output.accept(iterator.next()); } + } finally { + iterator.finishedIterating(); @@ -33841,13 +34615,13 @@ index 3861282e2e55c28bd4fa0169373ce5d8b914802e..79a34ce6c888a611115719412d9a9bbc } } diff --git a/net/minecraft/world/level/gameevent/DynamicGameEventListener.java b/net/minecraft/world/level/gameevent/DynamicGameEventListener.java -index b25fdc2562d6710da698b5d2532304e8e47bd535..cf1f2ed003a94156cf688071aeea53c7ad22b700 100644 +index c31f66710036cb68500ed936b8a4b89a2cf57574..4d5e3cf76b64e57c22e2717ca395e23a871e4446 100644 --- a/net/minecraft/world/level/gameevent/DynamicGameEventListener.java +++ b/net/minecraft/world/level/gameevent/DynamicGameEventListener.java @@ -26,6 +26,14 @@ public class DynamicGameEventListener { - public void remove(ServerLevel level) { - ifChunkExists(level, this.lastSection, listenerRegistry -> listenerRegistry.unregister(this.listener)); + public void remove(final ServerLevel level) { + ifChunkExists(level, this.lastSection, dispatcher -> dispatcher.unregister(this.listener)); + // Paper start - rewrite chunk system + // We need to unset the last section when removed, otherwise if the same instance is re-added at the same position it + // will assume there was no change and fail to re-register. @@ -33858,21 +34632,21 @@ index b25fdc2562d6710da698b5d2532304e8e47bd535..cf1f2ed003a94156cf688071aeea53c7 + // Paper end - rewrite chunk system } - public void move(ServerLevel level) { + public void move(final ServerLevel level) { diff --git a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -index b619bfcdc53fb1fc325dc1093695c13ea3bab1ae..d1068a122e5dbd5af274d0f8178ca9f5fa49f930 100644 +index d74c887d2fdc39e09233eba47464868dfa846b64..39e9a1c39b5312aaa2ca6d2a1ffd8ed17b9933e6 100644 --- a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java +++ b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -@@ -85,7 +85,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { +@@ -87,7 +87,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { return CompletableFuture.supplyAsync(() -> { - this.doCreateBiomes(blender, randomState, structureManager, chunk); - return chunk; + this.doCreateBiomes(blender, randomState, structureManager, protoChunk); + return protoChunk; - }, Util.backgroundExecutor().forName("init_biomes")); + }, Runnable::run); // Paper - rewrite chunk system } - private void doCreateBiomes(Blender blender, RandomState random, StructureManager structureManager, ChunkAccess chunk) { -@@ -327,7 +327,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { + private void doCreateBiomes(final Blender blender, final RandomState randomState, final StructureManager structureManager, final ChunkAccess protoChunk) { +@@ -376,7 +376,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { } return var20; @@ -33880,9 +34654,9 @@ index b619bfcdc53fb1fc325dc1093695c13ea3bab1ae..d1068a122e5dbd5af274d0f8178ca9f5 + }, Runnable::run); // Paper - rewrite chunk system } - private ChunkAccess doFill(Blender blender, StructureManager structureManager, RandomState random, ChunkAccess chunk, int minCellY, int cellCountY) { + private ChunkAccess doFill( diff --git a/net/minecraft/world/level/levelgen/structure/StructureCheck.java b/net/minecraft/world/level/levelgen/structure/StructureCheck.java -index 97d9e4d3e47dd568f624b96ce4e27f3abcd23678..5198894e7a9b4fffa5ee9d9e4d8fad173c2294cd 100644 +index 469fb7695b46420b5817928b8f905c4df3aa9bfe..2ab055d5cdfb1e2e7cfb6f8a769c3d571acb2861 100644 --- a/net/minecraft/world/level/levelgen/structure/StructureCheck.java +++ b/net/minecraft/world/level/levelgen/structure/StructureCheck.java @@ -50,8 +50,13 @@ public class StructureCheck { @@ -33900,66 +34674,66 @@ index 97d9e4d3e47dd568f624b96ce4e27f3abcd23678..5198894e7a9b4fffa5ee9d9e4d8fad17 + // Paper end - rewrite chunk system public StructureCheck( - ChunkScanAccess storageAccess, + final ChunkScanAccess storageAccess, @@ -93,7 +98,7 @@ public class StructureCheck { - public StructureCheckResult checkStart(ChunkPos chunkPos, Structure structure, StructurePlacement placement, boolean skipKnownStructures) { - long packedChunkPos = chunkPos.toLong(); -- Object2IntMap map = this.loadedChunks.get(packedChunkPos); -+ Object2IntMap map = this.loadedChunksSafe.get(packedChunkPos); // Paper - rewrite chunk system - if (map != null) { - return this.checkStructureInfo(map, structure, skipKnownStructures); + public StructureCheckResult checkStart(final ChunkPos pos, final Structure structure, final StructurePlacement placement, final boolean requireUnreferenced) { + long posKey = pos.pack(); +- Object2IntMap cachedResult = this.loadedChunks.get(posKey); ++ Object2IntMap cachedResult = this.loadedChunksSafe.get(posKey); // Paper - rewrite chunk system + if (cachedResult != null) { + return this.checkStructureInfo(cachedResult, structure, requireUnreferenced); } else { @@ -103,9 +108,11 @@ public class StructureCheck { - } else if (!placement.applyAdditionalChunkRestrictions(chunkPos.x, chunkPos.z, this.seed, this.getSaltOverride(structure))) { // Paper - add missing structure seed configs + } else if (!placement.applyAdditionalChunkRestrictions(pos.x(), pos.z(), this.seed, this.getSaltOverride(structure))) { // Paper - add missing structure seed configs return StructureCheckResult.START_NOT_PRESENT; } else { -- boolean flag = this.featureChecks -- .computeIfAbsent(structure, structure1 -> new Long2BooleanOpenHashMap()) -- .computeIfAbsent(packedChunkPos, l -> this.canCreateStructure(chunkPos, structure)); +- boolean isFeatureChunk = this.featureChecks +- .computeIfAbsent(structure, k -> new Long2BooleanOpenHashMap()) +- .computeIfAbsent(posKey, k -> this.canCreateStructure(pos, structure)); + // Paper start - rewrite chunk system -+ boolean flag = this.featureChecksSafe ++ boolean isFeatureChunk = this.featureChecksSafe + .computeIfAbsent(structure, structure1 -> new ca.spottedleaf.moonrise.common.map.SynchronisedLong2BooleanMap(PER_FEATURE_CHECK_LIMIT)) -+ .getOrCompute(packedChunkPos, l -> this.canCreateStructure(chunkPos, structure)); ++ .getOrCompute(posKey, l -> this.canCreateStructure(pos, structure)); + // Paper end - rewrite chunk system - return !flag ? StructureCheckResult.START_NOT_PRESENT : StructureCheckResult.CHUNK_LOAD_NEEDED; + return !isFeatureChunk ? StructureCheckResult.START_NOT_PRESENT : StructureCheckResult.CHUNK_LOAD_NEEDED; } } -@@ -224,15 +231,25 @@ public class StructureCheck { +@@ -222,15 +229,25 @@ public class StructureCheck { } - private void storeFullResults(long chunkPos, Object2IntMap structureChunks) { -- this.loadedChunks.put(chunkPos, deduplicateEmptyMap(structureChunks)); -- this.featureChecks.values().forEach(map -> map.remove(chunkPos)); + private void storeFullResults(final long posKey, final Object2IntMap starts) { +- this.loadedChunks.put(posKey, deduplicateEmptyMap(starts)); +- this.featureChecks.values().forEach(m -> m.remove(posKey)); + // Paper start - rewrite chunk system -+ this.loadedChunksSafe.put(chunkPos, deduplicateEmptyMap(structureChunks)); ++ this.loadedChunksSafe.put(posKey, deduplicateEmptyMap(starts)); + // once we insert into loadedChunks, we don't really need to be very careful about removing everything + // from this map, as everything that checks this map uses loadedChunks first + // so, one way or another it's a race condition that doesn't matter + for (ca.spottedleaf.moonrise.common.map.SynchronisedLong2BooleanMap value : this.featureChecksSafe.values()) { -+ value.remove(chunkPos); ++ value.remove(posKey); + } + // Paper end - rewrite chunk system } - public void incrementReference(ChunkPos pos, Structure structure) { -- this.loadedChunks.compute(pos.toLong(), (_long, map) -> { -- if (map == null || map.isEmpty()) { -+ this.loadedChunksSafe.compute(pos.toLong(), (_long, map) -> { // Paper start - rewrite chunk system -+ if (map == null) { - map = new Object2IntOpenHashMap<>(); + public void incrementReference(final ChunkPos chunkPos, final Structure structure) { +- this.loadedChunks.compute(chunkPos.pack(), (key, counts) -> { +- if (counts == null || counts.isEmpty()) { ++ this.loadedChunksSafe.compute(chunkPos.pack(), (key, counts) -> { // Paper start - rewrite chunk system ++ if (counts == null) { + counts = new Object2IntOpenHashMap<>(); + } else { -+ map = map instanceof Object2IntOpenHashMap fastClone ? fastClone.clone() : new Object2IntOpenHashMap<>(map); ++ counts = counts instanceof Object2IntOpenHashMap fastClone ? fastClone.clone() : new Object2IntOpenHashMap<>(counts); } + // Paper end - rewrite chunk system - map.computeInt(structure, (structure1, integer) -> integer == null ? 1 : integer + 1); - return map; + counts.computeInt(structure, (k, value) -> value == null ? 1 : value + 1); + return counts; diff --git a/net/minecraft/world/level/lighting/LevelLightEngine.java b/net/minecraft/world/level/lighting/LevelLightEngine.java -index dbf0a3a30070ad5c8d4084cd4fcd9941b50e98f8..51c9462d091a42192989dee08738caa25e88b1a2 100644 +index de10c0bb009fdbd6f24d1db5c9266270027e8863..c6825283ad1787fcf4bfb93b32ca8396ac44f503 100644 --- a/net/minecraft/world/level/lighting/LevelLightEngine.java +++ b/net/minecraft/world/level/lighting/LevelLightEngine.java -@@ -9,149 +9,111 @@ import net.minecraft.world.level.chunk.DataLayer; +@@ -9,149 +9,113 @@ import net.minecraft.world.level.chunk.DataLayer; import net.minecraft.world.level.chunk.LightChunkGetter; import org.jspecify.annotations.Nullable; @@ -33995,15 +34769,15 @@ index dbf0a3a30070ad5c8d4084cd4fcd9941b50e98f8..51c9462d091a42192989dee08738caa2 + } + // Paper end - rewrite chunk system - public LevelLightEngine(LightChunkGetter lightChunkGetter, boolean blockLight, boolean skyLight) { - this.levelHeightAccessor = lightChunkGetter.getLevel(); -- this.blockEngine = blockLight ? new BlockLightEngine(lightChunkGetter) : null; -- this.skyEngine = skyLight ? new SkyLightEngine(lightChunkGetter) : null; + public LevelLightEngine(final LightChunkGetter chunkSource, final boolean hasBlockLight, final boolean hasSkyLight) { + this.levelHeightAccessor = chunkSource.getLevel(); +- this.blockEngine = hasBlockLight ? new BlockLightEngine(chunkSource) : null; +- this.skyEngine = hasSkyLight ? new SkyLightEngine(chunkSource) : null; + // Paper start - rewrite chunk system -+ if (lightChunkGetter.getLevel() instanceof net.minecraft.world.level.Level) { -+ this.lightEngine = new ca.spottedleaf.moonrise.patches.starlight.light.StarLightInterface(lightChunkGetter, skyLight, blockLight, (LevelLightEngine)(Object)this); ++ if (chunkSource.getLevel() instanceof net.minecraft.world.level.Level) { ++ this.lightEngine = new ca.spottedleaf.moonrise.patches.starlight.light.StarLightInterface(chunkSource, hasSkyLight, hasBlockLight, (LevelLightEngine)(Object)this); + } else { -+ this.lightEngine = new ca.spottedleaf.moonrise.patches.starlight.light.StarLightInterface(null, skyLight, blockLight, (LevelLightEngine)(Object)this); ++ this.lightEngine = new ca.spottedleaf.moonrise.patches.starlight.light.StarLightInterface(null, hasSkyLight, hasBlockLight, (LevelLightEngine)(Object)this); + } + // Paper end - rewrite chunk system } @@ -34018,7 +34792,7 @@ index dbf0a3a30070ad5c8d4084cd4fcd9941b50e98f8..51c9462d091a42192989dee08738caa2 } @Override - public void checkBlock(BlockPos pos) { + public void checkBlock(final BlockPos pos) { - if (this.blockEngine != null) { - this.blockEngine.checkBlock(pos); - } @@ -34037,104 +34811,106 @@ index dbf0a3a30070ad5c8d4084cd4fcd9941b50e98f8..51c9462d091a42192989dee08738caa2 @Override public int runLightUpdates() { -- int i = 0; +- int count = 0; - if (this.blockEngine != null) { -- i += this.blockEngine.runLightUpdates(); +- count += this.blockEngine.runLightUpdates(); - } - - if (this.skyEngine != null) { -- i += this.skyEngine.runLightUpdates(); +- count += this.skyEngine.runLightUpdates(); - } - -- return i; +- return count; ++ // Paper start - rewrite chunk system + final boolean hadUpdates = this.hasLightWork(); + this.lightEngine.propagateChanges(); -+ return hadUpdates ? 1 : 0; // Paper - rewrite chunk system ++ return hadUpdates ? 1 : 0; ++ // Paper end - rewrite chunk system } @Override - public void updateSectionStatus(SectionPos pos, boolean isEmpty) { + public void updateSectionStatus(final SectionPos pos, final boolean sectionEmpty) { - if (this.blockEngine != null) { -- this.blockEngine.updateSectionStatus(pos, isEmpty); +- this.blockEngine.updateSectionStatus(pos, sectionEmpty); - } - - if (this.skyEngine != null) { -- this.skyEngine.updateSectionStatus(pos, isEmpty); +- this.skyEngine.updateSectionStatus(pos, sectionEmpty); - } -+ this.lightEngine.sectionChange(pos, isEmpty); // Paper - rewrite chunk system ++ this.lightEngine.sectionChange(pos, sectionEmpty); // Paper - rewrite chunk system } @Override - public void setLightEnabled(ChunkPos chunkPos, boolean lightEnabled) { + public void setLightEnabled(final ChunkPos pos, final boolean enable) { - if (this.blockEngine != null) { -- this.blockEngine.setLightEnabled(chunkPos, lightEnabled); +- this.blockEngine.setLightEnabled(pos, enable); - } - - if (this.skyEngine != null) { -- this.skyEngine.setLightEnabled(chunkPos, lightEnabled); +- this.skyEngine.setLightEnabled(pos, enable); - } + // Paper - rewrite chunk system } @Override - public void propagateLightSources(ChunkPos chunkPos) { + public void propagateLightSources(final ChunkPos pos) { - if (this.blockEngine != null) { -- this.blockEngine.propagateLightSources(chunkPos); +- this.blockEngine.propagateLightSources(pos); - } - - if (this.skyEngine != null) { -- this.skyEngine.propagateLightSources(chunkPos); +- this.skyEngine.propagateLightSources(pos); - } + // Paper - rewrite chunk system } - public LayerLightEventListener getLayerListener(LightLayer lightLayer) { -- if (lightLayer == LightLayer.BLOCK) { + public LayerLightEventListener getLayerListener(final LightLayer layer) { +- if (layer == LightLayer.BLOCK) { - return (LayerLightEventListener)(this.blockEngine == null ? LayerLightEventListener.DummyLightLayerEventListener.INSTANCE : this.blockEngine); - } else { - return (LayerLightEventListener)(this.skyEngine == null ? LayerLightEventListener.DummyLightLayerEventListener.INSTANCE : this.skyEngine); - } -+ return lightLayer == LightLayer.BLOCK ? this.lightEngine.getBlockReader() : this.lightEngine.getSkyReader(); // Paper - rewrite chunk system ++ return layer == LightLayer.BLOCK ? this.lightEngine.getBlockReader() : this.lightEngine.getSkyReader(); // Paper - rewrite chunk system } - public String getDebugData(LightLayer lightLayer, SectionPos sectionPos) { -- if (lightLayer == LightLayer.BLOCK) { + public String getDebugData(final LightLayer layer, final SectionPos pos) { +- if (layer == LightLayer.BLOCK) { - if (this.blockEngine != null) { -- return this.blockEngine.getDebugData(sectionPos.asLong()); +- return this.blockEngine.getDebugData(pos.asLong()); - } - } else if (this.skyEngine != null) { -- return this.skyEngine.getDebugData(sectionPos.asLong()); +- return this.skyEngine.getDebugData(pos.asLong()); - } - - return "n/a"; + return "n/a"; // Paper - rewrite chunk system } - public LayerLightSectionStorage.SectionType getDebugSectionType(LightLayer lightLayer, SectionPos sectionPos) { -- if (lightLayer == LightLayer.BLOCK) { + public LayerLightSectionStorage.SectionType getDebugSectionType(final LightLayer layer, final SectionPos pos) { +- if (layer == LightLayer.BLOCK) { - if (this.blockEngine != null) { -- return this.blockEngine.getDebugSectionType(sectionPos.asLong()); +- return this.blockEngine.getDebugSectionType(pos.asLong()); - } - } else if (this.skyEngine != null) { -- return this.skyEngine.getDebugSectionType(sectionPos.asLong()); +- return this.skyEngine.getDebugSectionType(pos.asLong()); - } - - return LayerLightSectionStorage.SectionType.EMPTY; + throw new UnsupportedOperationException(); // Paper - rewrite chunk system } - public void queueSectionData(LightLayer lightLayer, SectionPos sectionPos, @Nullable DataLayer dataLayer) { -- if (lightLayer == LightLayer.BLOCK) { + public void queueSectionData(final LightLayer layer, final SectionPos pos, final @Nullable DataLayer data) { +- if (layer == LightLayer.BLOCK) { - if (this.blockEngine != null) { -- this.blockEngine.queueSectionData(sectionPos.asLong(), dataLayer); +- this.blockEngine.queueSectionData(pos.asLong(), data); - } - } else if (this.skyEngine != null) { -- this.skyEngine.queueSectionData(sectionPos.asLong(), dataLayer); +- this.skyEngine.queueSectionData(pos.asLong(), data); - } + // Paper - rewrite chunk system } - public void retainData(ChunkPos pos, boolean retain) { + public void retainData(final ChunkPos pos, final boolean retain) { - if (this.blockEngine != null) { - this.blockEngine.retainData(pos, retain); - } @@ -34145,25 +34921,25 @@ index dbf0a3a30070ad5c8d4084cd4fcd9941b50e98f8..51c9462d091a42192989dee08738caa2 + // Paper - rewrite chunk system } - public int getRawBrightness(BlockPos pos, int amount) { -- int i = this.skyEngine == null ? 0 : this.skyEngine.getLightValue(pos) - amount; -- int i1 = this.blockEngine == null ? 0 : this.blockEngine.getLightValue(pos); -- return Math.max(i1, i); -+ return this.lightEngine.getRawBrightness(pos, amount); // Paper - rewrite chunk system + public int getRawBrightness(final BlockPos pos, final int skyDampen) { +- int skyLight = this.skyEngine == null ? 0 : this.skyEngine.getLightValue(pos) - skyDampen; +- int blockLight = this.blockEngine == null ? 0 : this.blockEngine.getLightValue(pos); +- return Math.max(blockLight, skyLight); ++ return this.lightEngine.getRawBrightness(pos, skyDampen); // Paper - rewrite chunk system } - public boolean lightOnInColumn(long columnPos) { + public boolean lightOnInColumn(final long sectionZeroNode) { - return this.blockEngine == null -- || this.blockEngine.storage.lightOnInColumn(columnPos) && (this.skyEngine == null || this.skyEngine.storage.lightOnInColumn(columnPos)); +- || this.blockEngine.storage.lightOnInColumn(sectionZeroNode) && (this.skyEngine == null || this.skyEngine.storage.lightOnInColumn(sectionZeroNode)); + throw new UnsupportedOperationException(); // Paper - rewrite chunk system // Paper - not implemented on server } public int getLightSectionCount() { diff --git a/net/minecraft/world/level/material/FlowingFluid.java b/net/minecraft/world/level/material/FlowingFluid.java -index 41bea92e2ce834ddb2e5d62d0a0008ef662b77b4..85e9ec8061fc01d39977f6db314da14b1e7725e1 100644 +index 27aa4e640ee29d96a854846bb906c30ebf4854f6..d7270de83fa13618d8f94de6eff63ffa37b823cc 100644 --- a/net/minecraft/world/level/material/FlowingFluid.java +++ b/net/minecraft/world/level/material/FlowingFluid.java -@@ -46,6 +46,48 @@ public abstract class FlowingFluid extends Fluid { +@@ -47,6 +47,48 @@ public abstract class FlowingFluid extends Fluid { }); private final Map shapes = Maps.newIdentityHashMap(); @@ -34210,31 +34986,36 @@ index 41bea92e2ce834ddb2e5d62d0a0008ef662b77b4..85e9ec8061fc01d39977f6db314da14b + // Paper end - fluid method optimisations + @Override - protected void createFluidStateDefinition(StateDefinition.Builder builder) { + protected void createFluidStateDefinition(final StateDefinition.Builder builder) { builder.add(FALLING); -@@ -210,65 +252,71 @@ public abstract class FlowingFluid extends Fluid { +@@ -212,70 +254,70 @@ public abstract class FlowingFluid extends Fluid { } } - private static boolean canPassThroughWall( -- Direction direction, BlockGetter level, BlockPos pos, BlockState state, BlockPos spreadPos, BlockState spreadState +- final Direction direction, +- final BlockGetter level, +- final BlockPos sourcePos, +- final BlockState sourceState, +- final BlockPos targetPos, +- final BlockState targetState - ) { -- if (!SharedConstants.DEBUG_DISABLE_LIQUID_SPREADING && (!SharedConstants.DEBUG_ONLY_GENERATE_HALF_THE_WORLD || spreadPos.getZ() >= 0)) { -- VoxelShape collisionShape = spreadState.getCollisionShape(level, spreadPos); -- if (collisionShape == Shapes.block()) { +- if (!SharedConstants.DEBUG_DISABLE_LIQUID_SPREADING && (!SharedConstants.DEBUG_ONLY_GENERATE_HALF_THE_WORLD || targetPos.getZ() >= 0)) { +- VoxelShape targetShape = targetState.getCollisionShape(level, targetPos); +- if (targetShape == Shapes.block()) { - return false; - } else { -- VoxelShape collisionShape1 = state.getCollisionShape(level, pos); -- if (collisionShape1 == Shapes.block()) { +- VoxelShape sourceShape = sourceState.getCollisionShape(level, sourcePos); +- if (sourceShape == Shapes.block()) { - return false; -- } else if (collisionShape1 == Shapes.empty() && collisionShape == Shapes.empty()) { +- } else if (sourceShape == Shapes.empty() && targetShape == Shapes.empty()) { - return true; - } else { -- Object2ByteLinkedOpenHashMap map; -- if (!state.getBlock().hasDynamicShape() && !spreadState.getBlock().hasDynamicShape()) { -- map = OCCLUSION_CACHE.get(); +- Object2ByteLinkedOpenHashMap cache; +- if (!sourceState.getBlock().hasDynamicShape() && !targetState.getBlock().hasDynamicShape()) { +- cache = OCCLUSION_CACHE.get(); - } else { -- map = null; +- cache = null; - } + // Paper start - fluid method optimisations + private static boolean canPassThroughWall(final Direction direction, final BlockGetter level, @@ -34245,36 +35026,36 @@ index 41bea92e2ce834ddb2e5d62d0a0008ef662b77b4..85e9ec8061fc01d39977f6db314da14b + return true; + } -- FlowingFluid.BlockStatePairKey blockStatePairKey; -- if (map != null) { -- blockStatePairKey = new FlowingFluid.BlockStatePairKey(state, spreadState, direction); -- byte andMoveToFirst = map.getAndMoveToFirst(blockStatePairKey); -- if (andMoveToFirst != 127) { -- return andMoveToFirst != 0; +- FlowingFluid.BlockStatePairKey key; +- if (cache != null) { +- key = new FlowingFluid.BlockStatePairKey(sourceState, targetState, direction); +- byte cached = cache.getAndMoveToFirst(key); +- if (cached != 127) { +- return cached != 0; - } - } else { -- blockStatePairKey = null; +- key = null; - } + if (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)fromState).moonrise$occludesFullBlock() | ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)toState).moonrise$occludesFullBlock()) { + // don't even try to cache simple cases + return false; + } -- boolean flag = !Shapes.mergedFaceOccludes(collisionShape1, collisionShape, direction); -- if (map != null) { -- if (map.size() == 200) { -- map.removeLastByte(); +- boolean result = !Shapes.mergedFaceOccludes(sourceShape, targetShape, direction); +- if (cache != null) { +- if (cache.size() == 200) { +- cache.removeLastByte(); - } + final ca.spottedleaf.moonrise.patches.collisions.util.FluidOcclusionCacheKey[] cache = ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)fromState).moonrise$hasCache() & ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)toState).moonrise$hasCache() ? + COLLISION_OCCLUSION_CACHE.get() : null; -- map.putAndMoveToFirst(blockStatePairKey, (byte)(flag ? 1 : 0)); +- cache.putAndMoveToFirst(key, (byte)(result ? 1 : 0)); - } + final int keyIndex + = (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)fromState).moonrise$uniqueId1() ^ ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)toState).moonrise$uniqueId2() ^ ((ca.spottedleaf.moonrise.patches.collisions.util.CollisionDirection)(Object)direction).moonrise$uniqueId()) + & (COLLISION_OCCLUSION_CACHE_SIZE - 1); -- return flag; +- return result; - } + if (cache != null) { + final ca.spottedleaf.moonrise.patches.collisions.util.FluidOcclusionCacheKey cached = cache[keyIndex]; @@ -34302,10 +35083,9 @@ index 41bea92e2ce834ddb2e5d62d0a0008ef662b77b4..85e9ec8061fc01d39977f6db314da14b public abstract Fluid getFlowing(); - public FluidState getFlowing(int level, boolean falling) { -- return this.getFlowing().defaultFluidState().setValue(LEVEL, level).setValue(FALLING, falling); + public FluidState getFlowing(final int amount, final boolean falling) { +- return this.getFlowing().defaultFluidState().setValue(LEVEL, amount).setValue(FALLING, falling); + // Paper start - fluid method optimisations -+ final int amount = level; + if (!this.init) { + this.init(); + } @@ -34316,7 +35096,7 @@ index 41bea92e2ce834ddb2e5d62d0a0008ef662b77b4..85e9ec8061fc01d39977f6db314da14b public abstract Fluid getSource(); - public FluidState getSource(boolean falling) { + public FluidState getSource(final boolean falling) { - return this.getSource().defaultFluidState().setValue(FALLING, falling); + // Paper start - fluid method optimisations + if (!this.init) { @@ -34328,16 +35108,16 @@ index 41bea92e2ce834ddb2e5d62d0a0008ef662b77b4..85e9ec8061fc01d39977f6db314da14b protected abstract boolean canConvertToSource(ServerLevel level); diff --git a/net/minecraft/world/level/material/FluidState.java b/net/minecraft/world/level/material/FluidState.java -index c460de79f14cab6c7de4ba28e359762382d7c972..3e5ada4a84bc1da06102dfacb0d3d45a4fc6db8a 100644 +index 2e10d9d3850c18cf1bf625aac1dcacab1792e162..c835ae31a965b92780b7130a24c63ac217980aaa 100644 --- a/net/minecraft/world/level/material/FluidState.java +++ b/net/minecraft/world/level/material/FluidState.java -@@ -25,12 +25,30 @@ import net.minecraft.world.phys.Vec3; +@@ -21,12 +21,30 @@ import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; import org.jspecify.annotations.Nullable; --public final class FluidState extends StateHolder { -+public final class FluidState extends StateHolder implements ca.spottedleaf.moonrise.patches.fluid.FluidFluidState { // Paper - fluid method optimisations - public static final Codec CODEC = codec(BuiltInRegistries.FLUID.byNameCodec(), Fluid::defaultFluidState).stable(); +-public final class FluidState extends StateHolder implements TypedInstance { ++public final class FluidState extends StateHolder implements TypedInstance, ca.spottedleaf.moonrise.patches.fluid.FluidFluidState { // Paper - fluid method optimisations + public static final Codec CODEC = codec(BuiltInRegistries.FLUID.byNameCodec(), Fluid::defaultFluidState, Fluid::getStateDefinition).stable(); public static final int AMOUNT_MAX = 9; public static final int AMOUNT_FULL = 8; protected final boolean isEmpty; // Paper - Perf: moved from isEmpty() @@ -34360,10 +35140,10 @@ index c460de79f14cab6c7de4ba28e359762382d7c972..3e5ada4a84bc1da06102dfacb0d3d45a + } + // Paper end - fluid method optimisations + - public FluidState(Fluid owner, Reference2ObjectArrayMap, Comparable> values, MapCodec propertiesCodec) { - super(owner, values, propertiesCodec); + public FluidState(final Fluid owner, final Property[] propertyKeys, final Comparable[] propertyValues) { + super(owner, propertyKeys, propertyValues); this.isEmpty = owner.isEmpty(); // Paper - Perf: moved from isEmpty() -@@ -41,11 +59,11 @@ public final class FluidState extends StateHolder { +@@ -37,11 +55,11 @@ public final class FluidState extends StateHolder implements } public boolean isSource() { @@ -34371,13 +35151,13 @@ index c460de79f14cab6c7de4ba28e359762382d7c972..3e5ada4a84bc1da06102dfacb0d3d45a + return this.isSource; // Paper - fluid method optimisations } - public boolean isSourceOfType(Fluid fluid) { -- return this.owner == fluid && this.owner.isSource(this); -+ return this.isSource && this.owner == fluid; // Paper - fluid method optimisations + public boolean isSourceOfType(final Fluid fluidType) { +- return this.owner == fluidType && this.owner.isSource(this); ++ return this.isSource && this.owner == fluidType; // Paper - fluid method optimisations } public boolean isEmpty() { -@@ -57,11 +75,11 @@ public final class FluidState extends StateHolder { +@@ -53,7 +71,7 @@ public final class FluidState extends StateHolder implements } public float getOwnHeight() { @@ -34385,13 +35165,17 @@ index c460de79f14cab6c7de4ba28e359762382d7c972..3e5ada4a84bc1da06102dfacb0d3d45a + return this.ownHeight; // Paper - fluid method optimisations } + public boolean isFull() { +@@ -61,7 +79,7 @@ public final class FluidState extends StateHolder implements + } + public int getAmount() { - return this.getType().getAmount(this); + return this.amount; // Paper - fluid method optimisations } - public boolean shouldRenderBackwardUpFace(BlockGetter level, BlockPos pos) { -@@ -87,7 +105,7 @@ public final class FluidState extends StateHolder { + public boolean shouldRenderBackwardUpFace(final BlockGetter level, final BlockPos above) { +@@ -87,7 +105,7 @@ public final class FluidState extends StateHolder implements } public boolean isRandomlyTicking() { @@ -34399,8 +35183,8 @@ index c460de79f14cab6c7de4ba28e359762382d7c972..3e5ada4a84bc1da06102dfacb0d3d45a + return this.isRandomlyTicking; // Paper - fluid method optimisations } - public void randomTick(ServerLevel level, BlockPos pos, RandomSource random) { -@@ -99,7 +117,12 @@ public final class FluidState extends StateHolder { + public void randomTick(final ServerLevel level, final BlockPos pos, final RandomSource random) { +@@ -99,7 +117,12 @@ public final class FluidState extends StateHolder implements } public BlockState createLegacyBlock() { @@ -34414,32 +35198,90 @@ index c460de79f14cab6c7de4ba28e359762382d7c972..3e5ada4a84bc1da06102dfacb0d3d45a } public @Nullable ParticleOptions getDripParticle() { +diff --git a/net/minecraft/world/level/portal/PortalForcer.java b/net/minecraft/world/level/portal/PortalForcer.java +index 08ac4ce3f34f2dc955ef2bfb2401f469f7a500a3..4e4903d38d705dca6ff16418e54a7a367074aa08 100644 +--- a/net/minecraft/world/level/portal/PortalForcer.java ++++ b/net/minecraft/world/level/portal/PortalForcer.java +@@ -49,13 +49,46 @@ public class PortalForcer { + PoiManager poiManager = this.level.getPoiManager(); + // int radius = toNether ? 16 : 128; + // CraftBukkit end +- poiManager.ensureLoadedAndValid(this.level, approximateExitPos, radius); +- return poiManager.getInSquare(type -> type.is(PoiTypes.NETHER_PORTAL), approximateExitPos, radius, PoiManager.Occupancy.ANY) +- .map(PoiRecord::getPos) +- .filter(worldBorder::isWithinBounds) +- .filter(pos -> !(this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> pos.getY() >= v))) // Paper - Configurable nether ceiling damage +- .filter(pos -> this.level.getBlockState(pos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS)) +- .min(Comparator.comparingDouble(p -> p.distSqr(approximateExitPos)).thenComparingInt(Vec3i::getY)); ++ // Paper start - optimise poi lookup ++ //poiManager.ensureLoadedAndValid(this.level, approximateExitPos, radius); // Paper - optimise poi lookup - we can let it load as it reads ++ java.util.List records = new java.util.ArrayList<>(); ++ ca.spottedleaf.moonrise.patches.poi_lookup.PoiAccess.findClosestPoiDataRecords( ++ poiManager, type -> type.is(PoiTypes.NETHER_PORTAL), ++ (final net.minecraft.core.Holder type, final BlockPos pos) -> { ++ if (!worldBorder.isWithinBounds(pos)) { ++ return false; ++ } ++ // Paper start - Configurable nether ceiling damage ++ if (this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> pos.getY() >= v)) { ++ return false; ++ } ++ // Paper end - Configurable nether ceiling damage ++ ++ net.minecraft.world.level.chunk.ChunkAccess lowest = this.level.getChunk(pos.getX() >> 4, pos.getZ() >> 4, net.minecraft.world.level.chunk.status.ChunkStatus.EMPTY); ++ net.minecraft.world.level.levelgen.BelowZeroRetrogen belowZeroRetrogen; ++ if (!lowest.getPersistedStatus().isOrAfter(net.minecraft.world.level.chunk.status.ChunkStatus.FULL) ++ // check below zero retrogen so that pre 1.17 worlds still load portals (JMP) ++ && ((belowZeroRetrogen = lowest.getBelowZeroRetrogen()) == null || !belowZeroRetrogen.targetStatus().isOrAfter(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN))) { ++ // why would we generate the chunk? ++ return false; ++ } ++ return lowest.getBlockState(pos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS); ++ }, ++ approximateExitPos, radius, Double.MAX_VALUE, PoiManager.Occupancy.ANY, true, records ++ ); ++ ++ // this gets us most of the way there, but Vanilla biases lower y values. ++ PoiRecord lowestYRecord = null; ++ for (PoiRecord record : records) { ++ if (lowestYRecord == null) { ++ lowestYRecord = record; ++ } else if (lowestYRecord.getPos().getY() > record.getPos().getY()) { ++ lowestYRecord = record; ++ } ++ } ++ // now we're done ++ return Optional.ofNullable(lowestYRecord == null ? null : lowestYRecord.getPos()); ++ // Paper end - optimise poi lookup + } + + public Optional createPortal(final BlockPos origin, final Direction.Axis portalAxis) { diff --git a/net/minecraft/world/phys/AABB.java b/net/minecraft/world/phys/AABB.java -index 26f807a2fadabfdd7a7144e1c1146e7a2577190a..7f0ff1bf4ddf7bfe1d3cad472416dd032806066b 100644 +index 1a1b775b4675603a9f5d492d6c9977af2ebbb72c..259a19cbcf9a42c5f48e5d03448729283baf36a7 100644 --- a/net/minecraft/world/phys/AABB.java +++ b/net/minecraft/world/phys/AABB.java -@@ -317,7 +317,7 @@ public class AABB { +@@ -319,7 +319,7 @@ public class AABB { } } - private static @Nullable Direction getDirection( + public static @Nullable Direction getDirection( // Paper - optimise collisions - public - AABB aabb, Vec3 start, double[] minDistance, @Nullable Direction facing, double deltaX, double deltaY, double deltaZ + final AABB aabb, final Vec3 from, final double[] scaleReference, final @Nullable Direction direction, final double dx, final double dy, final double dz ) { - return getDirection(aabb.minX, aabb.minY, aabb.minZ, aabb.maxX, aabb.maxY, aabb.maxZ, start, minDistance, facing, deltaX, deltaY, deltaZ); + return getDirection(aabb.minX, aabb.minY, aabb.minZ, aabb.maxX, aabb.maxY, aabb.maxZ, from, scaleReference, direction, dx, dy, dz); diff --git a/net/minecraft/world/phys/shapes/ArrayVoxelShape.java b/net/minecraft/world/phys/shapes/ArrayVoxelShape.java -index 91a8124a302594eedad0833a50eeb95d291b3674..3506d7db68d4428497126d5ebd29106d5816bcfe 100644 +index 6ffcd1a4a3d73de8abd2eadf567d97c0bfa5baa9..16e8bea1aa20e02140cdb319daa84e14eadead31 100644 --- a/net/minecraft/world/phys/shapes/ArrayVoxelShape.java +++ b/net/minecraft/world/phys/shapes/ArrayVoxelShape.java @@ -20,7 +20,7 @@ public class ArrayVoxelShape extends VoxelShape { ); } -- ArrayVoxelShape(DiscreteVoxelShape shape, DoubleList xs, DoubleList ys, DoubleList zs) { -+ public ArrayVoxelShape(DiscreteVoxelShape shape, DoubleList xs, DoubleList ys, DoubleList zs) { // Paper - optimise collisions - public +- ArrayVoxelShape(final DiscreteVoxelShape shape, final DoubleList xs, final DoubleList ys, final DoubleList zs) { ++ public ArrayVoxelShape(final DiscreteVoxelShape shape, final DoubleList xs, final DoubleList ys, final DoubleList zs) { // Paper - optimise collisions - public super(shape); - int i = shape.getXSize() + 1; - int i1 = shape.getYSize() + 1; + int xSize = shape.getXSize() + 1; + int ySize = shape.getYSize() + 1; @@ -34,6 +34,7 @@ public class ArrayVoxelShape extends VoxelShape { new IllegalArgumentException("Lengths of point arrays must be consistent with the size of the VoxelShape.") ); @@ -34449,7 +35291,7 @@ index 91a8124a302594eedad0833a50eeb95d291b3674..3506d7db68d4428497126d5ebd29106d @Override diff --git a/net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape.java b/net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape.java -index 14a12bdaa428556fa7b0c43e37b79699ae2fcb92..3a56e4ad9b3cba0cdf4bc373f7d0457d8643fdc4 100644 +index 73364066033aa722db0e8ab6c7e6cc0d93f7ecfd..f229a9247f9887dd8b01116f135e1db1a26ecb95 100644 --- a/net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape.java +++ b/net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape.java @@ -4,13 +4,13 @@ import java.util.BitSet; @@ -34471,14 +35313,14 @@ index 14a12bdaa428556fa7b0c43e37b79699ae2fcb92..3a56e4ad9b3cba0cdf4bc373f7d0457d + public int yMax; // Paper - optimise collisions - public + public int zMax; // Paper - optimise collisions - public - public BitSetDiscreteVoxelShape(int xSize, int ySize, int zSize) { + public BitSetDiscreteVoxelShape(final int xSize, final int ySize, final int zSize) { super(xSize, ySize, zSize); -@@ -150,47 +150,109 @@ public final class BitSetDiscreteVoxelShape extends DiscreteVoxelShape { - return bitSetDiscreteVoxelShape; +@@ -157,47 +157,109 @@ public final class BitSetDiscreteVoxelShape extends DiscreteVoxelShape { + return shape; } -- protected static void forAllBoxes(DiscreteVoxelShape shape, DiscreteVoxelShape.IntLineConsumer consumer, boolean combine) { -- BitSetDiscreteVoxelShape bitSetDiscreteVoxelShape = new BitSetDiscreteVoxelShape(shape); +- protected static void forAllBoxes(final DiscreteVoxelShape voxelShape, final DiscreteVoxelShape.IntLineConsumer consumer, final boolean mergeNeighbors) { +- BitSetDiscreteVoxelShape shape = new BitSetDiscreteVoxelShape(voxelShape); + // Paper start - optimise collisions + public static void forAllBoxes(final DiscreteVoxelShape shape, final DiscreteVoxelShape.IntLineConsumer consumer, final boolean mergeAdjacent) { + // Paper - remove debug @@ -34519,20 +35361,20 @@ index 14a12bdaa428556fa7b0c43e37b79699ae2fcb92..3a56e4ad9b3cba0cdf4bc373f7d0457d + // same notes about loop order as the above + // this branch is actually important to optimise, as it affects uncached toAabbs() (which affects optimize()) -- for (int i = 0; i < bitSetDiscreteVoxelShape.ySize; i++) { -- for (int i1 = 0; i1 < bitSetDiscreteVoxelShape.xSize; i1++) { -- int i2 = -1; +- for (int y = 0; y < shape.ySize; y++) { +- for (int x = 0; x < shape.xSize; x++) { +- int lastStartZ = -1; + // only clone when we may write to it + bitset = ca.spottedleaf.moonrise.common.util.MixinWorkarounds.clone(bitset); -- for (int i3 = 0; i3 <= bitSetDiscreteVoxelShape.zSize; i3++) { -- if (bitSetDiscreteVoxelShape.isFullWide(i1, i, i3)) { -- if (combine) { -- if (i2 == -1) { -- i2 = i3; +- for (int z = 0; z <= shape.zSize; z++) { +- if (shape.isFullWide(x, y, z)) { +- if (mergeNeighbors) { +- if (lastStartZ == -1) { +- lastStartZ = z; - } - } else { -- consumer.consume(i1, i, i3, i1 + 1, i + 1, i3 + 1); +- consumer.consume(x, y, z, x + 1, y + 1, z + 1); + for (int y = 0; y < sizeY; ++y, indexY += incY) { + indexX = indexY; + for (int x = 0; x < sizeX; ++x, indexX += incX) { @@ -34541,20 +35383,20 @@ index 14a12bdaa428556fa7b0c43e37b79699ae2fcb92..3a56e4ad9b3cba0cdf4bc373f7d0457d + + if (firstSetZ == -1) { + break; -+ } + } +- } else if (lastStartZ != -1) { +- int endX = x; +- int endY = y; +- shape.clearZStrip(lastStartZ, z, x, y); +- +- while (shape.isZStripFull(lastStartZ, z, endX + 1, y)) { +- shape.clearZStrip(lastStartZ, z, endX + 1, y); +- endX++; + + int lastSetZ = ca.spottedleaf.moonrise.common.util.FlatBitsetUtil.firstClear(bitset, firstSetZ, endIndex); + if (lastSetZ == -1) { + lastSetZ = endIndex; - } -- } else if (i2 != -1) { -- int i4 = i1; -- int i5 = i; -- bitSetDiscreteVoxelShape.clearZStrip(i2, i3, i1, i); -- -- while (bitSetDiscreteVoxelShape.isZStripFull(i2, i3, i4 + 1, i)) { -- bitSetDiscreteVoxelShape.clearZStrip(i2, i3, i4 + 1, i); -- i4++; ++ } + + ca.spottedleaf.moonrise.common.util.FlatBitsetUtil.clearRange(bitset, firstSetZ, lastSetZ); + @@ -34568,9 +35410,9 @@ index 14a12bdaa428556fa7b0c43e37b79699ae2fcb92..3a56e4ad9b3cba0cdf4bc373f7d0457d + ca.spottedleaf.moonrise.common.util.FlatBitsetUtil.clearRange(bitset, neighbourIdxStart, neighbourIdxEnd); } -- while (bitSetDiscreteVoxelShape.isXZRectangleFull(i1, i4 + 1, i2, i3, i5 + 1)) { -- for (int i6 = i1; i6 <= i4; i6++) { -- bitSetDiscreteVoxelShape.clearZStrip(i2, i3, i6, i5 + 1); +- while (shape.isXZRectangleFull(x, endX + 1, lastStartZ, z, endY + 1)) { +- for (int cx = x; cx <= endX; cx++) { +- shape.clearZStrip(lastStartZ, z, cx, endY + 1); + // try to merge neighbouring on the Y axis + + int endY; // exclusive @@ -34587,7 +35429,7 @@ index 14a12bdaa428556fa7b0c43e37b79699ae2fcb92..3a56e4ad9b3cba0cdf4bc373f7d0457d + } } -- i5++; +- endY++; + ++endY; + + // passed, so we can clear it @@ -34597,8 +35439,8 @@ index 14a12bdaa428556fa7b0c43e37b79699ae2fcb92..3a56e4ad9b3cba0cdf4bc373f7d0457d + } } -- consumer.consume(i1, i, i2, i4 + 1, i5 + 1, i3); -- i2 = -1; +- consumer.consume(x, y, lastStartZ, endX + 1, endY + 1, z); +- lastStartZ = -1; + consumer.consume(x, y, firstSetZ - indexX, endX, endY, lastSetZ - indexX); + zIdx = lastSetZ; } @@ -34608,22 +35450,22 @@ index 14a12bdaa428556fa7b0c43e37b79699ae2fcb92..3a56e4ad9b3cba0cdf4bc373f7d0457d } + // Paper end - optimise collisions - private boolean isZStripFull(int zMin, int zMax, int x, int y) { - return x < this.xSize && y < this.ySize && this.storage.nextClearBit(this.getIndex(x, y, zMin)) >= this.getIndex(x, y, zMax); + private boolean isZStripFull(final int startZ, final int endZ, final int x, final int y) { + return x < this.xSize && y < this.ySize && this.storage.nextClearBit(this.getIndex(x, y, startZ)) >= this.getIndex(x, y, endZ); diff --git a/net/minecraft/world/phys/shapes/CubeVoxelShape.java b/net/minecraft/world/phys/shapes/CubeVoxelShape.java -index f6b6481591e009de80f6b6318d35f193aabb7df3..e9b5069dcd572966b2f5aa220cef30e7a328fa2c 100644 +index fa27f840cc9f17563701934cc504e40865e1670b..f6f2417ae6549802559e2307c293a1310ff1515f 100644 --- a/net/minecraft/world/phys/shapes/CubeVoxelShape.java +++ b/net/minecraft/world/phys/shapes/CubeVoxelShape.java @@ -7,6 +7,7 @@ import net.minecraft.util.Mth; public final class CubeVoxelShape extends VoxelShape { - protected CubeVoxelShape(DiscreteVoxelShape shape) { + protected CubeVoxelShape(final DiscreteVoxelShape shape) { super(shape); + ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)this).moonrise$initCache(); // Paper - optimise collisions } @Override diff --git a/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java b/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java -index aad858a5a1bf50b1e3121cfd5bfb9660dca7b8e7..c60fbd10cae82f75566ffc2c9cc1abd94a6086f4 100644 +index 4631e6e8397676b30a7a9b04786e8abfdcc4549d..cdeff630372a998982f485424f0d3348f30c5291 100644 --- a/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java +++ b/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java @@ -5,12 +5,79 @@ import net.minecraft.core.AxisCycle; @@ -34704,11 +35546,11 @@ index aad858a5a1bf50b1e3121cfd5bfb9660dca7b8e7..c60fbd10cae82f75566ffc2c9cc1abd9 + } + // Paper end - optimise collisions + - protected DiscreteVoxelShape(int xSize, int ySize, int zSize) { + protected DiscreteVoxelShape(final int xSize, final int ySize, final int zSize) { if (xSize >= 0 && ySize >= 0 && zSize >= 0) { this.xSize = xSize; diff --git a/net/minecraft/world/phys/shapes/OffsetDoubleList.java b/net/minecraft/world/phys/shapes/OffsetDoubleList.java -index ac1488875537421b74f0c491c9b7a40e75539c92..9eb27eb8d6dcaad6ce02f8ce4546acc224c4196f 100644 +index 66085f0e57fd2e2fae3967aab7f6835d23fced07..7c1e93123371d0d1b371243c132b54d478f7d00f 100644 --- a/net/minecraft/world/phys/shapes/OffsetDoubleList.java +++ b/net/minecraft/world/phys/shapes/OffsetDoubleList.java @@ -4,8 +4,8 @@ import it.unimi.dsi.fastutil.doubles.AbstractDoubleList; @@ -34720,22 +35562,21 @@ index ac1488875537421b74f0c491c9b7a40e75539c92..9eb27eb8d6dcaad6ce02f8ce4546acc2 + public final DoubleList delegate; // Paper - optimise collisions - public + public final double offset; // Paper - optimise collisions - public - public OffsetDoubleList(DoubleList delegate, double offset) { + public OffsetDoubleList(final DoubleList delegate, final double offset) { this.delegate = delegate; diff --git a/net/minecraft/world/phys/shapes/Shapes.java b/net/minecraft/world/phys/shapes/Shapes.java -index b17ffae691eaaedf6b1371adae02adf92147056e..ac4bb789df88c07ea38d20700dccb6571e3ea58d 100644 +index b9ed92ee991727efd0e8f9ed6cf302eb364788f2..de10ce3faa86d6ed6d528575a083722a8feedf8d 100644 --- a/net/minecraft/world/phys/shapes/Shapes.java +++ b/net/minecraft/world/phys/shapes/Shapes.java @@ -21,9 +21,15 @@ public final class Shapes { public static final double EPSILON = 1.0E-7; public static final double BIG_EPSILON = 1.0E-6; private static final VoxelShape BLOCK = Util.make(() -> { -- DiscreteVoxelShape discreteVoxelShape = new BitSetDiscreteVoxelShape(1, 1, 1); -- discreteVoxelShape.fill(0, 0, 0); -- return new CubeVoxelShape(discreteVoxelShape); +- DiscreteVoxelShape shape = new BitSetDiscreteVoxelShape(1, 1, 1); + // Paper start - optimise collisions + final DiscreteVoxelShape shape = new BitSetDiscreteVoxelShape(1, 1, 1); -+ shape.fill(0, 0, 0); + shape.fill(0, 0, 0); +- return new CubeVoxelShape(shape); + + return new ArrayVoxelShape( + shape, @@ -34745,7 +35586,7 @@ index b17ffae691eaaedf6b1371adae02adf92147056e..ac4bb789df88c07ea38d20700dccb657 }); private static final Vec3 BLOCK_CENTER = new Vec3(0.5, 0.5, 0.5); public static final VoxelShape INFINITY = box( -@@ -49,6 +55,30 @@ public final class Shapes { +@@ -49,6 +55,48 @@ public final class Shapes { return BLOCK; } @@ -34771,21 +35612,39 @@ index b17ffae691eaaedf6b1371adae02adf92147056e..ac4bb789df88c07ea38d20700dccb657 + + return ret; + } ++ ++ private static boolean mergedMayOccludeBlock(final VoxelShape shape1, final VoxelShape shape2) { ++ // if the combined bounds of the two shapes cannot occlude, then neither can the merged ++ final AABB bounds1 = shape1.bounds(); ++ final AABB bounds2 = shape2.bounds(); ++ ++ final double minX = Math.min(bounds1.minX, bounds2.minX); ++ final double minY = Math.min(bounds1.minY, bounds2.minY); ++ final double minZ = Math.min(bounds1.minZ, bounds2.minZ); ++ ++ final double maxX = Math.max(bounds1.maxX, bounds2.maxX); ++ final double maxY = Math.max(bounds1.maxY, bounds2.maxY); ++ final double maxZ = Math.max(bounds1.maxZ, bounds2.maxZ); ++ ++ return (minX <= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON && maxX >= (1 - ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) && ++ (minY <= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON && maxY >= (1 - ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) && ++ (minZ <= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON && maxZ >= (1 - ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)); ++ } + // Paper end - optimise collisions + - public static VoxelShape box(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + public static VoxelShape box(final double minX, final double minY, final double minZ, final double maxX, final double maxY, final double maxZ) { if (!(minX > maxX) && !(minY > maxY) && !(minZ > maxZ)) { return create(minX, minY, minZ, maxX, maxY, maxZ); -@@ -58,39 +88,42 @@ public final class Shapes { +@@ -58,39 +106,42 @@ public final class Shapes { } - public static VoxelShape create(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + public static VoxelShape create(final double minX, final double minY, final double minZ, final double maxX, final double maxY, final double maxZ) { + // Paper start - optimise collisions if (!(maxX - minX < 1.0E-7) && !(maxY - minY < 1.0E-7) && !(maxZ - minZ < 1.0E-7)) { -- int i = findBits(minX, maxX); -- int i1 = findBits(minY, maxY); -- int i2 = findBits(minZ, maxZ); -- if (i < 0 || i1 < 0 || i2 < 0) { +- int xBits = findBits(minX, maxX); +- int yBits = findBits(minY, maxY); +- int zBits = findBits(minZ, maxZ); +- if (xBits < 0 || yBits < 0 || zBits < 0) { + final int bitsX = findBits(minX, maxX); + final int bitsY = findBits(minY, maxY); + final int bitsZ = findBits(minZ, maxZ); @@ -34814,28 +35673,28 @@ index b17ffae691eaaedf6b1371adae02adf92147056e..ac4bb789df88c07ea38d20700dccb657 - DoubleArrayList.wrap(new double[]{minX, maxX}), - DoubleArrayList.wrap(new double[]{minY, maxY}), - DoubleArrayList.wrap(new double[]{minZ, maxZ}) -- ); -- } else if (i == 0 && i1 == 0 && i2 == 0) { -- return block(); -- } else { -- int i3 = 1 << i; -- int i4 = 1 << i1; -- int i5 = 1 << i2; -- BitSetDiscreteVoxelShape bitSetDiscreteVoxelShape = BitSetDiscreteVoxelShape.withFilledBounds( -- i3, -- i4, -- i5, -- (int)Math.round(minX * i3), -- (int)Math.round(minY * i4), -- (int)Math.round(minZ * i5), -- (int)Math.round(maxX * i3), -- (int)Math.round(maxY * i4), -- (int)Math.round(maxZ * i5) + minX == 0.0 && maxX == 1.0 ? ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.ZERO_ONE : DoubleArrayList.wrap(new double[] { minX, maxX }), + minY == 0.0 && maxY == 1.0 ? ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.ZERO_ONE : DoubleArrayList.wrap(new double[] { minY, maxY }), + minZ == 0.0 && maxZ == 1.0 ? ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.ZERO_ONE : DoubleArrayList.wrap(new double[] { minZ, maxZ }) ); -- return new CubeVoxelShape(bitSetDiscreteVoxelShape); +- } else if (xBits == 0 && yBits == 0 && zBits == 0) { +- return block(); +- } else { +- int xSize = 1 << xBits; +- int ySize = 1 << yBits; +- int zSize = 1 << zBits; +- BitSetDiscreteVoxelShape voxelShape = BitSetDiscreteVoxelShape.withFilledBounds( +- xSize, +- ySize, +- zSize, +- (int)Math.round(minX * xSize), +- (int)Math.round(minY * ySize), +- (int)Math.round(minZ * zSize), +- (int)Math.round(maxX * xSize), +- (int)Math.round(maxY * ySize), +- (int)Math.round(maxZ * zSize) +- ); +- return new CubeVoxelShape(voxelShape); } } else { - return empty(); @@ -34844,12 +35703,15 @@ index b17ffae691eaaedf6b1371adae02adf92147056e..ac4bb789df88c07ea38d20700dccb657 + // Paper end - optimise collisions } - public static VoxelShape create(AABB aabb) { -@@ -126,85 +159,52 @@ public final class Shapes { + public static VoxelShape create(final AABB aabb) { +@@ -125,90 +176,54 @@ public final class Shapes { + return join(first, second, BooleanOp.OR); } - public static VoxelShape or(VoxelShape shape1, VoxelShape... others) { -- return Arrays.stream(others).reduce(shape1, Shapes::or); +- public static VoxelShape or(final VoxelShape first, final VoxelShape... tail) { +- return Arrays.stream(tail).reduce(first, Shapes::or); ++ // Paper start - optimise collisions ++ public static VoxelShape or(final VoxelShape shape1, final VoxelShape... others) { + int size = others.length; + if (size == 0) { + return shape1; @@ -34886,120 +35748,124 @@ index b17ffae691eaaedf6b1371adae02adf92147056e..ac4bb789df88c07ea38d20700dccb657 + // Paper end - optimise collisions } - public static VoxelShape join(VoxelShape shape1, VoxelShape shape2, BooleanOp function) { -- return joinUnoptimized(shape1, shape2, function).optimize(); -+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.joinOptimized(shape1, shape2, function); // Paper - optimise collisions + public static VoxelShape join(final VoxelShape first, final VoxelShape second, final BooleanOp op) { +- return joinUnoptimized(first, second, op).optimize(); ++ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.joinOptimized(first, second, op); // Paper - optimise collisions } - public static VoxelShape joinUnoptimized(VoxelShape shape1, VoxelShape shape2, BooleanOp function) { -- if (function.apply(false, false)) { + public static VoxelShape joinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp op) { +- if (op.apply(false, false)) { - throw (IllegalArgumentException)Util.pauseInIde(new IllegalArgumentException()); -- } else if (shape1 == shape2) { -- return function.apply(true, true) ? shape1 : empty(); +- } else if (first == second) { +- return op.apply(true, true) ? first : empty(); - } else { -- boolean flag = function.apply(true, false); -- boolean flag1 = function.apply(false, true); -- if (shape1.isEmpty()) { -- return flag1 ? shape2 : empty(); -- } else if (shape2.isEmpty()) { -- return flag ? shape1 : empty(); +- boolean firstOnlyMatters = op.apply(true, false); +- boolean secondOnlyMatters = op.apply(false, true); +- if (first.isEmpty()) { +- return secondOnlyMatters ? second : empty(); +- } else if (second.isEmpty()) { +- return firstOnlyMatters ? first : empty(); - } else { -- IndexMerger indexMerger = createIndexMerger(1, shape1.getCoords(Direction.Axis.X), shape2.getCoords(Direction.Axis.X), flag, flag1); -- IndexMerger indexMerger1 = createIndexMerger( -- indexMerger.size() - 1, shape1.getCoords(Direction.Axis.Y), shape2.getCoords(Direction.Axis.Y), flag, flag1 +- IndexMerger xMerger = createIndexMerger( +- 1, first.getCoords(Direction.Axis.X), second.getCoords(Direction.Axis.X), firstOnlyMatters, secondOnlyMatters - ); -- IndexMerger indexMerger2 = createIndexMerger( -- (indexMerger.size() - 1) * (indexMerger1.size() - 1), shape1.getCoords(Direction.Axis.Z), shape2.getCoords(Direction.Axis.Z), flag, flag1 +- IndexMerger yMerger = createIndexMerger( +- xMerger.size() - 1, first.getCoords(Direction.Axis.Y), second.getCoords(Direction.Axis.Y), firstOnlyMatters, secondOnlyMatters - ); -- BitSetDiscreteVoxelShape bitSetDiscreteVoxelShape = BitSetDiscreteVoxelShape.join( -- shape1.shape, shape2.shape, indexMerger, indexMerger1, indexMerger2, function +- IndexMerger zMerger = createIndexMerger( +- (xMerger.size() - 1) * (yMerger.size() - 1), +- first.getCoords(Direction.Axis.Z), +- second.getCoords(Direction.Axis.Z), +- firstOnlyMatters, +- secondOnlyMatters - ); -- return (VoxelShape)(indexMerger instanceof DiscreteCubeMerger -- && indexMerger1 instanceof DiscreteCubeMerger -- && indexMerger2 instanceof DiscreteCubeMerger -- ? new CubeVoxelShape(bitSetDiscreteVoxelShape) -- : new ArrayVoxelShape(bitSetDiscreteVoxelShape, indexMerger.getList(), indexMerger1.getList(), indexMerger2.getList())); +- BitSetDiscreteVoxelShape voxelShape = BitSetDiscreteVoxelShape.join(first.shape, second.shape, xMerger, yMerger, zMerger, op); +- return (VoxelShape)(xMerger instanceof DiscreteCubeMerger && yMerger instanceof DiscreteCubeMerger && zMerger instanceof DiscreteCubeMerger +- ? new CubeVoxelShape(voxelShape) +- : new ArrayVoxelShape(voxelShape, xMerger.getList(), yMerger.getList(), zMerger.getList())); - } - } -+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.joinUnoptimized(shape1, shape2, function); // Paper - optimise collisions ++ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.joinUnoptimized(first, second, op); // Paper - optimise collisions } - public static boolean joinIsNotEmpty(VoxelShape shape1, VoxelShape shape2, BooleanOp resultOperator) { -- if (resultOperator.apply(false, false)) { + public static boolean joinIsNotEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp op) { +- if (op.apply(false, false)) { - throw (IllegalArgumentException)Util.pauseInIde(new IllegalArgumentException()); - } else { -- boolean isEmpty = shape1.isEmpty(); -- boolean isEmpty1 = shape2.isEmpty(); -- if (!isEmpty && !isEmpty1) { -- if (shape1 == shape2) { -- return resultOperator.apply(true, true); +- boolean firstEmpty = first.isEmpty(); +- boolean secondEmpty = second.isEmpty(); +- if (!firstEmpty && !secondEmpty) { +- if (first == second) { +- return op.apply(true, true); - } else { -- boolean flag = resultOperator.apply(true, false); -- boolean flag1 = resultOperator.apply(false, true); +- boolean firstOnlyMatters = op.apply(true, false); +- boolean secondOnlyMatters = op.apply(false, true); - - for (Direction.Axis axis : AxisCycle.AXIS_VALUES) { -- if (shape1.max(axis) < shape2.min(axis) - 1.0E-7) { -- return flag || flag1; +- if (first.max(axis) < second.min(axis) - 1.0E-7) { +- return firstOnlyMatters || secondOnlyMatters; - } - -- if (shape2.max(axis) < shape1.min(axis) - 1.0E-7) { -- return flag || flag1; +- if (second.max(axis) < first.min(axis) - 1.0E-7) { +- return firstOnlyMatters || secondOnlyMatters; - } - } - -- IndexMerger indexMerger = createIndexMerger(1, shape1.getCoords(Direction.Axis.X), shape2.getCoords(Direction.Axis.X), flag, flag1); -- IndexMerger indexMerger1 = createIndexMerger( -- indexMerger.size() - 1, shape1.getCoords(Direction.Axis.Y), shape2.getCoords(Direction.Axis.Y), flag, flag1 +- IndexMerger xMerger = createIndexMerger( +- 1, first.getCoords(Direction.Axis.X), second.getCoords(Direction.Axis.X), firstOnlyMatters, secondOnlyMatters +- ); +- IndexMerger yMerger = createIndexMerger( +- xMerger.size() - 1, first.getCoords(Direction.Axis.Y), second.getCoords(Direction.Axis.Y), firstOnlyMatters, secondOnlyMatters - ); -- IndexMerger indexMerger2 = createIndexMerger( -- (indexMerger.size() - 1) * (indexMerger1.size() - 1), -- shape1.getCoords(Direction.Axis.Z), -- shape2.getCoords(Direction.Axis.Z), -- flag, -- flag1 +- IndexMerger zMerger = createIndexMerger( +- (xMerger.size() - 1) * (yMerger.size() - 1), +- first.getCoords(Direction.Axis.Z), +- second.getCoords(Direction.Axis.Z), +- firstOnlyMatters, +- secondOnlyMatters - ); -- return joinIsNotEmpty(indexMerger, indexMerger1, indexMerger2, shape1.shape, shape2.shape, resultOperator); +- return joinIsNotEmpty(xMerger, yMerger, zMerger, first.shape, second.shape, op); - } - } else { -- return resultOperator.apply(!isEmpty, !isEmpty1); +- return op.apply(!firstEmpty, !secondEmpty); - } - } -+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isJoinNonEmpty(shape1, shape2, resultOperator); // Paper - optimise collisions ++ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isJoinNonEmpty(first, second, op); // Paper - optimise collisions } private static boolean joinIsNotEmpty( -@@ -236,52 +236,113 @@ public final class Shapes { - return desiredOffset; +@@ -238,51 +253,93 @@ public final class Shapes { + return distance; } -- public static boolean blockOccludes(VoxelShape shape, VoxelShape adjacentShape, Direction side) { -- if (shape == block() && adjacentShape == block()) { +- public static boolean blockOccludes(final VoxelShape shape, final VoxelShape occluder, final Direction direction) { +- if (shape == block() && occluder == block()) { + // Paper start - optimise collisions + public static boolean blockOccludes(final VoxelShape first, final VoxelShape second, final Direction direction) { + if (first == BLOCK & second == BLOCK) { return true; -- } else if (adjacentShape.isEmpty()) { +- } else if (occluder.isEmpty()) { + } + + if (first.isEmpty() | second.isEmpty()) { - return false; -- } else { -- Direction.Axis axis = side.getAxis(); -- Direction.AxisDirection axisDirection = side.getAxisDirection(); -- VoxelShape voxelShape = axisDirection == Direction.AxisDirection.POSITIVE ? shape : adjacentShape; -- VoxelShape voxelShape1 = axisDirection == Direction.AxisDirection.POSITIVE ? adjacentShape : shape; -- BooleanOp booleanOp = axisDirection == Direction.AxisDirection.POSITIVE ? BooleanOp.ONLY_FIRST : BooleanOp.ONLY_SECOND; -- return DoubleMath.fuzzyEquals(voxelShape.max(axis), 1.0, 1.0E-7) -- && DoubleMath.fuzzyEquals(voxelShape1.min(axis), 0.0, 1.0E-7) -- && !joinIsNotEmpty(new SliceShape(voxelShape, axis, voxelShape.shape.getSize(axis) - 1), new SliceShape(voxelShape1, axis, 0), booleanOp); - } ++ return false; ++ } + + // we optimise getOpposite, so we can use it + // secondly, use our cache to retrieve sliced shape + final VoxelShape newFirst = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$getFaceShapeClamped(direction); + if (newFirst.isEmpty()) { -+ return false; -+ } + return false; +- } else { +- Direction.Axis axis = direction.getAxis(); +- Direction.AxisDirection sign = direction.getAxisDirection(); +- VoxelShape first = sign == Direction.AxisDirection.POSITIVE ? shape : occluder; +- VoxelShape second = sign == Direction.AxisDirection.POSITIVE ? occluder : shape; +- BooleanOp op = sign == Direction.AxisDirection.POSITIVE ? BooleanOp.ONLY_FIRST : BooleanOp.ONLY_SECOND; +- return DoubleMath.fuzzyEquals(first.max(axis), 1.0, 1.0E-7) +- && DoubleMath.fuzzyEquals(second.min(axis), 0.0, 1.0E-7) +- && !joinIsNotEmpty(new SliceShape(first, axis, first.shape.getSize(axis) - 1), new SliceShape(second, axis, 0), op); + } + final VoxelShape newSecond = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$getFaceShapeClamped(direction.getOpposite()); + if (newSecond.isEmpty()) { + return false; @@ -35009,55 +35875,35 @@ index b17ffae691eaaedf6b1371adae02adf92147056e..ac4bb789df88c07ea38d20700dccb657 + // Paper end - optimise collisions } -- public static boolean mergedFaceOccludes(VoxelShape shape, VoxelShape adjacentShape, Direction side) { -- if (shape != block() && adjacentShape != block()) { -- Direction.Axis axis = side.getAxis(); -- Direction.AxisDirection axisDirection = side.getAxisDirection(); -- VoxelShape voxelShape = axisDirection == Direction.AxisDirection.POSITIVE ? shape : adjacentShape; -- VoxelShape voxelShape1 = axisDirection == Direction.AxisDirection.POSITIVE ? adjacentShape : shape; -- if (!DoubleMath.fuzzyEquals(voxelShape.max(axis), 1.0, 1.0E-7)) { -- voxelShape = empty(); +- public static boolean mergedFaceOccludes(final VoxelShape shape, final VoxelShape occluder, final Direction direction) { +- if (shape != block() && occluder != block()) { +- Direction.Axis axis = direction.getAxis(); +- Direction.AxisDirection sign = direction.getAxisDirection(); +- VoxelShape first = sign == Direction.AxisDirection.POSITIVE ? shape : occluder; +- VoxelShape second = sign == Direction.AxisDirection.POSITIVE ? occluder : shape; +- if (!DoubleMath.fuzzyEquals(first.max(axis), 1.0, 1.0E-7)) { +- first = empty(); - } + // Paper start - optimise collisions -+ private static boolean mergedMayOccludeBlock(final VoxelShape shape1, final VoxelShape shape2) { -+ // if the combined bounds of the two shapes cannot occlude, then neither can the merged -+ final AABB bounds1 = shape1.bounds(); -+ final AABB bounds2 = shape2.bounds(); - -- if (!DoubleMath.fuzzyEquals(voxelShape1.min(axis), 0.0, 1.0E-7)) { -- voxelShape1 = empty(); -- } -+ final double minX = Math.min(bounds1.minX, bounds2.minX); -+ final double minY = Math.min(bounds1.minY, bounds2.minY); -+ final double minZ = Math.min(bounds1.minZ, bounds2.minZ); - -- return !joinIsNotEmpty( -- block(), -- joinUnoptimized(new SliceShape(voxelShape, axis, voxelShape.shape.getSize(axis) - 1), new SliceShape(voxelShape1, axis, 0), BooleanOp.OR), -- BooleanOp.ONLY_FIRST -- ); -- } else { -+ final double maxX = Math.max(bounds1.maxX, bounds2.maxX); -+ final double maxY = Math.max(bounds1.maxY, bounds2.maxY); -+ final double maxZ = Math.max(bounds1.maxZ, bounds2.maxZ); -+ -+ return (minX <= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON && maxX >= (1 - ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) && -+ (minY <= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON && maxY >= (1 - ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) && -+ (minZ <= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON && maxZ >= (1 - ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)); -+ } -+ // Paper end - optimise collisions -+ -+ // Paper start - optimise collisions + public static boolean mergedFaceOccludes(final VoxelShape first, final VoxelShape second, final Direction direction) { + // see if any of the shapes on their own occludes, only if cached + if (((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$occludesFullBlockIfCached() || ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$occludesFullBlockIfCached()) { + return true; + } -+ + +- if (!DoubleMath.fuzzyEquals(second.min(axis), 0.0, 1.0E-7)) { +- second = empty(); +- } + if (first.isEmpty() & second.isEmpty()) { + return false; + } -+ + +- return !joinIsNotEmpty( +- block(), +- joinUnoptimized(new SliceShape(first, axis, first.shape.getSize(axis) - 1), new SliceShape(second, axis, 0), BooleanOp.OR), +- BooleanOp.ONLY_FIRST +- ); +- } else { + // we optimise getOpposite, so we can use it + // secondly, use our cache to retrieve sliced shape + final VoxelShape newFirst = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$getFaceShapeClamped(direction); @@ -35107,30 +35953,29 @@ index b17ffae691eaaedf6b1371adae02adf92147056e..ac4bb789df88c07ea38d20700dccb657 + return ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)shape1).moonrise$occludesFullBlock(); + } -- public static boolean faceShapeOccludes(VoxelShape shape, VoxelShape adjacentShape) { +- public static boolean faceShapeOccludes(final VoxelShape shape, final VoxelShape occluder) { - return shape == block() -- || adjacentShape == block() -- || (!shape.isEmpty() || !adjacentShape.isEmpty()) -- && !joinIsNotEmpty(block(), joinUnoptimized(shape, adjacentShape, BooleanOp.OR), BooleanOp.ONLY_FIRST); +- || occluder == block() +- || (!shape.isEmpty() || !occluder.isEmpty()) && !joinIsNotEmpty(block(), joinUnoptimized(shape, occluder, BooleanOp.OR), BooleanOp.ONLY_FIRST); + return mergedMayOccludeBlock(shape1, shape2) && ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)shape1).moonrise$orUnoptimized(shape2)).moonrise$occludesFullBlock(); + // Paper end - optimise collisions } @VisibleForTesting diff --git a/net/minecraft/world/phys/shapes/SliceShape.java b/net/minecraft/world/phys/shapes/SliceShape.java -index 79f7f04207891dd98cc0b2d93ecb2e07c8baa7b6..7ca12213c10f962ff597a8d51413a17b1827bbb4 100644 +index ec7834b53788f95ab7956c22bddfa38c4ee4e2f2..a612ace0b4e128c05196400b125a12a8918e8b0d 100644 --- a/net/minecraft/world/phys/shapes/SliceShape.java +++ b/net/minecraft/world/phys/shapes/SliceShape.java @@ -12,6 +12,7 @@ public class SliceShape extends VoxelShape { - super(makeSlice(delegate.shape, axis, index)); + super(makeSlice(delegate.shape, axis, point)); this.delegate = delegate; this.axis = axis; + ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)this).moonrise$initCache(); // Paper - optimise collisions } - private static DiscreteVoxelShape makeSlice(DiscreteVoxelShape shape, Direction.Axis axis, int index) { + private static DiscreteVoxelShape makeSlice(final DiscreteVoxelShape delegate, final Direction.Axis axis, final int point) { diff --git a/net/minecraft/world/phys/shapes/VoxelShape.java b/net/minecraft/world/phys/shapes/VoxelShape.java -index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842cf10fceb 100644 +index 60a64339cd3b378f6f04cf5fe29a3ea8c18ec480..26619ecad88175cf76a62a5794aa58996968edb7 100644 --- a/net/minecraft/world/phys/shapes/VoxelShape.java +++ b/net/minecraft/world/phys/shapes/VoxelShape.java @@ -18,60 +18,545 @@ import net.minecraft.world.phys.Vec3; @@ -35557,11 +36402,11 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 + } + // Paper end - optimise collisions + - protected VoxelShape(DiscreteVoxelShape shape) { + protected VoxelShape(final DiscreteVoxelShape shape) { this.shape = shape; } - public double min(Direction.Axis axis) { + public double min(final Direction.Axis axis) { - int i = this.shape.firstFull(axis); - return i >= this.shape.getSize(axis) ? Double.POSITIVE_INFINITY : this.get(axis, i); + // Paper start - optimise collisions @@ -35587,7 +36432,7 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 + // Paper end - optimise collisions } - public double max(Direction.Axis axis) { + public double max(final Direction.Axis axis) { - int i = this.shape.lastFull(axis); - return i <= 0 ? Double.NEGATIVE_INFINITY : this.get(axis, i); + // Paper start - optimise collisions @@ -35679,10 +36524,10 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 + // Paper end - optimise collisions } - protected double get(Direction.Axis axis, int index) { -- return this.getCoords(axis).getDouble(index); + protected double get(final Direction.Axis axis, final int i) { +- return this.getCoords(axis).getDouble(i); + // Paper start - optimise collisions -+ final int idx = index; ++ final int idx = i; + switch (axis) { + case X: { + return this.rootCoordinatesX[idx] + this.offsetX; @@ -35700,25 +36545,25 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 + // Paper end - optimise collisions } - public abstract DoubleList getCoords(Direction.Axis axis); + public abstract DoubleList getCoords(final Direction.Axis axis); public boolean isEmpty() { - return this.shape.isEmpty(); + return this.isEmpty; // Paper - optimise collisions } - public VoxelShape move(Vec3 offset) { + public VoxelShape move(final Vec3 delta) { @@ -83,20 +568,96 @@ public abstract class VoxelShape { } - public VoxelShape move(double xOffset, double yOffset, double zOffset) { + public VoxelShape move(final double dx, final double dy, final double dz) { - return (VoxelShape)(this.isEmpty() - ? Shapes.empty() - : new ArrayVoxelShape( - this.shape, -- new OffsetDoubleList(this.getCoords(Direction.Axis.X), xOffset), -- new OffsetDoubleList(this.getCoords(Direction.Axis.Y), yOffset), -- new OffsetDoubleList(this.getCoords(Direction.Axis.Z), zOffset) +- new OffsetDoubleList(this.getCoords(Direction.Axis.X), dx), +- new OffsetDoubleList(this.getCoords(Direction.Axis.Y), dy), +- new OffsetDoubleList(this.getCoords(Direction.Axis.Z), dz) - )); + // Paper start - optimise collisions + if (this.isEmpty) { @@ -35727,14 +36572,14 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 + + final ArrayVoxelShape ret = new ArrayVoxelShape( + this.shape, -+ offsetList(this.rootCoordinatesX, this.offsetX + xOffset), -+ offsetList(this.rootCoordinatesY, this.offsetY + yOffset), -+ offsetList(this.rootCoordinatesZ, this.offsetZ + zOffset) ++ offsetList(this.rootCoordinatesX, this.offsetX + dx), ++ offsetList(this.rootCoordinatesY, this.offsetY + dy), ++ offsetList(this.rootCoordinatesZ, this.offsetZ + dz) + ); + + final ca.spottedleaf.moonrise.patches.collisions.shape.CachedToAABBs cachedToAABBs = this.cachedToAABBs; + if (cachedToAABBs != null) { -+ ((VoxelShape)(Object)ret).cachedToAABBs = ca.spottedleaf.moonrise.patches.collisions.shape.CachedToAABBs.offset(cachedToAABBs, xOffset, yOffset, zOffset); ++ ((VoxelShape)(Object)ret).cachedToAABBs = ca.spottedleaf.moonrise.patches.collisions.shape.CachedToAABBs.offset(cachedToAABBs, dx, dy, dz); + } + + return ret; @@ -35742,9 +36587,9 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 } public VoxelShape optimize() { -- VoxelShape[] voxelShapes = new VoxelShape[]{Shapes.empty()}; -- this.forAllBoxes((x1, y1, z1, x2, y2, z2) -> voxelShapes[0] = Shapes.joinUnoptimized(voxelShapes[0], Shapes.box(x1, y1, z1, x2, y2, z2), BooleanOp.OR)); -- return voxelShapes[0]; +- VoxelShape[] result = new VoxelShape[]{Shapes.empty()}; +- this.forAllBoxes((x1, y1, z1, x2, y2, z2) -> result[0] = Shapes.joinUnoptimized(result[0], Shapes.box(x1, y1, z1, x2, y2, z2), BooleanOp.OR)); +- return result[0]; + // Paper start - optimise collisions + if (this.isEmpty) { + return Shapes.empty(); @@ -35815,8 +36660,8 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 + // Paper end - optimise collisions } - public void forAllEdges(Shapes.DoubleLineConsumer action) { -@@ -128,9 +689,24 @@ public abstract class VoxelShape { + public void forAllEdges(final Shapes.DoubleLineConsumer consumer) { +@@ -133,9 +694,24 @@ public abstract class VoxelShape { } public List toAabbs() { @@ -35843,14 +36688,14 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 + // Paper end - optimise collisions } - public double min(Direction.Axis axis, double primaryPosition, double secondaryPosition) { -@@ -152,46 +728,91 @@ public abstract class VoxelShape { + public double min(final Direction.Axis aAxis, final double b, final double c) { +@@ -157,46 +733,91 @@ public abstract class VoxelShape { } - protected int findIndex(Direction.Axis axis, double position) { -- return Mth.binarySearch(0, this.shape.getSize(axis) + 1, value -> position < this.get(axis, value)) - 1; + protected int findIndex(final Direction.Axis axis, final double coord) { +- return Mth.binarySearch(0, this.shape.getSize(axis) + 1, index -> coord < this.get(axis, index)) - 1; + // Paper start - optimise collisions -+ final double value = position; ++ final double value = coord; + switch (axis) { + case X: { + final double[] values = this.rootCoordinatesX; @@ -35877,33 +36722,33 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 + // Paper end - optimise collisions } - public @Nullable BlockHitResult clip(Vec3 startVec, Vec3 endVec, BlockPos pos) { + public @Nullable BlockHitResult clip(final Vec3 from, final Vec3 to, final BlockPos pos) { - if (this.isEmpty()) { + // Paper start - optimise collisions + if (this.isEmpty) { return null; - } else { -- Vec3 vec3 = endVec.subtract(startVec); -- if (vec3.lengthSqr() < 1.0E-7) { +- Vec3 diff = to.subtract(from); +- if (diff.lengthSqr() < 1.0E-7) { - return null; - } else { -- Vec3 vec31 = startVec.add(vec3.scale(0.001)); +- Vec3 testPoint = from.add(diff.scale(0.001)); - return this.shape - .isFullWide( -- this.findIndex(Direction.Axis.X, vec31.x - pos.getX()), -- this.findIndex(Direction.Axis.Y, vec31.y - pos.getY()), -- this.findIndex(Direction.Axis.Z, vec31.z - pos.getZ()) +- this.findIndex(Direction.Axis.X, testPoint.x - pos.getX()), +- this.findIndex(Direction.Axis.Y, testPoint.y - pos.getY()), +- this.findIndex(Direction.Axis.Z, testPoint.z - pos.getZ()) - ) -- ? new BlockHitResult(vec31, Direction.getApproximateNearest(vec3.x, vec3.y, vec3.z).getOpposite(), pos, true) -- : AABB.clip(this.toAabbs(), startVec, endVec, pos); +- ? new BlockHitResult(testPoint, Direction.getApproximateNearest(diff.x, diff.y, diff.z).getOpposite(), pos, true) +- : AABB.clip(this.toAabbs(), from, to, pos); + } + -+ final Vec3 directionOpposite = endVec.subtract(startVec); ++ final Vec3 directionOpposite = to.subtract(from); + if (directionOpposite.lengthSqr() < ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) { + return null; + } + -+ final Vec3 fromBehind = startVec.add(directionOpposite.scale(0.001)); ++ final Vec3 fromBehind = from.add(directionOpposite.scale(0.001)); + final double fromBehindOffsetX = fromBehind.x - (double) pos.getX(); + final double fromBehindOffsetY = fromBehind.y - (double) pos.getY(); + final double fromBehindOffsetZ = fromBehind.z - (double) pos.getZ(); @@ -35913,34 +36758,34 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 + if (singleAABB.contains(fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) { + return new BlockHitResult(fromBehind, Direction.getApproximateNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), pos, true); } -+ return clip(singleAABB, startVec, endVec, pos); ++ return clip(singleAABB, from, to, pos); + } + + if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.strictlyContains((VoxelShape) (Object) this, fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) { + return new BlockHitResult(fromBehind, Direction.getApproximateNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), pos, true); } + -+ return AABB.clip(((VoxelShape) (Object) this).toAabbs(), startVec, endVec, pos); ++ return AABB.clip(((VoxelShape) (Object) this).toAabbs(), from, to, pos); + // Paper end - optimise collisions } -+ // Paper start - optimise collisions - public Optional closestPointTo(Vec3 point) { + public Optional closestPointTo(final Vec3 point) { - if (this.isEmpty()) { ++ // Paper start - optimise collisions + if (this.isEmpty) { return Optional.empty(); - } else { -- MutableObject mutableObject = new MutableObject<>(); +- MutableObject closest = new MutableObject<>(); - this.forAllBoxes((x1, y1, z1, x2, y2, z2) -> { -- double d = Mth.clamp(point.x(), x1, x2); -- double d1 = Mth.clamp(point.y(), y1, y2); -- double d2 = Mth.clamp(point.z(), z1, z2); -- Vec3 vec3 = mutableObject.get(); -- if (vec3 == null || point.distanceToSqr(d, d1, d2) < point.distanceToSqr(vec3)) { -- mutableObject.setValue(new Vec3(d, d1, d2)); +- double x = Mth.clamp(point.x(), x1, x2); +- double y = Mth.clamp(point.y(), y1, y2); +- double z = Mth.clamp(point.z(), z1, z2); +- Vec3 currentClosest = closest.get(); +- if (currentClosest == null || point.distanceToSqr(x, y, z) < point.distanceToSqr(currentClosest)) { +- closest.setValue(new Vec3(x, y, z)); - } - }); -- return Optional.of(Objects.requireNonNull(mutableObject.get())); +- return Optional.of(Objects.requireNonNull(closest.get())); } + + Vec3 ret = null; @@ -35964,33 +36809,33 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 + // Paper end - optimise collisions } - public VoxelShape getFaceShape(Direction side) { -@@ -214,19 +835,23 @@ public abstract class VoxelShape { + public VoxelShape getFaceShape(final Direction direction) { +@@ -219,19 +840,23 @@ public abstract class VoxelShape { } - private VoxelShape calculateFace(Direction side) { -- Direction.Axis axis = side.getAxis(); + private VoxelShape calculateFace(final Direction direction) { +- Direction.Axis axis = direction.getAxis(); - if (this.isCubeLikeAlong(axis)) { - return this; - } else { -- Direction.AxisDirection axisDirection = side.getAxisDirection(); -- int i = this.findIndex(axis, axisDirection == Direction.AxisDirection.POSITIVE ? 0.9999999 : 1.0E-7); -- SliceShape sliceShape = new SliceShape(this, axis, i); -- if (sliceShape.isEmpty()) { +- Direction.AxisDirection sign = direction.getAxisDirection(); +- int index = this.findIndex(axis, sign == Direction.AxisDirection.POSITIVE ? 0.9999999 : 1.0E-7); +- SliceShape slice = new SliceShape(this, axis, index); +- if (slice.isEmpty()) { - return Shapes.empty(); - } else { -- return (VoxelShape)(sliceShape.isCubeLike() ? Shapes.block() : sliceShape); +- return (VoxelShape)(slice.isCubeLike() ? Shapes.block() : slice); + // Paper start - optimise collisions -+ final Direction.Axis axis = side.getAxis(); ++ final Direction.Axis axis = direction.getAxis(); + switch (axis) { + case X: { -+ return this.calculateFaceDirect(side, axis, this.rootCoordinatesX, this.offsetX); ++ return this.calculateFaceDirect(direction, axis, this.rootCoordinatesX, this.offsetX); + } + case Y: { -+ return this.calculateFaceDirect(side, axis, this.rootCoordinatesY, this.offsetY); ++ return this.calculateFaceDirect(direction, axis, this.rootCoordinatesY, this.offsetY); + } + case Z: { -+ return this.calculateFaceDirect(side, axis, this.rootCoordinatesZ, this.offsetZ); ++ return this.calculateFaceDirect(direction, axis, this.rootCoordinatesZ, this.offsetZ); + } + default: { + throw new IllegalStateException("Unknown axis: " + axis); @@ -36000,12 +36845,12 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 } protected boolean isCubeLike() { -@@ -244,9 +869,30 @@ public abstract class VoxelShape { +@@ -249,9 +874,30 @@ public abstract class VoxelShape { return coords.size() == 2 && DoubleMath.fuzzyEquals(coords.getDouble(0), 0.0, 1.0E-7) && DoubleMath.fuzzyEquals(coords.getDouble(1), 1.0, 1.0E-7); } -- public double collide(Direction.Axis movementAxis, AABB collisionBox, double desiredOffset) { -- return this.collideX(AxisCycle.between(movementAxis, Direction.Axis.X), collisionBox, desiredOffset); +- public double collide(final Direction.Axis axis, final AABB moving, final double distance) { +- return this.collideX(AxisCycle.between(axis, Direction.Axis.X), moving, distance); + // Paper start - optimise collisions + public double collide(final Direction.Axis axis, final AABB source, final double source_move) { + if (this.isEmpty) { @@ -36031,18 +36876,18 @@ index 72727d38d1de7ae82b0fc755aacbc600cb811078..59ca209aa0c545c4288c89ea5ff18842 } + // Paper end - optimise collisions - protected double collideX(AxisCycle movementAxis, AABB collisionBox, double desiredOffset) { + protected double collideX(final AxisCycle transform, final AABB moving, double distance) { if (this.isEmpty()) { diff --git a/net/minecraft/world/ticks/LevelChunkTicks.java b/net/minecraft/world/ticks/LevelChunkTicks.java -index 5fa7b312bf3bdd1ea0900dc7c599f88542115329..0b13971259f58876b778c0d28ad20c3510ed46ad 100644 +index 082258e7d8270d4026a70a5c0a9e1228d70a2e8d..c3825e91504f5df6bfd8e41de1f5dc4ae45d81e2 100644 --- a/net/minecraft/world/ticks/LevelChunkTicks.java +++ b/net/minecraft/world/ticks/LevelChunkTicks.java @@ -13,12 +13,36 @@ import java.util.stream.Stream; import net.minecraft.core.BlockPos; import org.jspecify.annotations.Nullable; --public class LevelChunkTicks implements SerializableTickContainer, TickContainerAccess { -+public class LevelChunkTicks implements SerializableTickContainer, TickContainerAccess, ca.spottedleaf.moonrise.patches.chunk_system.ticks.ChunkSystemLevelChunkTicks { // Paper - rewrite chunk system +-public class LevelChunkTicks implements TickContainerAccess, SerializableTickContainer { ++public class LevelChunkTicks implements TickContainerAccess, SerializableTickContainer, ca.spottedleaf.moonrise.patches.chunk_system.ticks.ChunkSystemLevelChunkTicks { // Paper - rewrite chunk system private final Queue> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER); private @Nullable List> pendingTicks; private final Set> ticksPerPosition = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); @@ -36075,62 +36920,62 @@ index 5fa7b312bf3bdd1ea0900dc7c599f88542115329..0b13971259f58876b778c0d28ad20c35 public LevelChunkTicks() { } -@@ -41,7 +65,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon +@@ -41,7 +65,7 @@ public class LevelChunkTicks implements TickContainerAccess, SerializableT public @Nullable ScheduledTick poll() { - ScheduledTick scheduledTick = this.tickQueue.poll(); - if (scheduledTick != null) { -- this.ticksPerPosition.remove(scheduledTick); -+ this.ticksPerPosition.remove(scheduledTick); this.dirty = true; // Paper - rewrite chunk system + ScheduledTick result = this.tickQueue.poll(); + if (result != null) { +- this.ticksPerPosition.remove(result); ++ this.ticksPerPosition.remove(result); this.dirty = true; // Paper - rewrite chunk system } - return scheduledTick; -@@ -50,7 +74,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + return result; +@@ -50,7 +74,7 @@ public class LevelChunkTicks implements TickContainerAccess, SerializableT @Override - public void schedule(ScheduledTick tick) { + public void schedule(final ScheduledTick tick) { if (this.ticksPerPosition.add(tick)) { - this.scheduleUnchecked(tick); + this.scheduleUnchecked(tick); this.dirty = true; // Paper - rewrite chunk system } } -@@ -72,7 +96,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon +@@ -72,7 +96,7 @@ public class LevelChunkTicks implements TickContainerAccess, SerializableT while (iterator.hasNext()) { - ScheduledTick scheduledTick = iterator.next(); - if (predicate.test(scheduledTick)) { + ScheduledTick tick = iterator.next(); + if (test.test(tick)) { - iterator.remove(); + iterator.remove(); this.dirty = true; // Paper - rewrite chunk system - this.ticksPerPosition.remove(scheduledTick); + this.ticksPerPosition.remove(tick); } } -@@ -89,6 +113,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon +@@ -89,6 +113,7 @@ public class LevelChunkTicks implements TickContainerAccess, SerializableT @Override - public List> pack(long gameTime) { -+ this.lastSaved = gameTime; // Paper - rewrite chunk system - List> list = new ArrayList<>(this.tickQueue.size()); + public List> pack(final long currentTick) { ++ this.lastSaved = currentTick; // Paper - rewrite chunk system + List> ticks = new ArrayList<>(this.tickQueue.size()); if (this.pendingTicks != null) { - list.addAll(this.pendingTicks); -@@ -103,6 +128,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + ticks.addAll(this.pendingTicks); +@@ -103,6 +128,7 @@ public class LevelChunkTicks implements TickContainerAccess, SerializableT - public void unpack(long gameTime) { + public void unpack(final long currentTick) { if (this.pendingTicks != null) { -+ this.lastSaved = gameTime; // Paper - rewrite chunk system - int i = -this.pendingTicks.size(); ++ this.lastSaved = currentTick; // Paper - rewrite chunk system + int subTickBase = -this.pendingTicks.size(); - for (SavedTick savedTick : this.pendingTicks) { + for (SavedTick pendingTick : this.pendingTicks) { diff --git a/net/minecraft/world/waypoints/WaypointTransmitter.java b/net/minecraft/world/waypoints/WaypointTransmitter.java -index d66f7afa03790d54caac726063aff4a995002d8a..df85e70a7a2bb2591cab7879b9910f6aa60ad166 100644 +index d785e9a69a109d1d7ce7a7e182c215f3d6a35b64..d6f842cbd2060eb56a1e4384b3ea97feea3e73ba 100644 --- a/net/minecraft/world/waypoints/WaypointTransmitter.java +++ b/net/minecraft/world/waypoints/WaypointTransmitter.java -@@ -32,7 +32,10 @@ public interface WaypointTransmitter extends Waypoint { +@@ -34,7 +34,10 @@ public interface WaypointTransmitter extends Waypoint { } - static boolean isChunkVisible(ChunkPos pos, ServerPlayer player) { -- return player.getChunkTrackingView().isInViewDistance(pos.x, pos.z); + static boolean isChunkVisible(final ChunkPos chunkPos, final ServerPlayer receiver) { +- return receiver.getChunkTrackingView().isInViewDistance(chunkPos.x(), chunkPos.z()); + // Paper start - rewrite chunk system -+ final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData playerChunkLoader = ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer)player).moonrise$getChunkLoader(); -+ return playerChunkLoader != null && playerChunkLoader.getSentChunksRaw().contains(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(pos)); ++ final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData playerChunkLoader = ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer)receiver).moonrise$getChunkLoader(); ++ return playerChunkLoader != null && playerChunkLoader.getSentChunksRaw().contains(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkPos)); + // Paper end - rewrite chunk system } - static boolean isReallyFar(LivingEntity entity, ServerPlayer player) { + static boolean isReallyFar(final LivingEntity source, final ServerPlayer receiver) { diff --git a/paper-server/patches/features/0002-Rewrite-dataconverter-system.patch b/paper-server/patches/features/0002-Rewrite-dataconverter-system.patch index 6da76e696512..c600af64fdc7 100644 --- a/paper-server/patches/features/0002-Rewrite-dataconverter-system.patch +++ b/paper-server/patches/features/0002-Rewrite-dataconverter-system.patch @@ -217,10 +217,10 @@ index 0000000000000000000000000000000000000000..515f6691c72ffa82ac8b92646768be7a +} diff --git a/ca/spottedleaf/dataconverter/minecraft/MCVersionRegistry.java b/ca/spottedleaf/dataconverter/minecraft/MCVersionRegistry.java new file mode 100644 -index 0000000000000000000000000000000000000000..78eefba4cbf90f56e1844d284e7200172df448d5 +index 0000000000000000000000000000000000000000..c48181291e18bbec351f267ac35ce97718aec24c --- /dev/null +++ b/ca/spottedleaf/dataconverter/minecraft/MCVersionRegistry.java -@@ -0,0 +1,499 @@ +@@ -0,0 +1,512 @@ +package ca.spottedleaf.dataconverter.minecraft; + +import ca.spottedleaf.dataconverter.converters.DataConverter; @@ -515,7 +515,9 @@ index 0000000000000000000000000000000000000000..78eefba4cbf90f56e1844d284e720017 + 4657, + 4658, + 4661, -+ // All up to 1.21.11 ++ 4763, ++ 4769, ++ // All up to 26.1.1 + }; + Arrays.sort(converterVersions); + @@ -571,6 +573,9 @@ index 0000000000000000000000000000000000000000..78eefba4cbf90f56e1844d284e720017 + // There is a read of entity sub data in V4299 (salmon) which was written to after V1_20_6 + // There is also a sub type read in V4290 as it reads and converts all data within a text component + registerBreakpointAfter(V4290.VERSION); ++ ++ // final release of major version ++ registerBreakpointAfter(MCVersions.V1_21_11, Integer.MAX_VALUE); + } + + static { @@ -590,7 +595,15 @@ index 0000000000000000000000000000000000000000..78eefba4cbf90f56e1844d284e720017 + LOGGER.warn("Error registering version \"" + name + "\", version number '" + value + "' is already associated with \"" + VERSION_NAMES.get(value) + "\""); + } + -+ VERSION_NAMES.put(value, name.substring(1).replace("_PRE", "-PRE").replace("_RC", "-RC").replace('_', '.').toLowerCase(Locale.ROOT)); ++ VERSION_NAMES.put( ++ value, name ++ .substring(1) ++ .replace("_PRE", "-PRE") ++ .replace("_RC", "-RC") ++ .replace("_SNAPSHOT", "-SNAPSHOT") ++ .replace('_', '.') ++ .toLowerCase(Locale.ROOT) ++ ); + } + + for (final int version : DATACONVERTER_VERSIONS_MAJOR) { @@ -722,10 +735,10 @@ index 0000000000000000000000000000000000000000..78eefba4cbf90f56e1844d284e720017 +} diff --git a/ca/spottedleaf/dataconverter/minecraft/MCVersions.java b/ca/spottedleaf/dataconverter/minecraft/MCVersions.java new file mode 100644 -index 0000000000000000000000000000000000000000..50429272603fae8bf5b1537127c7942588e46cb5 +index 0000000000000000000000000000000000000000..ae6457bd6575900f5eaf4b51b5d6647e1d57c28e --- /dev/null +++ b/ca/spottedleaf/dataconverter/minecraft/MCVersions.java -@@ -0,0 +1,634 @@ +@@ -0,0 +1,654 @@ +package ca.spottedleaf.dataconverter.minecraft; + +@SuppressWarnings("unused") @@ -1357,6 +1370,26 @@ index 0000000000000000000000000000000000000000..50429272603fae8bf5b1537127c79425 + public static final int V1_21_11_RC2 = 4669; + public static final int V1_21_11_RC3 = 4670; + public static final int V1_21_11 = 4671; ++ public static final int V26_1_SNAPSHOT1 = 4764; ++ public static final int V26_1_SNAPSHOT2 = 4765; ++ public static final int V26_1_SNAPSHOT3 = 4767; ++ public static final int V26_1_SNAPSHOT4 = 4768; ++ public static final int V26_1_SNAPSHOT5 = 4770; ++ public static final int V26_1_SNAPSHOT6 = 4774; ++ public static final int V26_1_SNAPSHOT7 = 4775; ++ public static final int V26_1_SNAPSHOT8 = 4776; ++ public static final int V26_1_SNAPSHOT9 = 4777; ++ public static final int V26_1_SNAPSHOT10 = 4778; ++ public static final int V26_1_SNAPSHOT11 = 4779; ++ public static final int V26_1_PRE1 = 4780; ++ public static final int V26_1_PRE2 = 4781; ++ public static final int V26_1_PRE3 = 4782; ++ public static final int V26_1_RC1 = 4783; ++ public static final int V26_1_RC2 = 4784; ++ public static final int V26_1_RC3 = 4785; ++ public static final int V26_1 = 4786; ++ public static final int V26_1RC1 = 4787; ++ public static final int V26_1_1 = 4788; + + private MCVersions() {} +} @@ -9344,10 +9377,10 @@ index 0000000000000000000000000000000000000000..e1f7c0d7fd80556941bbba8018aa85e7 +} diff --git a/ca/spottedleaf/dataconverter/minecraft/datatypes/MCTypeRegistry.java b/ca/spottedleaf/dataconverter/minecraft/datatypes/MCTypeRegistry.java new file mode 100644 -index 0000000000000000000000000000000000000000..a102b8dc03721eb7cf5512f7fdf9135fd86d4235 +index 0000000000000000000000000000000000000000..631782c6a1805db6f8a02eb4c42fdf8a7b8ce731 --- /dev/null +++ b/ca/spottedleaf/dataconverter/minecraft/datatypes/MCTypeRegistry.java -@@ -0,0 +1,391 @@ +@@ -0,0 +1,397 @@ +package ca.spottedleaf.dataconverter.minecraft.datatypes; + +import ca.spottedleaf.dataconverter.minecraft.versions.*; @@ -9359,6 +9392,7 @@ index 0000000000000000000000000000000000000000..a102b8dc03721eb7cf5512f7fdf9135f + + private static final Logger LOGGER = LogUtils.getLogger(); + ++ // Note: LEVEL and LIGHTWEIGHT_LEVEL are not maintained past versions 1.21.11, and they were never tested to begin with. + public static final MCDataType LEVEL = new MCDataType("Level"); + public static final MCDataType LIGHTWEIGHT_LEVEL = new MCDataType("LightweightLevel"); + public static final MCDataType PLAYER = new MCDataType("Player"); @@ -9735,6 +9769,11 @@ index 0000000000000000000000000000000000000000..a102b8dc03721eb7cf5512f7fdf9135f + V4657.register(); + V4658.register(); + V4661.register(); ++ // V26.1 ++ V4763.register(); ++ // V4766: We no longer pretend to "maintain" LEVEL data types. ++ V4769.register(); ++ // V4771: Same as for V4766 + } + + private MCTypeRegistry() {} @@ -15839,7 +15878,7 @@ index 0000000000000000000000000000000000000000..0fe1e7b2c8c6e197c89643da0f2cda9f +} diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V2514.java b/ca/spottedleaf/dataconverter/minecraft/versions/V2514.java new file mode 100644 -index 0000000000000000000000000000000000000000..167109fb53b45901e944ae3eb4a690956fe18e8f +index 0000000000000000000000000000000000000000..42750772c20694588015c2b82c4d1d673889da3b --- /dev/null +++ b/ca/spottedleaf/dataconverter/minecraft/versions/V2514.java @@ -0,0 +1,590 @@ @@ -16004,8 +16043,8 @@ index 0000000000000000000000000000000000000000..167109fb53b45901e944ae3eb4a69095 + + static void replaceUUIDMLTag(final MapType data, final String oldPath, final String newPath) { + final int[] uuid = createUUIDFromLongs(data.getMap(oldPath), "M", "L"); ++ data.remove(oldPath); + if (uuid != null) { -+ data.remove(oldPath); + data.setInts(newPath, uuid); + } + } @@ -18560,7 +18599,7 @@ index 0000000000000000000000000000000000000000..a133c1eaf04cbd5a6a701ee2c4ad1155 +} diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V2832.java b/ca/spottedleaf/dataconverter/minecraft/versions/V2832.java new file mode 100644 -index 0000000000000000000000000000000000000000..53f8474cdd9331d1cc293c360d3f4f14640eedd3 +index 0000000000000000000000000000000000000000..532ce6d8532311201b92d04ee97b2acf0e9c1841 --- /dev/null +++ b/ca/spottedleaf/dataconverter/minecraft/versions/V2832.java @@ -0,0 +1,917 @@ @@ -19031,7 +19070,7 @@ index 0000000000000000000000000000000000000000..53f8474cdd9331d1cc293c360d3f4f14 + predictChunkStatusBeforeSurface(level, allBlocks); + + // done with sections, update the rest of the chunk -+ updateChunkData(level, isOverworld, isAlreadyExtended.getValue(), "minecraft:noise".equals(generator), bottomSection); ++ updateChunkData(level, isOverworld, isAlreadyExtended.get().booleanValue(), "minecraft:noise".equals(generator), bottomSection); + + return null; + } @@ -27401,10 +27440,10 @@ index 0000000000000000000000000000000000000000..b9da6154f060da6f0dabbd7354fa37ea +} diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V4658.java b/ca/spottedleaf/dataconverter/minecraft/versions/V4658.java new file mode 100644 -index 0000000000000000000000000000000000000000..67fe4cff2f9f6fa45178c55e4a7ffde1af3bd61b +index 0000000000000000000000000000000000000000..27219089e85bbe922cf889b8d7b9d1733c9f485c --- /dev/null +++ b/ca/spottedleaf/dataconverter/minecraft/versions/V4658.java -@@ -0,0 +1,165 @@ +@@ -0,0 +1,166 @@ +package ca.spottedleaf.dataconverter.minecraft.versions; + +import ca.spottedleaf.dataconverter.converters.DataConverter; @@ -27479,6 +27518,7 @@ index 0000000000000000000000000000000000000000..67fe4cff2f9f6fa45178c55e4a7ffde1 + + gameRules.remove("spawnChunkRadius"); + gameRules.remove("entitiesWithPassengersCanUsePortals"); ++ gameRules.remove("gameLoopFunction"); + gameRules.remove("doFireTick"); + gameRules.remove("allowFireTicksAwayFromPlayer"); + @@ -27604,6 +27644,95 @@ index 0000000000000000000000000000000000000000..5ddc808da8a5fcc365ce6d319ce0962e + + private V4661() {} +} +diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V4763.java b/ca/spottedleaf/dataconverter/minecraft/versions/V4763.java +new file mode 100644 +index 0000000000000000000000000000000000000000..85cab14f004d0803885cb5e785a4aab9635fc0f2 +--- /dev/null ++++ b/ca/spottedleaf/dataconverter/minecraft/versions/V4763.java +@@ -0,0 +1,26 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.converters.DataConverter; ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; ++import ca.spottedleaf.dataconverter.types.MapType; ++ ++public final class V4763 { ++ ++ private static final int VERSION = MCVersions.V1_21_11 + 92; ++ ++ public static void register() { ++ final DataConverter converter = new DataConverter<>(VERSION) { ++ @Override ++ public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { ++ data.setBoolean("VillagerDataFinalized", true); ++ return null; ++ } ++ }; ++ ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:villager", converter); ++ MCTypeRegistry.ENTITY.addConverterForId("minecraft:zombie_villager", converter); ++ } ++ ++ private V4763() {} ++} +diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V4769.java b/ca/spottedleaf/dataconverter/minecraft/versions/V4769.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d016327e39c9f2c338abced2c9b6598e0beffaf8 +--- /dev/null ++++ b/ca/spottedleaf/dataconverter/minecraft/versions/V4769.java +@@ -0,0 +1,51 @@ ++package ca.spottedleaf.dataconverter.minecraft.versions; ++ ++import ca.spottedleaf.dataconverter.minecraft.MCVersions; ++import ca.spottedleaf.dataconverter.minecraft.converters.advancements.ConverterAbstractAdvancementsRename; ++import ca.spottedleaf.dataconverter.minecraft.converters.recipe.ConverterAbstractRecipeRename; ++import com.google.common.collect.ImmutableMap; ++import java.util.HashMap; ++import java.util.Map; ++ ++public final class V4769 { ++ ++ private static final int VERSION = MCVersions.V26_1_SNAPSHOT4 + 1; ++ ++ public static final Map RECIPES_UPDATES = new HashMap<>( ++ ImmutableMap.builder() ++ .put("minecraft:chiseled_stone_bricks_stone_from_stonecutting", "minecraft:chiseled_stone_bricks_from_stone_stonecutting") ++ .put("minecraft:end_stone_brick_slab_from_end_stone_brick_stonecutting", "minecraft:end_stone_brick_slab_from_end_stone_bricks_stonecutting") ++ .put("minecraft:end_stone_brick_stairs_from_end_stone_brick_stonecutting", "minecraft:end_stone_brick_stairs_from_end_stone_bricks_stonecutting") ++ .put("minecraft:end_stone_brick_wall_from_end_stone_brick_stonecutting", "minecraft:end_stone_brick_wall_from_end_stone_bricks_stonecutting") ++ .put("minecraft:mossy_stone_brick_slab_from_mossy_stone_brick_stonecutting", "minecraft:mossy_stone_brick_slab_from_mossy_stone_bricks_stonecutting") ++ .put("minecraft:mossy_stone_brick_stairs_from_mossy_stone_brick_stonecutting", "minecraft:mossy_stone_brick_stairs_from_mossy_stone_bricks_stonecutting") ++ .put("minecraft:mossy_stone_brick_wall_from_mossy_stone_brick_stonecutting", "minecraft:mossy_stone_brick_wall_from_mossy_stone_bricks_stonecutting") ++ .put("minecraft:prismarine_brick_slab_from_prismarine_stonecutting", "minecraft:prismarine_brick_slab_from_prismarine_bricks_stonecutting") ++ .put("minecraft:prismarine_brick_stairs_from_prismarine_stonecutting", "minecraft:prismarine_brick_stairs_from_prismarine_bricks_stonecutting") ++ .put("minecraft:quartz_slab_from_stonecutting", "minecraft:quartz_slab_from_quartz_block_stonecutting") ++ .put("minecraft:stone_brick_walls_from_stone_stonecutting", "minecraft:stone_brick_wall_from_stone_stonecutting") ++ .build() ++ ); ++ public static final Map ADVANCEMENT_UPDATES = new HashMap<>( ++ ImmutableMap.builder() ++ .put("minecraft:recipes/building_blocks/chiseled_stone_bricks_stone_from_stonecutting", "minecraft:recipes/building_blocks/chiseled_stone_bricks_from_stone_stonecutting") ++ .put("minecraft:recipes/building_blocks/end_stone_brick_slab_from_end_stone_brick_stonecutting", "minecraft:recipes/building_blocks/end_stone_brick_slab_from_end_stone_bricks_stonecutting") ++ .put("minecraft:recipes/building_blocks/end_stone_brick_stairs_from_end_stone_brick_stonecutting", "minecraft:recipes/building_blocks/end_stone_brick_stairs_from_end_stone_bricks_stonecutting") ++ .put("minecraft:recipes/decorations/end_stone_brick_wall_from_end_stone_brick_stonecutting", "minecraft:recipes/decorations/end_stone_brick_wall_from_end_stone_bricks_stonecutting") ++ .put("minecraft:recipes/building_blocks/mossy_stone_brick_slab_from_mossy_stone_brick_stonecutting", "minecraft:recipes/building_blocks/mossy_stone_brick_slab_from_mossy_stone_bricks_stonecutting") ++ .put("minecraft:recipes/building_blocks/mossy_stone_brick_stairs_from_mossy_stone_brick_stonecutting", "minecraft:recipes/building_blocks/mossy_stone_brick_stairs_from_mossy_stone_bricks_stonecutting") ++ .put("minecraft:recipes/building_blocks/mossy_stone_brick_wall_from_mossy_stone_brick_stonecutting", "minecraft:recipes/decorations/mossy_stone_brick_wall_from_mossy_stone_bricks_stonecutting") ++ .put("minecraft:recipes/building_blocks/prismarine_brick_slab_from_prismarine_stonecutting", "minecraft:recipes/building_blocks/prismarine_brick_slab_from_prismarine_bricks_stonecutting") ++ .put("minecraft:recipes/building_blocks/prismarine_brick_stairs_from_prismarine_stonecutting", "minecraft:recipes/building_blocks/prismarine_brick_stairs_from_prismarine_bricks_stonecutting") ++ .put("minecraft:recipes/building_blocks/quartz_slab_from_stonecutting", "minecraft:recipes/building_blocks/quartz_slab_from_quartz_block_stonecutting") ++ .put("minecraft:recipes/decorations/stone_brick_walls_from_stone_stonecutting", "minecraft:recipes/decorations/stone_brick_wall_from_stone_stonecutting") ++ .build() ++ ); ++ ++ public static void register() { ++ ConverterAbstractRecipeRename.register(VERSION, RECIPES_UPDATES::get); ++ ConverterAbstractAdvancementsRename.register(VERSION, ADVANCEMENT_UPDATES::get); ++ } ++ ++ private V4769() {} ++} diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V501.java b/ca/spottedleaf/dataconverter/minecraft/versions/V501.java new file mode 100644 index 0000000000000000000000000000000000000000..da5731090c1de479b77d0d9a1b66d217f7f4eeb5 @@ -33301,7 +33430,7 @@ index 0000000000000000000000000000000000000000..baea86388c798705418f4f1498dbf819 + } +} diff --git a/ca/spottedleaf/moonrise/paper/PaperHooks.java b/ca/spottedleaf/moonrise/paper/PaperHooks.java -index bf051b6443edd32ecdbd177e02f40965477638de..ec35985da776bac8d1fc6f7a32164ab3bed24d32 100644 +index d69358d60146e47b7f4669c43afc6c97cbde276f..4a3f07daad8b48eaf3bea02bdacbfb0670dddf90 100644 --- a/ca/spottedleaf/moonrise/paper/PaperHooks.java +++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java @@ -230,6 +230,43 @@ public final class PaperHooks extends BaseChunkSystemHooks implements PlatformHo @@ -33349,44 +33478,44 @@ index bf051b6443edd32ecdbd177e02f40965477638de..ec35985da776bac8d1fc6f7a32164ab3 type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion ).getValue(); diff --git a/net/minecraft/data/structures/StructureUpdater.java b/net/minecraft/data/structures/StructureUpdater.java -index c9bbf15b75ac4516c7d7fdfaf2390351702ca415..372030b78d25b4ff0b18575d151b0a632dd62bec 100644 +index 109d7b563ee799d998a5773c5a36e61ca3607d46..747755082639436731989d3ee4b3c30a9390d9a3 100644 --- a/net/minecraft/data/structures/StructureUpdater.java +++ b/net/minecraft/data/structures/StructureUpdater.java @@ -27,7 +27,7 @@ public class StructureUpdater implements SnbtToNbt.Filter { - LOGGER.warn("SNBT Too old, do not forget to update: {} < {}: {}", dataVersion, 4650, structureLocationPath); + LOGGER.warn("SNBT Too old, do not forget to update: {} < {}: {}", fromVersion, 4763, name); } -- CompoundTag compoundTag = DataFixTypes.STRUCTURE.updateToCurrentVersion(DataFixers.getDataFixer(), tag, dataVersion); -+ CompoundTag compoundTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.STRUCTURE, tag, dataVersion, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); // Paper - structureTemplate.load(BuiltInRegistries.BLOCK, compoundTag); +- CompoundTag updated = DataFixTypes.STRUCTURE.updateToCurrentVersion(DataFixers.getDataFixer(), tag, fromVersion); ++ CompoundTag updated = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.STRUCTURE, tag, fromVersion, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); // Paper - rewrite dataconverter system + structureTemplate.load(BuiltInRegistries.BLOCK, updated); return structureTemplate.save(new CompoundTag()); } diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 8588b380f9b5375cdb2515a01e46a5731ad3ab33..b1ee66c671362cf99425d029045fe3f9c2713bd0 100644 +index 1b064f00c33712c3d2d85ed8520f1eda02abfbf9..65a971379f35c4b1b519b93787f690d891e29523 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -383,6 +383,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { + public static S spin(final Function factory) { + ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system - AtomicReference atomicReference = new AtomicReference<>(); - Thread thread = new ca.spottedleaf.moonrise.common.util.TickThread(() -> atomicReference.get().runServer(), "Server thread"); - thread.setUncaughtExceptionHandler((thread1, exception) -> LOGGER.error("Uncaught exception in server thread", exception)); + AtomicReference serverReference = new AtomicReference<>(); + Thread thread = new ca.spottedleaf.moonrise.common.util.TickThread(() -> serverReference.get().runServer(), "Server thread"); + thread.setUncaughtExceptionHandler((t, e) -> LOGGER.error("Uncaught exception in server thread", e)); diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java -index ba620dafb862dd59fff85da0619dc66cc1c2cb20..0ad800c4f9eed88322db97c78a8b86392324bc2d 100644 +index c206c74acc80c281d06a669eb668239dc61018eb..884fa9545a93008fa92863c845aef8f18d88e25a 100644 --- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java +++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java -@@ -112,7 +112,7 @@ public class SimpleRegionStorage implements ca.spottedleaf.moonrise.patches.chun +@@ -99,7 +99,7 @@ public class SimpleRegionStorage implements ca.spottedleaf.moonrise.patches.chun } // Spigot end - injectDatafixingContext(tag, contextTag); -- tag = this.dataFixType.updateToCurrentVersion(this.fixerUpper, tag, Math.max(this.legacyFixer.get().targetDataVersion(), dataVersion)); -+ tag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(this.getDataConverterType(), tag, Math.max(this.legacyFixer.get().targetDataVersion(), dataVersion), ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); // Paper - rewrite dataconverter system + injectDatafixingContext(chunkTag, dataFixContextTag); +- chunkTag = this.dataFixType.update(this.fixerUpper, chunkTag, version, targetVersion); ++ chunkTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(this.getDataConverterType(), chunkTag, version, targetVersion); // Paper - rewrite dataconverter system // Spigot start if (stopBelowZero) { - tag.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN).toString()); -@@ -130,6 +130,20 @@ public class SimpleRegionStorage implements ca.spottedleaf.moonrise.patches.chun + chunkTag.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN).toString()); +@@ -118,6 +118,20 @@ public class SimpleRegionStorage implements ca.spottedleaf.moonrise.patches.chun } } @@ -33404,71 +33533,45 @@ index ba620dafb862dd59fff85da0619dc66cc1c2cb20..0ad800c4f9eed88322db97c78a8b8639 + } + // Paper end - rewrite data conversion system + - public CompoundTag upgradeChunkTag(CompoundTag tag, int version) { - return this.upgradeChunkTag(tag, version, null, null); // CraftBukkit + public CompoundTag upgradeChunkTag(final CompoundTag chunkTag, final int defaultVersion) { + return this.upgradeChunkTag(chunkTag, defaultVersion, null, SharedConstants.getCurrentVersion().dataVersion().version()); } -diff --git a/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java b/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java -index c8307da35b5a0907ba526878a4412f353e967642..1611aa47d47d38b9aec65de98064b7982fc514c8 100644 ---- a/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java -+++ b/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java -@@ -117,7 +117,7 @@ public class LegacyStructureDataHandler implements LegacyTagFixer { - - int dataVersion = NbtUtils.getDataVersion(tag); - if (dataVersion < 1493) { -- tag = DataFixTypes.CHUNK.update(this.dataFixer, tag, dataVersion, 1493); -+ tag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.CHUNK, tag, dataVersion, 1493); // Paper - rewrite dataconverter system - if (tag.getCompound("Level").flatMap(compoundTag -> compoundTag.getBoolean("hasLegacyStructureData")).orElse(false)) { - tag = this.updateFromLegacy(tag); - } diff --git a/net/minecraft/world/level/levelgen/structure/StructureCheck.java b/net/minecraft/world/level/levelgen/structure/StructureCheck.java -index 5198894e7a9b4fffa5ee9d9e4d8fad173c2294cd..6a395b6a654ee702e5532194c63ad1c1d7ebee3c 100644 +index 2ab055d5cdfb1e2e7cfb6f8a769c3d571acb2861..a45da98735045d55d3da14271c9bbb7a37f3151c 100644 --- a/net/minecraft/world/level/levelgen/structure/StructureCheck.java +++ b/net/minecraft/world/level/levelgen/structure/StructureCheck.java -@@ -162,7 +162,7 @@ public class StructureCheck { +@@ -161,7 +161,7 @@ public class StructureCheck { - CompoundTag compoundTag1; - try { -- compoundTag1 = DataFixTypes.CHUNK.updateToCurrentVersion(this.fixerUpper, compoundTag, dataVersion); -+ compoundTag1 = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.CHUNK, compoundTag, dataVersion, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); // Paper - replace chunk converter - } catch (Exception var12) { - LOGGER.warn("Failed to partially datafix chunk {}", chunkPos, var12); - return StructureCheckResult.CHUNK_LOAD_NEEDED; -diff --git a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplateManager.java b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplateManager.java -index fb32f95bece75ce2b6594eb7ba6dfc6643829565..c92a2e2a87dc58c9378a12d4d2e46e212889feb2 100644 ---- a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplateManager.java -+++ b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplateManager.java -@@ -245,7 +245,7 @@ public class StructureTemplateManager { - public StructureTemplate readStructure(CompoundTag tag) { + CompoundTag fixedChunkTag; + try { +- fixedChunkTag = DataFixTypes.CHUNK.updateToCurrentVersion(this.fixerUpper, chunkTag, version); ++ fixedChunkTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.CHUNK, chunkTag, version, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); // Paper - replace chunk converter + } catch (Exception var12) { + LOGGER.warn("Failed to partially datafix chunk {}", pos, var12); + return StructureCheckResult.CHUNK_LOAD_NEEDED; +diff --git a/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java b/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java +index 780a3eb08b7fb2c385c8e75e2e5f02a9d67297f4..2c7e4a6288f0c7101755de4be8f14449cdc4e01e 100644 +--- a/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java ++++ b/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java +@@ -79,7 +79,7 @@ public abstract class TemplateSource { + public StructureTemplate readStructure(final CompoundTag tag) { StructureTemplate structureTemplate = new StructureTemplate(); - int dataVersion = NbtUtils.getDataVersion(tag, 500); -- structureTemplate.load(this.blockLookup, DataFixTypes.STRUCTURE.updateToCurrentVersion(this.fixerUpper, tag, dataVersion)); -+ structureTemplate.load(this.blockLookup, ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.STRUCTURE, tag, dataVersion, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion())); // Paper - rewrite data conversion system + int version = NbtUtils.getDataVersion(tag, 500); +- structureTemplate.load(this.blockLookup, DataFixTypes.STRUCTURE.updateToCurrentVersion(this.fixerUpper, tag, version)); ++ structureTemplate.load(this.blockLookup, ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.STRUCTURE, tag, version, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion())); // Paper - rewrite dataconverter system return structureTemplate; } - -diff --git a/net/minecraft/world/level/storage/LevelStorageSource.java b/net/minecraft/world/level/storage/LevelStorageSource.java -index 2c5d0d02880c90b6bdaa3b2472327b277473130c..140630186c3b0324c248cc2a2f31d3b906557b9c 100644 ---- a/net/minecraft/world/level/storage/LevelStorageSource.java -+++ b/net/minecraft/world/level/storage/LevelStorageSource.java -@@ -224,7 +224,7 @@ public class LevelStorageSource { - CompoundTag compoundOrEmpty = levelDataTagRaw.getCompoundOrEmpty("Data"); - int dataVersion = NbtUtils.getDataVersion(compoundOrEmpty); - Dynamic dynamic = DataFixTypes.LEVEL.updateToCurrentVersion(dataFixer, new Dynamic<>(NbtOps.INSTANCE, compoundOrEmpty), dataVersion); -- dynamic = dynamic.update("Player", dynamic1 -> DataFixTypes.PLAYER.updateToCurrentVersion(dataFixer, dynamic1, dataVersion)); -+ dynamic = dynamic.update("Player", dynamic1 -> new Dynamic(dynamic1.getOps(), ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.PLAYER, (net.minecraft.nbt.CompoundTag)dynamic1.getValue(), dataVersion, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()))); // Paper - replace data conversion system - return dynamic.update("WorldGenSettings", dynamic1 -> DataFixTypes.WORLD_GEN_SETTINGS.updateToCurrentVersion(dataFixer, dynamic1, dataVersion)); - } - + } diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java -index 4ce07e194acc362da3cbcb62e5756bd48b5b3439..b8ef50bc3d07890c9da2c98d5f009a3adc52f4b0 100644 +index 41cbb27861f33ad8d7523f3b2fef9a30a69693a9..c5898bfb8b4f075b08090cba2e9f980174e0a326 100644 --- a/net/minecraft/world/level/storage/PlayerDataStorage.java +++ b/net/minecraft/world/level/storage/PlayerDataStorage.java @@ -98,7 +98,7 @@ public class PlayerDataStorage { - return optional.or(() -> this.load(nameAndId, ".dat_old")).map(compoundTag -> { - int dataVersion = NbtUtils.getDataVersion(compoundTag); -- return DataFixTypes.PLAYER.updateToCurrentVersion(this.fixerUpper, compoundTag, dataVersion); -+ return ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.PLAYER, compoundTag, dataVersion, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); + return optTag.or(() -> this.load(nameAndId, ".dat_old")).map(tag -> { + int version = NbtUtils.getDataVersion(tag); +- return DataFixTypes.PLAYER.updateToCurrentVersion(this.fixerUpper, tag, version); ++ return ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.PLAYER, tag, version, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); }); } diff --git a/paper-server/patches/features/0003-Optimize-Network-Manager-and-add-advanced-packet-sup.patch b/paper-server/patches/features/0003-Optimize-Connection-and-add-advanced-packet-support.patch similarity index 84% rename from paper-server/patches/features/0003-Optimize-Network-Manager-and-add-advanced-packet-sup.patch rename to paper-server/patches/features/0003-Optimize-Connection-and-add-advanced-packet-support.patch index bd8a743b0fdf..ee35a6002ecd 100644 --- a/paper-server/patches/features/0003-Optimize-Network-Manager-and-add-advanced-packet-sup.patch +++ b/paper-server/patches/features/0003-Optimize-Connection-and-add-advanced-packet-support.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Wed, 6 May 2020 04:53:35 -0400 -Subject: [PATCH] Optimize Network Manager and add advanced packet support +Subject: [PATCH] Optimize Connection and add advanced packet support Adds ability for 1 packet to bundle other packets to follow it Adds ability for a packet to delay sending more packets until a state is ready. @@ -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 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364ccfa390e5 100644 +index 0bc6ab5e498f82c5b3cbdf8dddfed6c8406ad511..9cd6ec17981da310be1d35054af080d6b33beff8 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java @@ -65,7 +65,7 @@ public class Connection extends SimpleChannelInboundHandler> { @@ -49,12 +49,12 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c + public boolean queueImmunity; + // Paper end - Optimize network - public Connection(PacketFlow receiving) { + public Connection(final PacketFlow receiving) { this.receiving = receiving; -@@ -395,11 +399,38 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -393,11 +397,38 @@ public class Connection extends SimpleChannelInboundHandler> { } - public void send(Packet packet, @Nullable ChannelFutureListener sendListener, boolean flush) { + public void send(final Packet packet, final @Nullable ChannelFutureListener listener, final boolean flush) { - if (this.isConnected()) { - this.flushQueue(); + // Paper start - Optimize network: Handle oversized packets better @@ -67,14 +67,14 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c + if (connected && (InnerUtil.canSendImmediate(this, packet) + || (io.papermc.paper.util.MCUtil.isMainThread() && packet.isReady() && this.pendingActions.isEmpty() + && (packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty())))) { - this.sendPacket(packet, sendListener, flush); + this.sendPacket(packet, listener, flush); } else { -- this.pendingActions.add(connection -> connection.sendPacket(packet, sendListener, flush)); +- this.pendingActions.add(connection -> connection.sendPacket(packet, listener, flush)); + // Write the packets to the queue, then flush - antixray hooks there already + final java.util.List> extraPackets = InnerUtil.buildExtraPackets(packet); + final boolean hasExtraPackets = extraPackets != null && !extraPackets.isEmpty(); + if (!hasExtraPackets) { -+ this.pendingActions.add(new PacketSendAction(packet, sendListener, flush)); ++ this.pendingActions.add(new PacketSendAction(packet, listener, flush)); + } else { + final java.util.List actions = new java.util.ArrayList<>(1 + extraPackets.size()); + actions.add(new PacketSendAction(packet, null, false)); // Delay the future listener until the end of the extra packets @@ -82,7 +82,7 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c + for (int i = 0, len = extraPackets.size(); i < len;) { + final Packet extraPacket = extraPackets.get(i); + final boolean end = ++i == len; -+ actions.add(new PacketSendAction(extraPacket, end ? sendListener : null, end)); // Append listener to the end ++ actions.add(new PacketSendAction(extraPacket, end ? listener : null, end)); // Append listener to the end + } + + this.pendingActions.addAll(actions); @@ -93,7 +93,7 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c } } -@@ -408,7 +439,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -406,7 +437,7 @@ public class Connection extends SimpleChannelInboundHandler> { this.flushQueue(); action.accept(this); } else { @@ -102,10 +102,10 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c } } -@@ -422,21 +453,41 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -420,21 +451,41 @@ public class Connection extends SimpleChannelInboundHandler> { } - private void doSendPacket(Packet packet, @Nullable ChannelFutureListener sendListener, boolean flush) { + private void doSendPacket(final Packet packet, final @Nullable ChannelFutureListener listener, final boolean flush) { + // Paper start - Optimize network + final net.minecraft.server.level.ServerPlayer player = this.getPlayer(); + if (!this.isConnected()) { @@ -113,23 +113,23 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c + return; + } + try { -+ final ChannelFuture channelFuture; ++ final ChannelFuture future; + // Paper end - Optimize network - if (sendListener != null) { -- ChannelFuture channelFuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet); -+ channelFuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet); // Paper - Optimize network - channelFuture.addListener(sendListener); + if (listener != null) { +- ChannelFuture future = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet); ++ future = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet); // Paper - Optimize network + future.addListener(listener); } else if (flush) { - this.channel.writeAndFlush(packet, this.channel.voidPromise()); -+ channelFuture = this.channel.writeAndFlush(packet, this.channel.voidPromise()); // Paper - Optimize network ++ future = this.channel.writeAndFlush(packet, this.channel.voidPromise()); // Paper - Optimize network } else { - this.channel.write(packet, this.channel.voidPromise()); -+ channelFuture = this.channel.write(packet, this.channel.voidPromise()); // Paper - Optimize network ++ future = this.channel.write(packet, this.channel.voidPromise()); // Paper - Optimize network + } + + // Paper start - Optimize network + if (packet.hasFinishListener()) { -+ channelFuture.addListener((ChannelFutureListener) future -> packet.onPacketDispatchFinish(player, future)); ++ future.addListener((ChannelFutureListener) f -> packet.onPacketDispatchFinish(player, f)); + } + } catch (final Exception e) { + LOGGER.error("NetworkException: {}", player, e); @@ -148,7 +148,7 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c } } -@@ -448,16 +499,57 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -446,16 +497,57 @@ public class Connection extends SimpleChannelInboundHandler> { } } @@ -164,9 +164,9 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c + } else if (this.isPending) { + // Should only happen during login/status stages synchronized (this.pendingActions) { -- Consumer consumer; -- while ((consumer = this.pendingActions.poll()) != null) { -- consumer.accept(this); +- Consumer pendingAction; +- while ((pendingAction = this.pendingActions.poll()) != null) { +- pendingAction.accept(this); + return this.processQueue(); + } + } @@ -211,15 +211,15 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world private static int joinAttemptsThisTick; // Paper - Buffer joins to world -@@ -527,6 +619,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -525,6 +617,7 @@ public class Connection extends SimpleChannelInboundHandler> { - public void disconnect(DisconnectionDetails disconnectionDetails) { + public void disconnect(final DisconnectionDetails details) { this.preparing = false; // Spigot + this.clearPacketQueue(); // Paper - Optimize network if (this.channel == null) { - this.delayedDisconnect = disconnectionDetails; + this.delayedDisconnect = details; } -@@ -703,7 +796,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -714,7 +807,7 @@ public class Connection extends SimpleChannelInboundHandler> { public void handleDisconnection() { if (this.channel != null && !this.channel.isOpen()) { if (this.disconnectionHandled) { @@ -228,17 +228,17 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c } else { this.disconnectionHandled = true; PacketListener packetListener = this.getPacketListener(); -@@ -714,7 +807,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -725,7 +818,7 @@ public class Connection extends SimpleChannelInboundHandler> { ); - packetListener1.onDisconnect(disconnectionDetails); + disconnectListener.onDisconnect(details); } - this.pendingActions.clear(); // Free up packet queue. + this.clearPacketQueue(); // Paper - Optimize network // Paper start - Add PlayerConnectionCloseEvent if (packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl commonPacketListener) { /* Player was logged in, either game listener or configuration listener */ -@@ -749,4 +842,96 @@ public class Connection extends SimpleChannelInboundHandler> { - public void setBandwidthLogger(LocalSampleLogger bandwidthLogger) { +@@ -760,4 +853,96 @@ public class Connection extends SimpleChannelInboundHandler> { + public void setBandwidthLogger(final LocalSampleLogger bandwidthLogger) { this.bandwidthDebugMonitor = new BandwidthDebugMonitor(bandwidthLogger); } + @@ -279,8 +279,8 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c + } + } + -+ private static boolean canSendImmediate(final Connection networkManager, final net.minecraft.network.protocol.Packet packet) { -+ return networkManager.isPending || networkManager.packetListener.protocol() != ConnectionProtocol.PLAY || ++ private static boolean canSendImmediate(final Connection connection, final net.minecraft.network.protocol.Packet packet) { ++ return connection.isPending || connection.packetListener.protocol() != ConnectionProtocol.PLAY || + packet instanceof net.minecraft.network.protocol.common.ClientboundKeepAlivePacket || + packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket || + packet instanceof net.minecraft.network.protocol.game.ClientboundSystemChatPacket || @@ -335,12 +335,12 @@ index 68b081ef1b0bf9a58564d900569bc7a5dfef2a32..8817a2d6500d1bdce96d87dc8f7f364c + // Paper end - Optimize network } diff --git a/net/minecraft/network/protocol/Packet.java b/net/minecraft/network/protocol/Packet.java -index 65ff8b9112ec76eeac48c679044fc02ae7d4ffeb..062fd531edf6dfebb42dbaeee7bbda524316d3e4 100644 +index 1480d0db90f5797e3dee19503e52d1e783493ac3..f6cfce2ce30a426cd57a89f3f8ccf144b6050363 100644 --- a/net/minecraft/network/protocol/Packet.java +++ b/net/minecraft/network/protocol/Packet.java @@ -35,4 +35,32 @@ public interface Packet { - static > StreamCodec codec(StreamMemberEncoder encoder, StreamDecoder decoder) { - return StreamCodec.ofMember(encoder, decoder); + static > StreamCodec codec(final StreamMemberEncoder writer, final StreamDecoder reader) { + return StreamCodec.ofMember(writer, reader); } + + // Paper start @@ -372,10 +372,10 @@ index 65ff8b9112ec76eeac48c679044fc02ae7d4ffeb..062fd531edf6dfebb42dbaeee7bbda52 + // Paper end } diff --git a/net/minecraft/server/network/ServerConnectionListener.java b/net/minecraft/server/network/ServerConnectionListener.java -index c080b4eccb94551eefd7016200431e37221232b5..309845b28306dfa5946e79b80e5b1dde82dd68cd 100644 +index b0445b4ad9adb6d2dec08537559d2ed243ca69bb..b266e4f7b437578356568d86910e3b428ee66523 100644 --- a/net/minecraft/server/network/ServerConnectionListener.java +++ b/net/minecraft/server/network/ServerConnectionListener.java -@@ -50,11 +50,13 @@ public class ServerConnectionListener { +@@ -52,11 +52,13 @@ public class ServerConnectionListener { // Paper start - prevent blocking on adding a new connection while the server is ticking private final java.util.Queue pending = new java.util.concurrent.ConcurrentLinkedQueue<>(); @@ -389,11 +389,11 @@ index c080b4eccb94551eefd7016200431e37221232b5..309845b28306dfa5946e79b80e5b1dde } } // Paper end - prevent blocking on adding a new connection while the server is ticking -@@ -86,6 +88,7 @@ public class ServerConnectionListener { +@@ -92,6 +94,7 @@ public class ServerConnectionListener { } catch (ChannelException var5) { } + if (!disableFlushConsolidation) channel.pipeline().addFirst(new io.netty.handler.flush.FlushConsolidationHandler()); // Paper - Optimize network - ChannelPipeline channelPipeline = channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30)); + ChannelPipeline pipeline = channel.pipeline().addLast(HandlerNames.TIMEOUT, new ReadTimeoutHandler(30)); if (ServerConnectionListener.this.server.repliesToStatus()) { - channelPipeline.addLast("legacy_query", new LegacyQueryHandler(ServerConnectionListener.this.getServer())); + pipeline.addLast(HandlerNames.LEGACY_QUERY, new LegacyQueryHandler(ServerConnectionListener.this.getServer())); diff --git a/paper-server/patches/features/0004-Allow-Saving-of-Oversized-Chunks.patch b/paper-server/patches/features/0004-Allow-Saving-of-Oversized-Chunks.patch index 7cf5f74712b2..2aea52bcb239 100644 --- a/paper-server/patches/features/0004-Allow-Saving-of-Oversized-Chunks.patch +++ b/paper-server/patches/features/0004-Allow-Saving-of-Oversized-Chunks.patch @@ -31,10 +31,10 @@ 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 99bbae1f8f681950f410b2bfe9af037164d8dcb7..944a01c34d19225d8366f6f97cbc3cacbb5d5498 100644 +index 5149635679ec03d881e63effbd79fc193d7e52c8..d628e06a8dbf11b491b323c10a512fde22e72a12 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 +@@ -69,6 +69,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche this.info = info; this.path = path; this.version = version; @@ -42,8 +42,8 @@ index 99bbae1f8f681950f410b2bfe9af037164d8dcb7..944a01c34d19225d8366f6f97cbc3cac if (!Files.isDirectory(externalFileDir)) { throw new IllegalArgumentException("Expected directory, got " + externalFileDir.toAbsolutePath()); } else { -@@ -464,4 +465,75 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche - interface CommitOp { +@@ -465,4 +466,75 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche + private interface CommitOp { void run() throws IOException; } + @@ -119,10 +119,10 @@ index 99bbae1f8f681950f410b2bfe9af037164d8dcb7..944a01c34d19225d8366f6f97cbc3cac + // Paper end } diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -index 5d9168242f7a7ca4dd888f32fecb27457fd89456..98b3f20c720d70293c6fcf77fdc081b44c71dd6b 100644 +index e9316cc08fe4cb4af28583a8cb1ec658243bffd0..aa36e241d49725eb13f00e57d020d4da24add53d 100644 --- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -@@ -237,6 +237,43 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise +@@ -241,6 +241,43 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise // Paper end - rewrite chunk system } @@ -134,7 +134,7 @@ index 5d9168242f7a7ca4dd888f32fecb27457fd89456..98b3f20c720d70293c6fcf77fdc081b4 + 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 oversizedData = regionfile.getOversizedData(chunkCoordinate.x(), chunkCoordinate.z()); + CompoundTag chunk = NbtIo.read(datainputstream); + if (oversizedData == null) { + return chunk; @@ -163,27 +163,27 @@ index 5d9168242f7a7ca4dd888f32fecb27457fd89456..98b3f20c720d70293c6fcf77fdc081b4 + } + // Paper end + - public @Nullable CompoundTag read(ChunkPos chunkPos) throws IOException { + public @Nullable CompoundTag read(final ChunkPos pos) throws IOException { // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing - RegionFile regionFile = this.getRegionFile(chunkPos, true); -@@ -244,6 +281,12 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + RegionFile region = this.getRegionFile(pos, true); +@@ -248,6 +285,12 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise return null; } // CraftBukkit end + // Paper start -+ if (regionFile.isOversized(chunkPos.x, chunkPos.z)) { -+ printOversizedLog("Loading Oversized Chunk!", regionFile.getPath(), chunkPos.x, chunkPos.z); -+ return readOversizedChunk(regionFile, chunkPos); ++ if (region.isOversized(pos.x(), pos.z())) { ++ printOversizedLog("Loading Oversized Chunk!", region.getPath(), pos.x(), pos.z()); ++ return readOversizedChunk(region, pos); + } + // Paper end CompoundTag var4; - try (DataInputStream chunkDataInputStream = regionFile.getChunkDataInputStream(chunkPos)) { -@@ -286,6 +329,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + try (DataInputStream regionChunkInputStream = region.getChunkDataInputStream(pos)) { +@@ -290,6 +333,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise } else { - try (DataOutputStream chunkDataOutputStream = regionFile.getChunkDataOutputStream(chunkPos)) { - NbtIo.write(chunkData, chunkDataOutputStream); -+ regionFile.setOversized(chunkPos.x, chunkPos.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 + 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 } } } diff --git a/paper-server/patches/features/0005-Entity-Activation-Range-2.0.patch b/paper-server/patches/features/0005-Entity-Activation-Range-2.0.patch index 6110e917e631..ea12eb15714c 100644 --- a/paper-server/patches/features/0005-Entity-Activation-Range-2.0.patch +++ b/paper-server/patches/features/0005-Entity-Activation-Range-2.0.patch @@ -242,7 +242,7 @@ index 0000000000000000000000000000000000000000..c18823746ab2edcab536cb1589b7720e + } + // special cases. + if (entity instanceof final LivingEntity living) { -+ if (living.onClimbable() || living.jumping || living.hurtTime > 0 || !living.activeEffects.isEmpty() || living.isFreezing()) { ++ if (living.blockPosition().equals(living.getLastClimbablePos().orElse(null)) || living.jumping || living.hurtTime > 0 || !living.activeEffects.isEmpty() || living.isFreezing()) { + return 1; + } + if (entity instanceof final Mob mob && mob.getTarget() != null) { @@ -354,81 +354,81 @@ index 0000000000000000000000000000000000000000..c18823746ab2edcab536cb1589b7720e + } +} diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 7656665352632fc718bea91dcfd3dd41fc436e1f..bdbc15bfbbfeae2bc5b884e300b86cb25c88840f 100644 +index 8c954add1949777b4f5658064eb7cd381d542140..ff0c463a64255af4eb417b8fa84d0ba38397d9fa 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -830,6 +830,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - profilerFiller.pop(); +@@ -857,6 +857,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + profiler.pop(); } + io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR this.entityTickList .forEach( entity -> { -@@ -1347,12 +1348,15 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1373,12 +1374,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet entity.totalEntityAge++; // Paper - age-like counter for all entities - profilerFiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString()); - profilerFiller.incrementCounter("tickNonPassenger"); + profiler.push(entity.typeHolder()::getRegisteredName); + profiler.incrementCounter("tickNonPassenger"); + final boolean isActive = io.papermc.paper.entity.activation.ActivationRange.checkIfActive(entity); // Paper - EAR 2 + if (isActive) { // Paper - EAR 2 entity.tick(); entity.postTick(); // CraftBukkit + } else {entity.inactiveTick();} // Paper - EAR 2 - profilerFiller.pop(); + profiler.pop(); - for (Entity entity1 : entity.getPassengers()) { -- this.tickPassenger(entity, entity1); -+ this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2 + for (Entity passenger : entity.getPassengers()) { +- this.tickPassenger(entity, passenger); ++ this.tickPassenger(entity, passenger, isActive); // Paper - EAR 2 } // Paper start - log detailed entity tick information } finally { -@@ -1363,7 +1367,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1389,7 +1393,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet // Paper end - log detailed entity tick information } -- private void tickPassenger(Entity ridingEntity, Entity passengerEntity) { -+ private void tickPassenger(Entity ridingEntity, Entity passengerEntity, final boolean isActive) { // Paper - EAR 2 - if (passengerEntity.isRemoved() || passengerEntity.getVehicle() != ridingEntity) { - passengerEntity.stopRiding(); - } else if (passengerEntity instanceof Player || this.entityTickList.contains(passengerEntity)) { -@@ -1373,12 +1377,21 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(passengerEntity.getType()).toString()); - profilerFiller.incrementCounter("tickPassenger"); +- private void tickPassenger(final Entity vehicle, final Entity entity) { ++ private void tickPassenger(final Entity vehicle, final Entity entity, final boolean isActive) { // Paper - EAR 2 + if (entity.isRemoved() || entity.getVehicle() != vehicle) { + entity.stopRiding(); + } else if (entity instanceof Player || this.entityTickList.contains(entity)) { +@@ -1399,12 +1403,21 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + ProfilerFiller profiler = Profiler.get(); + profiler.push(entity.typeHolder()::getRegisteredName); + profiler.incrementCounter("tickPassenger"); + // Paper start - EAR 2 + if (isActive) { - passengerEntity.rideTick(); - passengerEntity.postTick(); // CraftBukkit + entity.rideTick(); + entity.postTick(); // CraftBukkit + } else { -+ passengerEntity.setDeltaMovement(Vec3.ZERO); -+ passengerEntity.inactiveTick(); ++ entity.setDeltaMovement(Vec3.ZERO); ++ entity.inactiveTick(); + // copied from inside of if (isPassenger()) of passengerTick, but that ifPassenger is unnecessary -+ ridingEntity.positionRider(passengerEntity); ++ vehicle.positionRider(entity); + } + // Paper end - EAR 2 - profilerFiller.pop(); + profiler.pop(); - for (Entity entity : passengerEntity.getPassengers()) { -- this.tickPassenger(passengerEntity, entity); -+ this.tickPassenger(passengerEntity, entity, isActive); // Paper - EAR 2 + for (Entity passenger : entity.getPassengers()) { +- this.tickPassenger(entity, passenger); ++ this.tickPassenger(entity, passenger, isActive); // Paper - EAR 2 } } } diff --git a/net/minecraft/world/entity/AgeableMob.java b/net/minecraft/world/entity/AgeableMob.java -index d4452e28a7c65c464c97d5553985c82e59672108..4b918f07a13e7b99c25f92ae377742cf0a7e2ead 100644 +index 4f98f6e1ca266ab7377fb52e753b977d4ccd1558..a0c930afcdb01e2fb103836613417fb9fc96d86b 100644 --- a/net/minecraft/world/entity/AgeableMob.java +++ b/net/minecraft/world/entity/AgeableMob.java -@@ -129,6 +129,21 @@ public abstract class AgeableMob extends PathfinderMob { - super.onSyncedDataUpdated(key); +@@ -184,6 +184,21 @@ public abstract class AgeableMob extends PathfinderMob { + super.onSyncedDataUpdated(accessor); } + // Paper start - EAR 2 + @Override + public void inactiveTick() { + super.inactiveTick(); -+ if (!this.ageLocked && this.isAlive()) { ++ if (this.isAlive()) { + int age = this.getAge(); -+ if (age < 0) { ++ if (this.canAgeUp()) { + this.setAge(++age); + } else if (age > 0) { + this.setAge(--age); @@ -441,7 +441,7 @@ index d4452e28a7c65c464c97d5553985c82e59672108..4b918f07a13e7b99c25f92ae377742cf public void aiStep() { super.aiStep(); diff --git a/net/minecraft/world/entity/AreaEffectCloud.java b/net/minecraft/world/entity/AreaEffectCloud.java -index 5461bd9a39bb20ad29d3bc110c38860cf35a770a..b10762fd050ca03c1d31dc57e42739a1f114ca2f 100644 +index 6309a615ba2525437758b1fe39c43060ec42d6f8..677b0cadec2270537d868aac7d0acaf7e6bfa2ea 100644 --- a/net/minecraft/world/entity/AreaEffectCloud.java +++ b/net/minecraft/world/entity/AreaEffectCloud.java @@ -140,6 +140,16 @@ public class AreaEffectCloud extends Entity implements TraceableEntity { @@ -462,10 +462,10 @@ index 5461bd9a39bb20ad29d3bc110c38860cf35a770a..b10762fd050ca03c1d31dc57e42739a1 public void tick() { super.tick(); diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 34ef15867759ebc7296256a6d5ec286eb3a5f895..d3d97b0b6ff6312f1deb0bb8b1beef8b3578bc53 100644 +index fe197ff989f084b563021ac62984e4710594402e..6a76817b135d5bf1f045c3ff263c3945bdaa8c2d 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -368,6 +368,15 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -382,6 +382,15 @@ public abstract class Entity private final int despawnTime; // Paper - entity despawn time limit public int totalEntityAge; // Paper - age-like counter for all entities public final io.papermc.paper.entity.activation.ActivationType activationType = io.papermc.paper.entity.activation.ActivationType.activationTypeFor(this); // Paper - EAR 2/tracking ranges @@ -481,7 +481,7 @@ index 34ef15867759ebc7296256a6d5ec286eb3a5f895..d3d97b0b6ff6312f1deb0bb8b1beef8b // CraftBukkit end // Paper start -@@ -533,6 +542,13 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -547,6 +556,13 @@ public abstract class Entity this.position = Vec3.ZERO; this.blockPosition = BlockPos.ZERO; this.chunkPosition = ChunkPos.ZERO; @@ -492,39 +492,39 @@ index 34ef15867759ebc7296256a6d5ec286eb3a5f895..d3d97b0b6ff6312f1deb0bb8b1beef8b + this.defaultActivationState = false; + } + // Paper end - EAR 2 - SynchedEntityData.Builder builder = new SynchedEntityData.Builder(this); - builder.define(DATA_SHARED_FLAGS_ID, (byte)0); - builder.define(DATA_AIR_SUPPLY_ID, this.getMaxAirSupply()); -@@ -1115,6 +1131,10 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name - this.minorHorizontalCollision = false; + SynchedEntityData.Builder entityDataBuilder = new SynchedEntityData.Builder(this); + entityDataBuilder.define(DATA_SHARED_FLAGS_ID, (byte)0); + entityDataBuilder.define(DATA_AIR_SUPPLY_ID, this.getMaxAirSupply()); +@@ -1135,6 +1151,10 @@ public abstract class Entity } else { - if (type == MoverType.PISTON) { + if (moverType == MoverType.PISTON) { + delta = this.limitPistonMovement(delta); + // Paper start - EAR 2 + this.activatedTick = Math.max(this.activatedTick, net.minecraft.server.MinecraftServer.currentTick + 20); + this.activatedImmunityTick = Math.max(this.activatedImmunityTick, net.minecraft.server.MinecraftServer.currentTick + 20); + // Paper end - EAR 2 - movement = this.limitPistonMovement(movement); - if (movement.equals(Vec3.ZERO)) { + if (delta.equals(Vec3.ZERO)) { return; -@@ -1131,6 +1151,13 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name + } +@@ -1150,6 +1170,13 @@ public abstract class Entity this.stuckSpeedMultiplier = Vec3.ZERO; this.setDeltaMovement(Vec3.ZERO); } + // Paper start - ignore movement changes while inactive. -+ if (isTemporarilyActive && !(this instanceof ItemEntity) && movement == getDeltaMovement() && type == MoverType.SELF) { ++ if (isTemporarilyActive && !(this instanceof ItemEntity) && delta == getDeltaMovement() && moverType == MoverType.SELF) { + setDeltaMovement(Vec3.ZERO); -+ profilerFiller.pop(); ++ profiler.pop(); + return; + } + // Paper end - movement = this.maybeBackOffFromEdge(movement, type); - Vec3 vec3 = this.collide(movement); + 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 5b6020de0848458f5576113e690b5a6435f35d05..6f49b5e5888a6296b929e465a5ef87dc49bd4516 100644 +index 9d579b7f17dc38c9e92447ea27ec71a8967a9964..30d518454e7a48b42242184203ab583b19b3e770 100644 --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java -@@ -3330,6 +3330,14 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin +@@ -3385,6 +3385,14 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin protected void playAttackSound() { } @@ -540,10 +540,10 @@ index 5b6020de0848458f5576113e690b5a6435f35d05..6f49b5e5888a6296b929e465a5ef87dc public void tick() { super.tick(); diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java -index 95c99f266f259042d5946e891f9bab04d2fdba2a..92175cc019750e829fcad7691a937024c2649882 100644 +index c7c53ef16e104ebc48aeb8719783fb4c45d39fc2..90eaa775dc134041647618c3b965334a9bb9fd3b 100644 --- a/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java -@@ -217,6 +217,19 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab +@@ -211,6 +211,19 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs return this.lookControl; } @@ -561,10 +561,10 @@ index 95c99f266f259042d5946e891f9bab04d2fdba2a..92175cc019750e829fcad7691a937024 + // Paper end + public MoveControl getMoveControl() { - return this.getControlledVehicle() instanceof Mob mob ? mob.getMoveControl() : this.moveControl; + return this.getControlledVehicle() instanceof Mob riding ? riding.getMoveControl() : this.moveControl; } diff --git a/net/minecraft/world/entity/PathfinderMob.java b/net/minecraft/world/entity/PathfinderMob.java -index 71b0d444884848c4299cc4ef4f92f80f9b5fd516..ca585508c2405a0dfc2c60cfd025117689045e28 100644 +index c4b4a135f61955c2b87801772a2b31eeeb77bf45..9d12cc2d169f243ba23485e2782bca7064282a36 100644 --- a/net/minecraft/world/entity/PathfinderMob.java +++ b/net/minecraft/world/entity/PathfinderMob.java @@ -17,6 +17,8 @@ public abstract class PathfinderMob extends Mob { @@ -573,11 +573,11 @@ index 71b0d444884848c4299cc4ef4f92f80f9b5fd516..ca585508c2405a0dfc2c60cfd0251176 + public BlockPos movingTarget; public BlockPos getMovingTarget() { return movingTarget; } // Paper + - public float getWalkTargetValue(BlockPos pos) { + public float getWalkTargetValue(final BlockPos pos) { return this.getWalkTargetValue(pos, this.level()); } diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java -index d552e47aa38c61a8f78ed114a3433603c9658a24..674966c580220a4e0c83a628c763aaea8bfd0b1c 100644 +index 6bc2d38fb990628784deb5ee615e34a0fc320a47..844ced7a271ffb557f3c8f2fcd6a4c6ad499aeae 100644 --- a/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java @@ -24,6 +24,7 @@ public class GoalSelector { @@ -586,10 +586,10 @@ index d552e47aa38c61a8f78ed114a3433603c9658a24..674966c580220a4e0c83a628c763aaea private final EnumSet disabledFlags = EnumSet.noneOf(Goal.Flag.class); + private int curRate; // Paper - EAR 2 - public void addGoal(int priority, Goal goal) { - this.availableGoals.add(new WrappedGoal(priority, goal)); + public void addGoal(final int prio, final Goal goal) { + this.availableGoals.add(new WrappedGoal(prio, goal)); @@ -33,6 +34,22 @@ public class GoalSelector { - this.availableGoals.removeIf(wrappedGoal -> filter.test(wrappedGoal.getGoal())); + this.availableGoals.removeIf(goal -> predicate.test(goal.getGoal())); } + // Paper start - EAR 2 @@ -599,7 +599,7 @@ index d552e47aa38c61a8f78ed114a3433603c9658a24..674966c580220a4e0c83a628c763aaea + } + + public boolean hasTasks() { -+ for (WrappedGoal task : this.availableGoals) { ++ for (final WrappedGoal task : this.availableGoals) { + if (task.isRunning()) { + return true; + } @@ -608,15 +608,15 @@ index d552e47aa38c61a8f78ed114a3433603c9658a24..674966c580220a4e0c83a628c763aaea + } + // Paper end - EAR 2 + - public void removeGoal(Goal goal) { - for (WrappedGoal wrappedGoal : this.availableGoals) { - if (wrappedGoal.getGoal() == goal && wrappedGoal.isRunning()) { + public void removeGoal(final Goal toRemove) { + for (WrappedGoal availableGoal : this.availableGoals) { + if (availableGoal.getGoal() == toRemove && availableGoal.isRunning()) { diff --git a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -index 3f780276be6766ef253c50212e06fd93a96b0caa..7e70c7bee633c54497d1cd2854dd60f4fb5ff160 100644 +index d46e0e3a4a7fc3bb3f1dd695d4cc563778b99db0..c6e9b155c2341c13d569f28f54f0c1d2d7313676 100644 --- a/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java @@ -23,6 +23,14 @@ public abstract class MoveToBlockGoal extends Goal { - public MoveToBlockGoal(PathfinderMob mob, double speedModifier, int searchRange) { + public MoveToBlockGoal(final PathfinderMob mob, final double speedModifier, final int searchRange) { this(mob, speedModifier, searchRange, 1); } + // Paper start - activation range improvements @@ -628,21 +628,21 @@ index 3f780276be6766ef253c50212e06fd93a96b0caa..7e70c7bee633c54497d1cd2854dd60f4 + } + // Paper end - public MoveToBlockGoal(PathfinderMob mob, double speedModifier, int searchRange, int verticalSearchRange) { + public MoveToBlockGoal(final PathfinderMob mob, final double speedModifier, final int searchRange, final int verticalSearchRange) { this.mob = mob; @@ -113,6 +121,7 @@ public abstract class MoveToBlockGoal extends Goal { - mutableBlockPos.setWithOffset(blockPos, i4, i2 - 1, i5); - if (this.mob.isWithinHome(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) { - this.blockPos = mutableBlockPos; -+ this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : mutableBlockPos.immutable(); // Paper + pos.setWithOffset(mobPos, x, y - 1, z); + if (this.mob.isWithinHome(pos) && this.isValidTarget(this.mob.level(), pos)) { + this.blockPos = pos; ++ this.mob.movingTarget = pos == BlockPos.ZERO ? null : pos.immutable(); // Paper return true; } } diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java -index 768adc0205a2ec5221045c9c2ca0fdeec9f734b6..e82bb8f2d2b0e771dc371fd7e709116e1f5f204f 100644 +index a4f719c1b1b6a8920068ed8969a9e780420eade1..3791ffa6a14a1b2304780382e19f0e38a59f573f 100644 --- a/net/minecraft/world/entity/item/ItemEntity.java +++ b/net/minecraft/world/entity/item/ItemEntity.java -@@ -117,6 +117,29 @@ public class ItemEntity extends Entity implements TraceableEntity { +@@ -124,6 +124,29 @@ public class ItemEntity extends Entity implements TraceableEntity { return 0.04; } @@ -673,10 +673,10 @@ index 768adc0205a2ec5221045c9c2ca0fdeec9f734b6..e82bb8f2d2b0e771dc371fd7e709116e public void tick() { if (this.getItem().isEmpty()) { diff --git a/net/minecraft/world/entity/npc/villager/Villager.java b/net/minecraft/world/entity/npc/villager/Villager.java -index 10a66bb3c093a1d31f3c985b418acb9232cc882c..89844d7e804cc8a2110b694e448bc5993991bea7 100644 +index a80e6a23cf29cb44943339101f15565dbc19af1f..f481fd661440f77c98940cbde4a8b95818e4a22c 100644 --- a/net/minecraft/world/entity/npc/villager/Villager.java +++ b/net/minecraft/world/entity/npc/villager/Villager.java -@@ -269,11 +269,35 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -244,11 +244,35 @@ public class Villager extends AbstractVillager implements VillagerDataHolder, Re return this.assignProfessionWhenSpawned; } @@ -700,29 +700,29 @@ index 10a66bb3c093a1d31f3c985b418acb9232cc882c..89844d7e804cc8a2110b694e448bc599 + // Paper end - EAR 2 + @Override - protected void customServerAiStep(ServerLevel level) { + protected void customServerAiStep(final ServerLevel level) { + // Paper start - EAR 2 + this.customServerAiStep(level, false); + } + protected void customServerAiStep(ServerLevel level, final boolean inactive) { + // Paper end - EAR 2 - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("villagerBrain"); + ProfilerFiller profiler = Profiler.get(); + profiler.push("villagerBrain"); - this.getBrain().tick(level, this); + if (!inactive) this.getBrain().tick(level, this); // Paper - EAR 2 - profilerFiller.pop(); + profiler.pop(); if (this.assignProfessionWhenSpawned) { this.assignProfessionWhenSpawned = false; -@@ -297,7 +321,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -272,7 +296,7 @@ public class Villager extends AbstractVillager implements VillagerDataHolder, Re this.lastTradedPlayer = null; } - if (!this.isNoAi() && this.random.nextInt(100) == 0) { + if (!inactive && !this.isNoAi() && this.random.nextInt(100) == 0) { // Paper - EAR 2 - Raid raidAt = level.getRaidAt(this.blockPosition()); - if (raidAt != null && raidAt.isActive() && !raidAt.isOver()) { + Raid raid = level.getRaidAt(this.blockPosition()); + if (raid != null && raid.isActive() && !raid.isOver()) { level.broadcastEntityEvent(this, EntityEvent.VILLAGER_SWEAT); -@@ -308,6 +332,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -283,6 +307,7 @@ public class Villager extends AbstractVillager implements VillagerDataHolder, Re this.stopTrading(); } @@ -731,11 +731,11 @@ index 10a66bb3c093a1d31f3c985b418acb9232cc882c..89844d7e804cc8a2110b694e448bc599 } diff --git a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java -index 3ba7d5bcaa76442adbd681a15175ba178297e704..1b633d2eb77704fd9a6358f09ed368fab2a5b212 100644 +index c457a6b0935fa4dce20f93db79c27070ce147705..c9f7e68abccbc6b894a66b675952c973e22af8e6 100644 --- a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java +++ b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java -@@ -107,6 +107,21 @@ public class FireworkRocketEntity extends Projectile implements ItemSupplier { - return super.shouldRender(x, y, z) && !this.isAttachedToEntity(); +@@ -111,6 +111,21 @@ public class FireworkRocketEntity extends Projectile implements ItemSupplier { + return super.shouldRender(camX, camY, camZ) && !this.isAttachedToEntity(); } + // Paper start - EAR 2 @@ -757,11 +757,11 @@ index 3ba7d5bcaa76442adbd681a15175ba178297e704..1b633d2eb77704fd9a6358f09ed368fa public void tick() { super.tick(); diff --git a/net/minecraft/world/entity/projectile/arrow/Arrow.java b/net/minecraft/world/entity/projectile/arrow/Arrow.java -index acef4aee02169a155fb2cc5195c33fb5fe29fa4d..bbbdf0048db1e806642ad96cce66818e524acc62 100644 +index 23a9a11f8b9397c91ac610d858b7c1c3810095c8..d8c84e718b9c9d142925e2569afbd72e462bf5ca 100644 --- a/net/minecraft/world/entity/projectile/arrow/Arrow.java +++ b/net/minecraft/world/entity/projectile/arrow/Arrow.java @@ -70,6 +70,16 @@ public class Arrow extends AbstractArrow { - builder.define(ID_EFFECT_COLOR, -1); + entityData.define(ID_EFFECT_COLOR, -1); } + // Paper start - EAR 2 @@ -778,12 +778,12 @@ index acef4aee02169a155fb2cc5195c33fb5fe29fa4d..bbbdf0048db1e806642ad96cce66818e public void tick() { super.tick(); diff --git a/net/minecraft/world/entity/vehicle/minecart/MinecartHopper.java b/net/minecraft/world/entity/vehicle/minecart/MinecartHopper.java -index 4a6384a668546371f4ffa13927be4bd462d47c60..3c961b76769f16160caedce8ec32bb2a2561163f 100644 +index 3a590d4dc980a2912e9cc043d8c8db4cf9d60803..06ab7c48b18c03af494ab10fc2b584cefc776749 100644 --- a/net/minecraft/world/entity/vehicle/minecart/MinecartHopper.java +++ b/net/minecraft/world/entity/vehicle/minecart/MinecartHopper.java @@ -50,6 +50,7 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper - if (flag != this.isEnabled()) { - this.setEnabled(flag); + if (newEnabled != this.isEnabled()) { + this.setEnabled(newEnabled); } + this.immunize(); // Paper } @@ -796,16 +796,16 @@ index 4a6384a668546371f4ffa13927be4bd462d47c60..3c961b76769f16160caedce8ec32bb2a + this.immunize(); // Paper return true; } else { - for (ItemEntity itemEntity : this.level() + for (ItemEntity entity : this.level() .getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.25, 0.0, 0.25), EntitySelector.ENTITY_STILL_ALIVE)) { - if (HopperBlockEntity.addItem(this, itemEntity)) { + if (HopperBlockEntity.addItem(this, entity)) { + this.immunize(); // Paper return true; } } @@ -142,4 +145,11 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper - public AbstractContainerMenu createMenu(int id, Inventory playerInventory) { - return new HopperMenu(id, playerInventory, this); + public AbstractContainerMenu createMenu(final int containerId, final Inventory inventory) { + return new HopperMenu(containerId, inventory, this); } + + // Paper start @@ -816,10 +816,10 @@ index 4a6384a668546371f4ffa13927be4bd462d47c60..3c961b76769f16160caedce8ec32bb2a + } diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java -index 49aca568407523d95d5801988fe153d71c9dc768..9e88d83b31129cc853afe60d841838852a33fc45 100644 +index 86b37a276b33a10efc950243787e85a657b93138..9a56fc04ad397d962b56e2a3a8fedb8eddeeb89e 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java -@@ -149,6 +149,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -154,6 +154,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl @Nullable public List captureDrops; public final it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap ticksPerSpawnCategory = new it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<>(); @@ -833,13 +833,13 @@ index 49aca568407523d95d5801988fe153d71c9dc768..9e88d83b31129cc853afe60d84183885 public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot // Paper start - add paper world config diff --git a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java -index 9f50e86a637aab8b5c34913e226807990c1891a4..bc42a6c678987a39b0a70a1a3e96eb90340ffea5 100644 +index ec01093094f1d4de2708791ae6a5545268a021ae..f904c587fafe7395473450f766354d874e36ebac 100644 --- a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java +++ b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java -@@ -152,6 +152,10 @@ public class PistonMovingBlockEntity extends BlockEntity { +@@ -159,6 +159,10 @@ public class PistonMovingBlockEntity extends BlockEntity { } - entity.setDeltaMovement(d1, d2, d3); + entity.setDeltaMovement(dx, dy, dz); + // Paper - EAR items stuck in slime pushed by a piston + entity.activatedTick = Math.max(entity.activatedTick, net.minecraft.server.MinecraftServer.currentTick + 10); + entity.activatedImmunityTick = Math.max(entity.activatedImmunityTick, net.minecraft.server.MinecraftServer.currentTick + 10); diff --git a/paper-server/patches/features/0006-Optimize-Voxel-Shape-Merging.patch b/paper-server/patches/features/0006-Optimize-Voxel-Shape-Merging.patch new file mode 100644 index 000000000000..29e36520b431 --- /dev/null +++ b/paper-server/patches/features/0006-Optimize-Voxel-Shape-Merging.patch @@ -0,0 +1,126 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 3 May 2020 22:35:09 -0400 +Subject: [PATCH] Optimize Voxel Shape Merging + +This method shows up as super hot in profiler, and also a high "self" time. + +Upon analyzing, it appears most usages of this method fall down to the final +else statement of the nasty ternary. + +Upon even further analyzation, it appears then the majority of those have a +consistent list 1.... One with Infinity head and Tails. + +First optimization is to detect these infinite states and immediately return that +IndirectMerger so we can avoid testing the rest for most cases. + +Break the method into 2 to help the JVM promote inlining of this fast path. + +Then it was also noticed that IndirectMerger constructor is also a hotspot +with a high self time... + +Well, knowing that in most cases our list 1 is actualy the same value, it allows +us to know that with an infinite list1, the result on the merger is essentially +list2 as the final values. + +This let us analyze the 2 potential states (Infinite with 2 sources or 4 sources) +and compute a deterministic result for the Merger values. + +Additionally, this lets us avoid even allocating new objects for this too, further +reducing memory usage. + +diff --git a/net/minecraft/world/phys/shapes/IndirectMerger.java b/net/minecraft/world/phys/shapes/IndirectMerger.java +index 7e525297aaca8c18cf1669ab80040475029ff652..b342e4c0260fc5a64e5943e6342de0703233a99b 100644 +--- a/net/minecraft/world/phys/shapes/IndirectMerger.java ++++ b/net/minecraft/world/phys/shapes/IndirectMerger.java +@@ -10,12 +10,32 @@ public class IndirectMerger implements IndexMerger { + private final int[] firstIndices; + private final int[] secondIndices; + private final int resultLength; ++ // Paper start ++ private static final int[] INFINITE_B_1 = {1, 1}; ++ private static final int[] INFINITE_B_0 = {0, 0}; ++ private static final int[] INFINITE_C = {0, 1}; ++ // Paper end + + public IndirectMerger(final DoubleList first, final DoubleList second, final boolean firstOnlyMatters, final boolean secondOnlyMatters) { + double lastValue = Double.NaN; + int firstSize = first.size(); + int secondSize = second.size(); + int capacity = firstSize + secondSize; ++ // Paper start - optimize common path of infinity doublelist ++ double tail = first.getDouble(firstSize - 1); ++ double head = first.getDouble(0); ++ if (head == Double.NEGATIVE_INFINITY && tail == Double.POSITIVE_INFINITY && !firstOnlyMatters && !secondOnlyMatters && (firstSize == 2 || firstSize == 4)) { ++ this.result = second.toDoubleArray(); ++ this.resultLength = second.size(); ++ if (firstSize == 2) { ++ this.firstIndices = INFINITE_B_0; ++ } else { ++ this.firstIndices = INFINITE_B_1; ++ } ++ this.secondIndices = INFINITE_C; ++ return; ++ } ++ // Paper end + this.result = new double[capacity]; + this.firstIndices = new int[capacity]; + this.secondIndices = new int[capacity]; +diff --git a/net/minecraft/world/phys/shapes/Shapes.java b/net/minecraft/world/phys/shapes/Shapes.java +index de10ce3faa86d6ed6d528575a083722a8feedf8d..7574443bc098c1a0958b64f1f54e76322e48a2e7 100644 +--- a/net/minecraft/world/phys/shapes/Shapes.java ++++ b/net/minecraft/world/phys/shapes/Shapes.java +@@ -343,11 +343,26 @@ public final class Shapes { + } + + @VisibleForTesting +- protected static IndexMerger createIndexMerger( ++ private static IndexMerger createIndexMerger( // Paper - private + final int cost, final DoubleList first, final DoubleList second, final boolean firstOnlyMatters, final boolean secondOnlyMatters + ) { ++ // Paper start - fast track the most common scenario ++ // first is usually a DoubleArrayList with Infinite head/tails that falls to the final else clause ++ // This is actually the most common path, so jump to it straight away ++ if (first.getDouble(0) == Double.NEGATIVE_INFINITY && first.getDouble(first.size() - 1) == Double.POSITIVE_INFINITY) { ++ return new IndirectMerger(first, second, firstOnlyMatters, secondOnlyMatters); ++ } ++ // Split out rest to hopefully inline the above ++ return lessCommonMerge(cost, first, second, firstOnlyMatters, secondOnlyMatters); ++ } ++ ++ private static IndexMerger lessCommonMerge( ++ final int cost, final DoubleList first, final DoubleList second, final boolean firstOnlyMatters, final boolean secondOnlyMatters ++ ) { ++ // Paper end - fast track the most common scenario + int firstSize = first.size() - 1; + int secondSize = second.size() - 1; ++ // Paper note - Rewrite below as optimized order if instead of nasty ternary + if (first instanceof CubePointRange && second instanceof CubePointRange) { + long size = lcm(firstSize, secondSize); + if (cost * size <= 256L) { +@@ -355,14 +370,21 @@ public final class Shapes { + } + } + +- if (first.getDouble(firstSize) < second.getDouble(0) - 1.0E-7) { ++ // Paper start - Identical happens more often than Disjoint ++ if (firstSize == secondSize && Objects.equals(first, second)) { ++ if (first instanceof IdenticalMerger firstMerger) { ++ return firstMerger; ++ } else if (second instanceof IdenticalMerger secondMerger) { ++ return secondMerger; ++ } ++ return new IdenticalMerger(first); ++ } else if (first.getDouble(firstSize) < second.getDouble(0) - 1.0E-7) { ++ // Paper end - Identical happens more often than Disjoint + return new NonOverlappingMerger(first, second, false); + } else if (second.getDouble(secondSize) < first.getDouble(0) - 1.0E-7) { + return new NonOverlappingMerger(second, first, true); + } else { +- return (IndexMerger)(firstSize == secondSize && Objects.equals(first, second) +- ? new IdenticalMerger(first) +- : new IndirectMerger(first, second, firstOnlyMatters, secondOnlyMatters)); ++ return new IndirectMerger(first, second, firstOnlyMatters, secondOnlyMatters); // Paper - Identical happens more often than Disjoint + } + } + diff --git a/paper-server/patches/features/0007-optimize-dirt-and-snow-spreading.patch b/paper-server/patches/features/0007-optimize-dirt-and-snow-spreading.patch new file mode 100644 index 000000000000..c09fba8774d1 --- /dev/null +++ b/paper-server/patches/features/0007-optimize-dirt-and-snow-spreading.patch @@ -0,0 +1,80 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: lukas81298 +Date: Fri, 22 Jan 2021 21:50:18 +0100 +Subject: [PATCH] optimize dirt and snow spreading + + +diff --git a/net/minecraft/world/level/block/SpreadingSnowyBlock.java b/net/minecraft/world/level/block/SpreadingSnowyBlock.java +index 6695d7f76daf45ed3e73c4984326b8b59e5a76cc..f868b1ccfa0e373c47344d000f26fbe8507c3510 100644 +--- a/net/minecraft/world/level/block/SpreadingSnowyBlock.java ++++ b/net/minecraft/world/level/block/SpreadingSnowyBlock.java +@@ -27,8 +27,13 @@ public abstract class SpreadingSnowyBlock extends SnowyBlock { + protected abstract MapCodec codec(); + + private static boolean canStayAlive(final BlockState state, final LevelReader level, final BlockPos pos) { ++ // Paper start - Perf: optimize dirt and snow spreading ++ return canStayAlive(level.getChunk(pos), state, pos); ++ } ++ private static boolean canStayAlive(final net.minecraft.world.level.chunk.ChunkAccess chunk, final BlockState state, final BlockPos pos) { ++ // Paper end - Perf: optimize dirt and snow spreading + BlockPos above = pos.above(); +- BlockState aboveState = level.getBlockState(above); ++ BlockState aboveState = chunk.getBlockState(above); // Paper - Perf: optimize dirt and snow spreading + if (aboveState.is(Blocks.SNOW) && aboveState.getValue(SnowLayerBlock.LAYERS) == 1) { + return true; + } else if (aboveState.getFluidState().isFull()) { +@@ -40,8 +45,14 @@ public abstract class SpreadingSnowyBlock extends SnowyBlock { + } + + private static boolean canPropagate(final BlockState state, final LevelReader level, final BlockPos pos) { ++ // Paper start - Perf: optimize dirt and snow spreading ++ return canPropagate(level.getChunk(pos), state, pos); ++ } ++ ++ private static boolean canPropagate(final net.minecraft.world.level.chunk.ChunkAccess chunk, final BlockState state, final BlockPos pos) { ++ // Paper end - Perf: optimize dirt and snow spreading + BlockPos above = pos.above(); +- return canStayAlive(state, level, pos) && !level.getFluidState(above).is(FluidTags.WATER); ++ return canStayAlive(chunk, state, pos) && !chunk.getFluidState(above).is(FluidTags.WATER); // Paper - Perf: optimize dirt and snow spreading + } + + @Override +@@ -50,7 +61,14 @@ public abstract class SpreadingSnowyBlock extends SnowyBlock { + Registry blocks = level.registryAccess().lookupOrThrow(Registries.BLOCK); + Optional baseBlock = blocks.getOptional(this.baseBlock); + if (!baseBlock.isEmpty()) { +- if (!canStayAlive(state, level, pos)) { ++ // Paper start - Perf: optimize dirt and snow spreading ++ final net.minecraft.world.level.chunk.ChunkAccess cachedChunk = level.getChunkIfLoaded(pos); ++ if (cachedChunk == null) { // Is this needed? ++ return; ++ } ++ ++ if (!canStayAlive(cachedChunk, state, pos)) { ++ // Paper end - Perf: optimize dirt and snow spreading + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, baseBlock.get().defaultBlockState()).isCancelled()) { + return; +@@ -63,8 +81,20 @@ public abstract class SpreadingSnowyBlock extends SnowyBlock { + + for (int i = 0; i < 4; i++) { + BlockPos testPos = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1); +- if (level.getBlockState(testPos).is(baseBlock.get()) && canPropagate(defaultBlockState, level, testPos)) { +- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, testPos, defaultBlockState.setValue(SNOWY, isSnowySetting(level.getBlockState(testPos.above()))), Block.UPDATE_ALL); // CraftBukkit ++ // Paper start - Perf: optimize dirt and snow spreading ++ if (pos.getX() == testPos.getX() && pos.getY() == testPos.getY() && pos.getZ() == testPos.getZ()) { ++ continue; ++ } ++ ++ final net.minecraft.world.level.chunk.ChunkAccess access; ++ if (cachedChunk.locX == testPos.getX() >> 4 && cachedChunk.locZ == testPos.getZ() >> 4) { ++ access = cachedChunk; ++ } else { ++ access = level.getChunkAt(testPos); ++ } ++ if (access.getBlockState(testPos).is(baseBlock.get()) && SpreadingSnowyBlock.canPropagate(access, defaultBlockState, testPos)) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, testPos, defaultBlockState.setValue(SNOWY, isSnowySetting(access.getBlockState(testPos.above()))), Block.UPDATE_ALL); // CraftBukkit ++ // Paper end - Perf: optimize dirt and snow spreading + } + } + } diff --git a/paper-server/patches/features/0008-Optimize-Voxel-Shape-Merging.patch b/paper-server/patches/features/0008-Optimize-Voxel-Shape-Merging.patch deleted file mode 100644 index bafbe6886916..000000000000 --- a/paper-server/patches/features/0008-Optimize-Voxel-Shape-Merging.patch +++ /dev/null @@ -1,122 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Sun, 3 May 2020 22:35:09 -0400 -Subject: [PATCH] Optimize Voxel Shape Merging - -This method shows up as super hot in profiler, and also a high "self" time. - -Upon analyzing, it appears most usages of this method fall down to the final -else statement of the nasty ternary. - -Upon even further analyzation, it appears then the majority of those have a -consistent list 1.... One with Infinity head and Tails. - -First optimization is to detect these infinite states and immediately return that -VoxelShapeMergerList so we can avoid testing the rest for most cases. - -Break the method into 2 to help the JVM promote inlining of this fast path. - -Then it was also noticed that VoxelShapeMergerList constructor is also a hotspot -with a high self time... - -Well, knowing that in most cases our list 1 is actualy the same value, it allows -us to know that with an infinite list1, the result on the merger is essentially -list2 as the final values. - -This let us analyze the 2 potential states (Infinite with 2 sources or 4 sources) -and compute a deterministic result for the MergerList values. - -Additionally, this lets us avoid even allocating new objects for this too, further -reducing memory usage. - -diff --git a/net/minecraft/world/phys/shapes/IndirectMerger.java b/net/minecraft/world/phys/shapes/IndirectMerger.java -index 60b56a5086b8aad0fad693f686b89138b3a4c80d..5b079ec43c259368d0eed4e8385880712abda4f7 100644 ---- a/net/minecraft/world/phys/shapes/IndirectMerger.java -+++ b/net/minecraft/world/phys/shapes/IndirectMerger.java -@@ -10,12 +10,32 @@ public class IndirectMerger implements IndexMerger { - private final int[] firstIndices; - private final int[] secondIndices; - private final int resultLength; -+ // Paper start -+ private static final int[] INFINITE_B_1 = {1, 1}; -+ private static final int[] INFINITE_B_0 = {0, 0}; -+ private static final int[] INFINITE_C = {0, 1}; -+ // Paper end - - public IndirectMerger(DoubleList lower, DoubleList upper, boolean excludeUpper, boolean excludeLower) { - double d = Double.NaN; - int size = lower.size(); - int size1 = upper.size(); - int i = size + size1; -+ // Paper start - optimize common path of infinity doublelist -+ double tail = lower.getDouble(size - 1); -+ double head = lower.getDouble(0); -+ if (head == Double.NEGATIVE_INFINITY && tail == Double.POSITIVE_INFINITY && !excludeUpper && !excludeLower && (size == 2 || size == 4)) { -+ this.result = upper.toDoubleArray(); -+ this.resultLength = upper.size(); -+ if (size == 2) { -+ this.firstIndices = INFINITE_B_0; -+ } else { -+ this.firstIndices = INFINITE_B_1; -+ } -+ this.secondIndices = INFINITE_C; -+ return; -+ } -+ // Paper end - this.result = new double[i]; - this.firstIndices = new int[i]; - this.secondIndices = new int[i]; -diff --git a/net/minecraft/world/phys/shapes/Shapes.java b/net/minecraft/world/phys/shapes/Shapes.java -index ac4bb789df88c07ea38d20700dccb6571e3ea58d..fc2b393d24fef8ce3a74d38d0937bfe5604eae87 100644 ---- a/net/minecraft/world/phys/shapes/Shapes.java -+++ b/net/minecraft/world/phys/shapes/Shapes.java -@@ -346,9 +346,22 @@ public final class Shapes { - } - - @VisibleForTesting -- protected static IndexMerger createIndexMerger(int size, DoubleList list1, DoubleList list2, boolean excludeUpper, boolean excludeLower) { -+ private static IndexMerger createIndexMerger(int size, DoubleList list1, DoubleList list2, boolean excludeUpper, boolean excludeLower) { // Paper - private -+ // Paper start - fast track the most common scenario -+ // doublelist is usually a DoubleArrayList with Infinite head/tails that falls to the final else clause -+ // This is actually the most common path, so jump to it straight away -+ if (list1.getDouble(0) == Double.NEGATIVE_INFINITY && list1.getDouble(list1.size() - 1) == Double.POSITIVE_INFINITY) { -+ return new IndirectMerger(list1, list2, excludeUpper, excludeLower); -+ } -+ // Split out rest to hopefully inline the above -+ return lessCommonMerge(size, list1, list2, excludeUpper, excludeLower); -+ } -+ -+ private static IndexMerger lessCommonMerge(int size, DoubleList list1, DoubleList list2, boolean excludeUpper, boolean excludeLower) { -+ // Paper end - fast track the most common scenario - int i = list1.size() - 1; - int i1 = list2.size() - 1; -+ // Paper note - Rewrite below as optimized order if instead of nasty ternary - if (list1 instanceof CubePointRange && list2 instanceof CubePointRange) { - long l = lcm(i, i1); - if (size * l <= 256L) { -@@ -356,14 +369,21 @@ public final class Shapes { - } - } - -- if (list1.getDouble(i) < list2.getDouble(0) - 1.0E-7) { -+ // Paper start - Identical happens more often than Disjoint -+ if (i == i1 && Objects.equals(list1, list2)) { -+ if (list1 instanceof IdenticalMerger) { -+ return (IndexMerger) list1; -+ } else if (list2 instanceof IdenticalMerger) { -+ return (IndexMerger) list2; -+ } -+ return new IdenticalMerger(list1); -+ } else if (list1.getDouble(i) < list2.getDouble(0) - 1.0E-7) { -+ // Paper end - Identical happens more often than Disjoint - return new NonOverlappingMerger(list1, list2, false); - } else if (list2.getDouble(i1) < list1.getDouble(0) - 1.0E-7) { - return new NonOverlappingMerger(list2, list1, true); - } else { -- return (IndexMerger)(i == i1 && Objects.equals(list1, list2) -- ? new IdenticalMerger(list1) -- : new IndirectMerger(list1, list2, excludeUpper, excludeLower)); -+ return new IndirectMerger(list1, list2, excludeUpper, excludeLower); // Paper - Identical happens more often than Disjoint - } - } - diff --git a/paper-server/patches/features/0006-Use-Velocity-compression-and-cipher-natives.patch b/paper-server/patches/features/0008-Use-Velocity-compression-and-cipher-natives.patch similarity index 61% rename from paper-server/patches/features/0006-Use-Velocity-compression-and-cipher-natives.patch rename to paper-server/patches/features/0008-Use-Velocity-compression-and-cipher-natives.patch index 86b04d15d7f5..9ddeaa454cc2 100644 --- a/paper-server/patches/features/0006-Use-Velocity-compression-and-cipher-natives.patch +++ b/paper-server/patches/features/0008-Use-Velocity-compression-and-cipher-natives.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Use Velocity compression and cipher natives diff --git a/net/minecraft/network/CipherDecoder.java b/net/minecraft/network/CipherDecoder.java -index b284b0b9907743a1736c95a4d111a727b789fc94..1467be3de89009529471c1cf4db2590128a6d792 100644 +index e134f0e82d16528da8a6a4af976f98514e5cecc5..034480cac0482355e0be4240214422476bd880ff 100644 --- a/net/minecraft/network/CipherDecoder.java +++ b/net/minecraft/network/CipherDecoder.java @@ -7,14 +7,30 @@ import java.util.List; @@ -15,21 +15,21 @@ index b284b0b9907743a1736c95a4d111a727b789fc94..1467be3de89009529471c1cf4db25901 - private final CipherBase cipher; + private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper - Use Velocity cipher -- public CipherDecoder(Cipher cipher) { +- public CipherDecoder(final Cipher cipher) { - this.cipher = new CipherBase(cipher); -+ public CipherDecoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher ++ public CipherDecoder(final com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher + this.cipher = cipher; // Paper - Use Velocity cipher } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { -- out.add(this.cipher.decipher(ctx, in)); + protected void decode(final ChannelHandlerContext ctx, final ByteBuf msg, final List out) throws Exception { +- out.add(this.cipher.decipher(ctx, msg)); + // Paper start - Use Velocity cipher -+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(ctx.alloc(), this.cipher, in); ++ final ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(ctx.alloc(), this.cipher, msg); + try { + this.cipher.process(compatible); + out.add(compatible); -+ } catch (Exception e) { ++ } catch (final Exception e) { + compatible.release(); // compatible will never be used if we throw an exception + throw e; + } @@ -38,13 +38,13 @@ index b284b0b9907743a1736c95a4d111a727b789fc94..1467be3de89009529471c1cf4db25901 + + // Paper start - Use Velocity cipher + @Override -+ public void handlerRemoved(ChannelHandlerContext ctx) { ++ public void handlerRemoved(final ChannelHandlerContext ctx) { + this.cipher.close(); + } + // Paper end - Use Velocity cipher } diff --git a/net/minecraft/network/CipherEncoder.java b/net/minecraft/network/CipherEncoder.java -index 8f0d47bf46bb5ddf61a84c43891a8dbc1e9f04e1..08363d710fb9c9d91967919b85fdd722e64d4d12 100644 +index 0003a548fe4a48ecfe3534f3de309a920568b773..fae764ffd6858c920cafd2fba4444793b4a83536 100644 --- a/net/minecraft/network/CipherEncoder.java +++ b/net/minecraft/network/CipherEncoder.java @@ -5,15 +5,31 @@ import io.netty.channel.ChannelHandlerContext; @@ -56,22 +56,22 @@ index 8f0d47bf46bb5ddf61a84c43891a8dbc1e9f04e1..08363d710fb9c9d91967919b85fdd722 +public class CipherEncoder extends io.netty.handler.codec.MessageToMessageEncoder { // Paper - Use Velocity cipher; change superclass + private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper - Use Velocity cipher -- public CipherEncoder(Cipher cipher) { +- public CipherEncoder(final Cipher cipher) { - this.cipher = new CipherBase(cipher); -+ public CipherEncoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher ++ public CipherEncoder(final com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher + this.cipher = cipher; // Paper - Use Velocity cipher } + // Paper start - Use Velocity cipher @Override -- protected void encode(ChannelHandlerContext ctx, ByteBuf message, ByteBuf out) throws Exception { -- this.cipher.encipher(message, out); -+ protected void encode(ChannelHandlerContext ctx, ByteBuf message, java.util.List list) throws Exception { -+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(ctx.alloc(), this.cipher, message); +- protected void encode(final ChannelHandlerContext ctx, final ByteBuf msg, final ByteBuf out) throws Exception { +- this.cipher.encipher(msg, out); ++ protected void encode(final ChannelHandlerContext ctx, final ByteBuf msg, final java.util.List out) throws Exception { ++ final ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(ctx.alloc(), this.cipher, msg); + try { + this.cipher.process(compatible); -+ list.add(compatible); -+ } catch (Exception e) { ++ out.add(compatible); ++ } catch (final Exception e) { + compatible.release(); // compatible will never be used if we throw an exception + throw e; + } @@ -80,13 +80,13 @@ index 8f0d47bf46bb5ddf61a84c43891a8dbc1e9f04e1..08363d710fb9c9d91967919b85fdd722 + + // Paper start - Use Velocity cipher + @Override -+ public void handlerRemoved(ChannelHandlerContext ctx) { ++ public void handlerRemoved(final ChannelHandlerContext ctx) { + this.cipher.close(); + } + // Paper end - Use Velocity cipher } diff --git a/net/minecraft/network/CompressionDecoder.java b/net/minecraft/network/CompressionDecoder.java -index 7e16d73f6fe852ca2dfd19cdd4592dedc9c54960..624c5d3d30282cf0d54faabc88d81ca0a9a8c785 100644 +index 2f660186f00414b37fa5c01d00c048173feaef13..fe3f0b59561e7b7236aba9cca2a13cee8955a933 100644 --- a/net/minecraft/network/CompressionDecoder.java +++ b/net/minecraft/network/CompressionDecoder.java @@ -12,14 +12,22 @@ import java.util.zip.Inflater; @@ -100,10 +100,10 @@ index 7e16d73f6fe852ca2dfd19cdd4592dedc9c54960..624c5d3d30282cf0d54faabc88d81ca0 + // Paper start - Use Velocity cipher + @io.papermc.paper.annotation.DoNotUse - public CompressionDecoder(int threshold, boolean validateDecompressed) { + public CompressionDecoder(final int threshold, final boolean validateDecompressed) { + this(null, threshold, validateDecompressed); + } -+ public CompressionDecoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold, boolean validateDecompressed) { ++ public CompressionDecoder(final com.velocitypowered.natives.compression.VelocityCompressor compressor, final int threshold, final boolean validateDecompressed) { this.threshold = threshold; this.validateDecompressed = validateDecompressed; - this.inflater = new Inflater(); @@ -113,27 +113,26 @@ index 7e16d73f6fe852ca2dfd19cdd4592dedc9c54960..624c5d3d30282cf0d54faabc88d81ca0 } @Override -@@ -38,12 +46,40 @@ public class CompressionDecoder extends ByteToMessageDecoder { +@@ -38,12 +46,39 @@ public class CompressionDecoder extends ByteToMessageDecoder { } } + if (inflater != null) { // Paper - Use Velocity cipher; fallback to vanilla inflater this.setupInflaterInput(in); - ByteBuf byteBuf = this.inflate(ctx, i); + ByteBuf output = this.inflate(ctx, uncompressedLength); this.inflater.reset(); - out.add(byteBuf); + out.add(output); + return; // Paper - Use Velocity cipher + } // Paper - use velocity compression + + // Paper start - Use Velocity cipher -+ int claimedUncompressedSize = i; // OBFHELPER -+ ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(ctx.alloc(), this.compressor, in); -+ ByteBuf uncompressed = com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(ctx.alloc(), this.compressor, claimedUncompressedSize); ++ final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(ctx.alloc(), this.compressor, in); ++ final ByteBuf uncompressed = com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(ctx.alloc(), this.compressor, uncompressedLength); + try { -+ this.compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize); ++ this.compressor.inflate(compatibleIn, uncompressed, uncompressedLength); + out.add(uncompressed); + in.clear(); -+ } catch (Exception e) { ++ } catch (final Exception e) { + uncompressed.release(); + throw e; + } finally { @@ -145,22 +144,22 @@ index 7e16d73f6fe852ca2dfd19cdd4592dedc9c54960..624c5d3d30282cf0d54faabc88d81ca0 + + // Paper start - Use Velocity cipher + @Override -+ public void handlerRemoved0(ChannelHandlerContext ctx) { ++ public void handlerRemoved0(final ChannelHandlerContext ctx) { + if (this.compressor != null) { + this.compressor.close(); } } + // Paper end - Use Velocity cipher - private void setupInflaterInput(ByteBuf buffer) { - ByteBuffer byteBuffer; -@@ -79,7 +115,13 @@ public class CompressionDecoder extends ByteToMessageDecoder { + private void setupInflaterInput(final ByteBuf in) { + ByteBuffer input; +@@ -84,7 +119,13 @@ public class CompressionDecoder extends ByteToMessageDecoder { } } -- public void setThreshold(int threshold, boolean validateDecompressed) { +- public void setThreshold(final int threshold, final boolean validateDecompressed) { + // Paper start - Use Velocity cipher -+ public void setThreshold(com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold, boolean validateDecompressed) { ++ public void setThreshold(final com.velocitypowered.natives.compression.VelocityCompressor compressor, final int threshold, final boolean validateDecompressed) { + if (this.compressor == null && compressor != null) { // Only re-configure once. Re-reconfiguring would require closing the native compressor. + this.compressor = compressor; + this.inflater = null; @@ -170,7 +169,7 @@ index 7e16d73f6fe852ca2dfd19cdd4592dedc9c54960..624c5d3d30282cf0d54faabc88d81ca0 this.validateDecompressed = validateDecompressed; } diff --git a/net/minecraft/network/CompressionEncoder.java b/net/minecraft/network/CompressionEncoder.java -index 35b8a2bddeefff2b1ba8ed75ec780c41d59ce92f..1baa8daf880c5f87f1d72ecf0e1b93c7f749648d 100644 +index 319325e741cc314b84844bb49ea569aaf014f319..7d8986fe3c96cef829275ddd113bcd8a8d7ec583 100644 --- a/net/minecraft/network/CompressionEncoder.java +++ b/net/minecraft/network/CompressionEncoder.java @@ -6,17 +6,31 @@ import io.netty.handler.codec.MessageToByteEncoder; @@ -185,10 +184,10 @@ index 35b8a2bddeefff2b1ba8ed75ec780c41d59ce92f..1baa8daf880c5f87f1d72ecf0e1b93c7 private int threshold; + // Paper start - Use Velocity cipher - public CompressionEncoder(int threshold) { + public CompressionEncoder(final int threshold) { + this(null, threshold); + } -+ public CompressionEncoder(@javax.annotation.Nullable com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold) { ++ public CompressionEncoder(@javax.annotation.Nullable final com.velocitypowered.natives.compression.VelocityCompressor compressor, final int threshold) { this.threshold = threshold; - this.deflater = new Deflater(); + if (compressor == null) { @@ -203,19 +202,19 @@ index 35b8a2bddeefff2b1ba8ed75ec780c41d59ce92f..1baa8daf880c5f87f1d72ecf0e1b93c7 } @Override -- protected void encode(ChannelHandlerContext ctx, ByteBuf encodingByteBuf, ByteBuf byteBuf) { -+ protected void encode(ChannelHandlerContext ctx, ByteBuf encodingByteBuf, ByteBuf byteBuf) throws Exception { // Paper - Use Velocity cipher - int i = encodingByteBuf.readableBytes(); - if (i > 8388608) { - throw new IllegalArgumentException("Packet too big (is " + i + ", should be less than 8388608)"); +- protected void encode(final ChannelHandlerContext ctx, final ByteBuf uncompressed, final ByteBuf out) { ++ protected void encode(final ChannelHandlerContext ctx, final ByteBuf uncompressed, final ByteBuf out) throws Exception { // Paper - Use Velocity cipher + int uncompressedLength = uncompressed.readableBytes(); + if (uncompressedLength > 8388608) { + throw new IllegalArgumentException("Packet too big (is " + uncompressedLength + ", should be less than 8388608)"); @@ -25,6 +39,7 @@ public class CompressionEncoder extends MessageToByteEncoder { - VarInt.write(byteBuf, 0); - byteBuf.writeBytes(encodingByteBuf); + VarInt.write(out, 0); + out.writeBytes(uncompressed); } else { + if (this.deflater != null) { // Paper - Use Velocity cipher - byte[] bytes = new byte[i]; - encodingByteBuf.readBytes(bytes); - VarInt.write(byteBuf, bytes.length); + byte[] input = new byte[uncompressedLength]; + uncompressed.readBytes(input); + VarInt.write(out, input.length); @@ -37,6 +52,17 @@ public class CompressionEncoder extends MessageToByteEncoder { } @@ -224,10 +223,10 @@ index 35b8a2bddeefff2b1ba8ed75ec780c41d59ce92f..1baa8daf880c5f87f1d72ecf0e1b93c7 + return; + } + -+ VarInt.write(byteBuf, i); -+ final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(ctx.alloc(), this.compressor, encodingByteBuf); ++ VarInt.write(out, uncompressedLength); ++ final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(ctx.alloc(), this.compressor, uncompressed); + try { -+ this.compressor.deflate(compatibleIn, byteBuf); ++ this.compressor.deflate(compatibleIn, out); + } finally { + compatibleIn.release(); + } @@ -235,13 +234,13 @@ index 35b8a2bddeefff2b1ba8ed75ec780c41d59ce92f..1baa8daf880c5f87f1d72ecf0e1b93c7 } } @@ -48,4 +74,31 @@ public class CompressionEncoder extends MessageToByteEncoder { - public void setThreshold(int threshold) { + public void setThreshold(final int threshold) { this.threshold = threshold; } + + // Paper start - Use Velocity cipher + @Override -+ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception { ++ protected ByteBuf allocateBuffer(final ChannelHandlerContext ctx, final ByteBuf msg, final boolean preferDirect) throws Exception { + if (this.compressor != null) { + // We allocate bytes to be compressed plus 1 byte. This covers two cases: + // @@ -259,7 +258,7 @@ index 35b8a2bddeefff2b1ba8ed75ec780c41d59ce92f..1baa8daf880c5f87f1d72ecf0e1b93c7 + } + + @Override -+ public void handlerRemoved(ChannelHandlerContext ctx) { ++ public void handlerRemoved(final ChannelHandlerContext ctx) { + if (this.compressor != null) { + this.compressor.close(); + } @@ -267,28 +266,28 @@ index 35b8a2bddeefff2b1ba8ed75ec780c41d59ce92f..1baa8daf880c5f87f1d72ecf0e1b93c7 + // Paper end - Use Velocity cipher } diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java -index 8817a2d6500d1bdce96d87dc8f7f364ccfa390e5..19ec939529eb638bdc4d7fd9260f161fae118314 100644 +index 9cd6ec17981da310be1d35054af080d6b33beff8..d7ed1621709c491525daaf7adc8e9f0d781c93e1 100644 --- a/net/minecraft/network/Connection.java +++ b/net/minecraft/network/Connection.java -@@ -728,11 +728,22 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -738,11 +738,22 @@ public class Connection extends SimpleChannelInboundHandler> { return connection; } -- public void setEncryptionKey(Cipher decryptingCipher, Cipher encryptingCipher) { +- public void setEncryptionKey(final Cipher decryptCipher, final Cipher encryptCipher) { - this.encrypted = true; -- this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptingCipher)); -- this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptingCipher)); +- this.channel.pipeline().addBefore(HandlerNames.SPLITTER, HandlerNames.DECRYPT, new CipherDecoder(decryptCipher)); +- this.channel.pipeline().addBefore(HandlerNames.PREPENDER, HandlerNames.ENCRYPT, new CipherEncoder(encryptCipher)); + // Paper start - Use Velocity cipher -+ public void setEncryptionKey(javax.crypto.SecretKey key) throws net.minecraft.util.CryptException { ++ public void setEncryptionKey(final javax.crypto.SecretKey key) throws net.minecraft.util.CryptException { + if (!this.encrypted) { + try { -+ com.velocitypowered.natives.encryption.VelocityCipher decryptionCipher = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key); -+ com.velocitypowered.natives.encryption.VelocityCipher encryptionCipher = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key); ++ final com.velocitypowered.natives.encryption.VelocityCipher decryptionCipher = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key); ++ final com.velocitypowered.natives.encryption.VelocityCipher encryptionCipher = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key); + + this.encrypted = true; -+ this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher)); -+ this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher)); -+ } catch (java.security.GeneralSecurityException e) { ++ this.channel.pipeline().addBefore(HandlerNames.SPLITTER, HandlerNames.DECRYPT, new CipherDecoder(decryptionCipher)); ++ this.channel.pipeline().addBefore(HandlerNames.PREPENDER, HandlerNames.ENCRYPT, new CipherEncoder(encryptionCipher)); ++ } catch (final java.security.GeneralSecurityException e) { + throw new net.minecraft.util.CryptException(e); + } + } @@ -297,32 +296,32 @@ index 8817a2d6500d1bdce96d87dc8f7f364ccfa390e5..19ec939529eb638bdc4d7fd9260f161f public boolean isEncrypted() { return this.encrypted; -@@ -769,16 +780,17 @@ public class Connection extends SimpleChannelInboundHandler> { - // Paper end - add proper async disconnect - public void setupCompression(int threshold, boolean validateDecompressed) { +@@ -780,16 +791,17 @@ public class Connection extends SimpleChannelInboundHandler> { + + public void setupCompression(final int threshold, final boolean validateDecompressed) { if (threshold >= 0) { + com.velocitypowered.natives.compression.VelocityCompressor compressor = com.velocitypowered.natives.util.Natives.compress.get().create(io.papermc.paper.configuration.GlobalConfiguration.get().misc.compressionLevel.or(-1)); // Paper - Use Velocity cipher - if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder compressionDecoder) { + if (this.channel.pipeline().get(HandlerNames.DECOMPRESS) instanceof CompressionDecoder compressionDecoder) { - compressionDecoder.setThreshold(threshold, validateDecompressed); + compressionDecoder.setThreshold(compressor, threshold, validateDecompressed); // Paper - Use Velocity cipher } else { -- this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(threshold, validateDecompressed)); -+ this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(compressor, threshold, validateDecompressed)); // Paper - Use Velocity cipher +- this.channel.pipeline().addAfter(HandlerNames.SPLITTER, HandlerNames.DECOMPRESS, new CompressionDecoder(threshold, validateDecompressed)); ++ this.channel.pipeline().addAfter(HandlerNames.SPLITTER, HandlerNames.DECOMPRESS, new CompressionDecoder(compressor, threshold, validateDecompressed)); // Paper - Use Velocity cipher } - if (this.channel.pipeline().get("compress") instanceof CompressionEncoder compressionEncoder) { + if (this.channel.pipeline().get(HandlerNames.COMPRESS) instanceof CompressionEncoder compressionEncoder) { compressionEncoder.setThreshold(threshold); } else { -- this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(threshold)); -+ this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressor, threshold)); // Paper - Use Velocity cipher +- this.channel.pipeline().addAfter(HandlerNames.PREPENDER, HandlerNames.COMPRESS, new CompressionEncoder(threshold)); ++ this.channel.pipeline().addAfter(HandlerNames.PREPENDER, HandlerNames.COMPRESS, new CompressionEncoder(compressor, threshold)); // Paper - Use Velocity cipher } 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 309845b28306dfa5946e79b80e5b1dde82dd68cd..33e1cdb00d648c992af03a976899253141314b27 100644 +index b266e4f7b437578356568d86910e3b428ee66523..0b57e3bcd0319e4e9571fddbcb85db5a3105b8b2 100644 --- a/net/minecraft/server/network/ServerConnectionListener.java +++ b/net/minecraft/server/network/ServerConnectionListener.java -@@ -75,6 +75,11 @@ public class ServerConnectionListener { +@@ -77,6 +77,10 @@ public class ServerConnectionListener { LOGGER.warn("Using HAProxy, please ensure the server port is adequately firewalled."); } // Paper end - Warn people with console access that HAProxy is in use. @@ -330,23 +329,22 @@ index 309845b28306dfa5946e79b80e5b1dde82dd68cd..33e1cdb00d648c992af03a9768992531 + ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.compress.getLoadedVariant() + " compression from Velocity."); + ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.cipher.getLoadedVariant() + " cipher from Velocity."); + // Paper end - Use Velocity cipher -+ this.channels .add( new ServerBootstrap() diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index ec5bd4568872c48addd26dd652868e0e5df57038..31bc0a105152a7f24a4126542bea7dbebc3e037c 100644 +index 8203765d5570644b4c8f6790f361bca2c7066b25..718f00ad9c587a4b0c12f351c10d462113f16ac3 100644 --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java @@ -245,11 +245,9 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, } - SecretKey secretKey = packet.getSecretKey(_private); -- Cipher cipher = Crypt.getCipher(2, secretKey); -- Cipher cipher1 = Crypt.getCipher(1, secretKey); - string = new BigInteger(Crypt.digestData("", this.server.getKeyPair().getPublic(), secretKey)).toString(16); + SecretKey secretKey = packet.getSecretKey(serverPrivateKey); +- Cipher decryptCipher = Crypt.getCipher(2, secretKey); +- Cipher encryptCipher = Crypt.getCipher(1, secretKey); + digest = new BigInteger(Crypt.digestData("", this.server.getKeyPair().getPublic(), secretKey)).toString(16); this.state = ServerLoginPacketListenerImpl.State.AUTHENTICATING; -- this.connection.setEncryptionKey(cipher, cipher1); +- this.connection.setEncryptionKey(decryptCipher, encryptCipher); + this.connection.setEncryptionKey(secretKey); // Paper - Use Velocity cipher } catch (CryptException var7) { throw new IllegalStateException("Protocol error", var7); diff --git a/paper-server/patches/features/0007-Optimize-GoalSelector-Goal.Flag-Set-operations.patch b/paper-server/patches/features/0009-Optimize-GoalSelector-Goal.Flag-Set-operations.patch similarity index 62% rename from paper-server/patches/features/0007-Optimize-GoalSelector-Goal.Flag-Set-operations.patch rename to paper-server/patches/features/0009-Optimize-GoalSelector-Goal.Flag-Set-operations.patch index 79957e9faa04..21a8d5e24dda 100644 --- a/paper-server/patches/features/0007-Optimize-GoalSelector-Goal.Flag-Set-operations.patch +++ b/paper-server/patches/features/0009-Optimize-GoalSelector-Goal.Flag-Set-operations.patch @@ -7,7 +7,7 @@ Optimise the stream.anyMatch statement to move to a bitset where we can replace the call with a single bitwise operation. diff --git a/net/minecraft/world/entity/ai/goal/Goal.java b/net/minecraft/world/entity/ai/goal/Goal.java -index a66c86066ca2eda10f0ef62e5197a765a994f250..f54bbe2e65b18f214266769c7a64144baafa9a58 100644 +index 5746eefd9e8fa851e74ccf6b5d889a9f8e6fc1e0..81fa892212f586e8cd7938556eeaa9f9dc2654aa 100644 --- a/net/minecraft/world/entity/ai/goal/Goal.java +++ b/net/minecraft/world/entity/ai/goal/Goal.java @@ -7,7 +7,15 @@ import net.minecraft.world.entity.Entity; @@ -30,12 +30,12 @@ index a66c86066ca2eda10f0ef62e5197a765a994f250..f54bbe2e65b18f214266769c7a64144b @@ -33,8 +41,13 @@ public abstract class Goal { } - public void setFlags(EnumSet flagSet) { + public void setFlags(final EnumSet requiredControlFlags) { - this.flags.clear(); -- this.flags.addAll(flagSet); +- this.flags.addAll(requiredControlFlags); + // Paper start - remove streams from GoalSelector + this.goalTypes.clear(); -+ this.goalTypes.addAllUnchecked(flagSet); ++ this.goalTypes.addAllUnchecked(requiredControlFlags); + if (this.goalTypes.size() == 0) { + this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR); + } @@ -59,17 +59,17 @@ index a66c86066ca2eda10f0ef62e5197a765a994f250..f54bbe2e65b18f214266769c7a64144b // Paper start - Mob Goal API public boolean hasFlag(final Goal.Flag flag) { - return this.flags.contains(flag); -+ return this.goalTypes.hasElement(flag); ++ return this.goalTypes.hasElement(flag); // Paper - remove streams from GoalSelector } public void addFlag(final Goal.Flag flag) { - this.flags.add(flag); -+ this.goalTypes.addUnchecked(flag); ++ this.goalTypes.addUnchecked(flag); // Paper - remove streams from GoalSelector } // Paper end - Mob Goal API diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java -index 674966c580220a4e0c83a628c763aaea8bfd0b1c..859b859d29b637200cf7c9a0bd52d9f712413e3d 100644 +index 844ced7a271ffb557f3c8f2fcd6a4c6ad499aeae..75d8953845b2e9acb62b19a859d0e39fd63533eb 100644 --- a/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java @@ -23,7 +23,8 @@ public class GoalSelector { @@ -81,80 +81,80 @@ index 674966c580220a4e0c83a628c763aaea8bfd0b1c..859b859d29b637200cf7c9a0bd52d9f7 + private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from GoalSelector private int curRate; // Paper - EAR 2 - public void addGoal(int priority, Goal goal) { + public void addGoal(final int prio, final Goal goal) { @@ -60,18 +61,18 @@ public class GoalSelector { - this.availableGoals.removeIf(wrappedGoal1 -> wrappedGoal1.getGoal() == goal); + this.availableGoals.removeIf(goal -> goal.getGoal() == toRemove); } -- private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet flag) { -- for (Goal.Flag flag1 : goal.getFlags()) { -- if (flag.contains(flag1)) { +- private static boolean goalContainsAnyFlags(final WrappedGoal goal, final EnumSet disabledFlags) { +- for (Goal.Flag flag : goal.getFlags()) { +- if (disabledFlags.contains(flag)) { - return true; - } - } - - return false; + // Paper start - Perf: optimize goal types -+ private static boolean goalContainsAnyFlags(WrappedGoal goal, ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet flags) { -+ return goal.getFlags().hasCommonElements(flags); ++ private static boolean goalContainsAnyFlags(final WrappedGoal goal, final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet disabledFlags) { ++ return goal.getFlags().hasCommonElements(disabledFlags); } - private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map flag) { -- for (Goal.Flag flag1 : goal.getFlags()) { + private static boolean goalCanBeReplacedForAllFlags(final WrappedGoal goal, final Map lockedFlags) { +- for (Goal.Flag flag : goal.getFlags()) { + long flagIterator = goal.getFlags().getBackingSet(); -+ int wrappedGoalSize = goal.getFlags().size(); -+ for (int i = 0; i < wrappedGoalSize; ++i) { -+ final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; ++ final int flagSize = goal.getFlags().size(); ++ for (int i = 0; i < flagSize; ++i) { ++ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; + flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); + // Paper end - Perf: optimize goal types - if (!flag.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) { + if (!lockedFlags.getOrDefault(flag, NO_GOAL).canBeReplacedBy(goal)) { return false; } @@ -85,7 +86,7 @@ public class GoalSelector { - profilerFiller.push("goalCleanup"); + profiler.push("goalCleanup"); - for (WrappedGoal wrappedGoal : this.availableGoals) { -- if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.disabledFlags) || !wrappedGoal.canContinueToUse())) { -+ if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams - wrappedGoal.stop(); + for (WrappedGoal goal : this.availableGoals) { +- if (goal.isRunning() && (goalContainsAnyFlags(goal, this.disabledFlags) || !goal.canContinueToUse())) { ++ if (goal.isRunning() && (goalContainsAnyFlags(goal, this.goalTypes) || !goal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams + goal.stop(); } } @@ -95,11 +96,14 @@ public class GoalSelector { - profilerFiller.push("goalUpdate"); - - for (WrappedGoal wrappedGoalx : this.availableGoals) { -- if (!wrappedGoalx.isRunning() -- && !goalContainsAnyFlags(wrappedGoalx, this.disabledFlags) -- && goalCanBeReplacedForAllFlags(wrappedGoalx, this.lockedFlags) -- && wrappedGoalx.canUse()) { -- for (Goal.Flag flag : wrappedGoalx.getFlags()) { + profiler.push("goalUpdate"); + + for (WrappedGoal goalx : this.availableGoals) { +- if (!goalx.isRunning() +- && !goalContainsAnyFlags(goalx, this.disabledFlags) +- && goalCanBeReplacedForAllFlags(goalx, this.lockedFlags) +- && goalx.canUse()) { +- for (Goal.Flag flag : goalx.getFlags()) { + // Paper start -+ if (!wrappedGoalx.isRunning() && !goalContainsAnyFlags(wrappedGoalx, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoalx, this.lockedFlags) && wrappedGoalx.canUse()) { -+ long flagIterator = wrappedGoalx.getFlags().getBackingSet(); -+ int wrappedGoalSize = wrappedGoalx.getFlags().size(); ++ if (!goalx.isRunning() && !goalContainsAnyFlags(goalx, this.goalTypes) && goalCanBeReplacedForAllFlags(goalx, this.lockedFlags) && goalx.canUse()) { ++ long flagIterator = goalx.getFlags().getBackingSet(); ++ int wrappedGoalSize = goalx.getFlags().size(); + for (int i = 0; i < wrappedGoalSize; ++i) { + final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; + flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); + // Paper end - WrappedGoal wrappedGoal1 = this.lockedFlags.getOrDefault(flag, NO_GOAL); - wrappedGoal1.stop(); - this.lockedFlags.put(flag, wrappedGoalx); + WrappedGoal currentGoal = this.lockedFlags.getOrDefault(flag, NO_GOAL); + currentGoal.stop(); + this.lockedFlags.put(flag, goalx); @@ -131,11 +135,11 @@ public class GoalSelector { } - public void disableControlFlag(Goal.Flag flag) { + public void disableControlFlag(final Goal.Flag flag) { - this.disabledFlags.add(flag); + this.goalTypes.addUnchecked(flag); // Paper - remove streams from GoalSelector } - public void enableControlFlag(Goal.Flag flag) { + public void enableControlFlag(final Goal.Flag flag) { - this.disabledFlags.remove(flag); + this.goalTypes.removeUnchecked(flag); // Paper - remove streams from GoalSelector } - public void setControlFlag(Goal.Flag flag, boolean enabled) { + public void setControlFlag(final Goal.Flag flag, final boolean enabled) { diff --git a/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/net/minecraft/world/entity/ai/goal/WrappedGoal.java -index 4bdbd323b642ed3422948fe24780be8b503602dc..2c2ab6a1df9d3d23773e44ce4041cc1c21b55163 100644 +index 95d8a70deba7da2b5471afd8c70eeefa2db37ea3..0203405b8abefa82cf9cafd9bc0ab2a3ee866240 100644 --- a/net/minecraft/world/entity/ai/goal/WrappedGoal.java +++ b/net/minecraft/world/entity/ai/goal/WrappedGoal.java @@ -69,7 +69,7 @@ public class WrappedGoal extends Goal { diff --git a/paper-server/patches/features/0009-Handle-Oversized-block-entities-in-chunks.patch b/paper-server/patches/features/0010-Handle-Oversized-block-entities-in-chunks.patch similarity index 90% rename from paper-server/patches/features/0009-Handle-Oversized-block-entities-in-chunks.patch rename to paper-server/patches/features/0010-Handle-Oversized-block-entities-in-chunks.patch index d1f3ce49f7b9..66abb1d74e1c 100644 --- a/paper-server/patches/features/0009-Handle-Oversized-block-entities-in-chunks.patch +++ b/paper-server/patches/features/0010-Handle-Oversized-block-entities-in-chunks.patch @@ -9,7 +9,7 @@ creating too large of a packet to sed. Co-authored-by: Spottedleaf diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java -index de234f220ba09ad9b5e0c8215b49d20ca51d0ac7..e216a9d70be5a3da7c03ee99a8986391ef2dbd5b 100644 +index 2bfc4cb47fc51345fc733b6ab3b19ace4f8bc171..dad2b89355b05900251955d015451fe195bfdb49 100644 --- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java +++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java @@ -32,6 +32,14 @@ public class ClientboundLevelChunkPacketData { @@ -25,7 +25,7 @@ index de234f220ba09ad9b5e0c8215b49d20ca51d0ac7..e216a9d70be5a3da7c03ee99a8986391 + } + // Paper end - Handle oversized block entities in chunks - public ClientboundLevelChunkPacketData(LevelChunk levelChunk) { + public ClientboundLevelChunkPacketData(final LevelChunk levelChunk) { this.heightmaps = levelChunk.getHeightmaps() @@ -41,8 +49,18 @@ public class ClientboundLevelChunkPacketData { this.buffer = new byte[calculateChunkSize(levelChunk)]; @@ -47,10 +47,10 @@ index de234f220ba09ad9b5e0c8215b49d20ca51d0ac7..e216a9d70be5a3da7c03ee99a8986391 } } diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -index 3a384175f8e7f204234bbaf3081bdc20c47a0d4b..fdd164cd45a26c7ef25f1153ab8985ba50c01b14 100644 +index be8ba1425b55a4948afb3f86c0feb3e2b1c2b561..a94e6ca1d396b6b0781de5d550c4a804274ee003 100644 --- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java +++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -@@ -71,4 +71,11 @@ public class ClientboundLevelChunkWithLightPacket implements Packet -Date: Fri, 22 Jan 2021 21:50:18 +0100 -Subject: [PATCH] optimize dirt and snow spreading - - -diff --git a/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java b/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java -index 0036d684e619e373b0e15e0c38788a2231864b52..b11c6d76cb9df1f92a4581646929d0dc79ec3e4a 100644 ---- a/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java -+++ b/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java -@@ -17,8 +17,13 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock { - } - - private static boolean canBeGrass(BlockState state, LevelReader level, BlockPos pos) { -+ // Paper start - Perf: optimize dirt and snow spreading -+ return canBeGrass(level.getChunk(pos), state, level, pos); -+ } -+ private static boolean canBeGrass(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader level, BlockPos pos) { -+ // Paper end - Perf: optimize dirt and snow spreading - BlockPos blockPos = pos.above(); -- BlockState blockState = level.getBlockState(blockPos); -+ BlockState blockState = chunk.getBlockState(blockPos); // Paper - Perf: optimize dirt and snow spreading - if (blockState.is(Blocks.SNOW) && blockState.getValue(SnowLayerBlock.LAYERS) == 1) { - return true; - } else if (blockState.getFluidState().getAmount() == 8) { -@@ -33,14 +38,27 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock { - protected abstract MapCodec codec(); - - private static boolean canPropagate(BlockState state, LevelReader level, BlockPos pos) { -+ // Paper start - Perf: optimize dirt and snow spreading -+ return canPropagate(level.getChunk(pos), state, level, pos); -+ } -+ -+ private static boolean canPropagate(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader level, BlockPos pos) { -+ // Paper end - Perf: optimize dirt and snow spreading - BlockPos blockPos = pos.above(); -- return canBeGrass(state, level, pos) && !level.getFluidState(blockPos).is(FluidTags.WATER); -+ return canBeGrass(chunk, state, level, pos) && !chunk.getFluidState(blockPos).is(FluidTags.WATER); // Paper - Perf: optimize dirt and snow spreading - } - - @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { - if (this instanceof GrassBlock && level.paperConfig().tickRates.grassSpread != 1 && (level.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % level.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks -- if (!canBeGrass(state, level, pos)) { -+ // Paper start - Perf: optimize dirt and snow spreading -+ final net.minecraft.world.level.chunk.ChunkAccess cachedBlockChunk = level.getChunkIfLoaded(pos); -+ if (cachedBlockChunk == null) { // Is this needed? -+ return; -+ } -+ -+ if (!canBeGrass(cachedBlockChunk, state, level, pos)) { -+ // Paper end - Perf: optimize dirt and snow spreading - // CraftBukkit start - if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) { - return; -@@ -53,8 +71,20 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock { - - for (int i = 0; i < 4; i++) { - BlockPos blockPos = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1); -- if (level.getBlockState(blockPos).is(Blocks.DIRT) && canPropagate(blockState, level, blockPos)) { -- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, blockState.setValue(SNOWY, isSnowySetting(level.getBlockState(blockPos.above()))), Block.UPDATE_ALL); // CraftBukkit -+ // Paper start - Perf: optimize dirt and snow spreading -+ if (pos.getX() == blockPos.getX() && pos.getY() == blockPos.getY() && pos.getZ() == blockPos.getZ()) { -+ continue; -+ } -+ -+ final net.minecraft.world.level.chunk.ChunkAccess access; -+ if (cachedBlockChunk.locX == blockPos.getX() >> 4 && cachedBlockChunk.locZ == blockPos.getZ() >> 4) { -+ access = cachedBlockChunk; -+ } else { -+ access = level.getChunkAt(blockPos); -+ } -+ if (access.getBlockState(blockPos).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(access, blockState, level, blockPos)) { -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, blockState.setValue(SNOWY, isSnowySetting(access.getBlockState(blockPos.above()))), Block.UPDATE_ALL); // CraftBukkit -+ // Paper end - Perf: optimize dirt and snow spreading - } - } - } diff --git a/paper-server/patches/features/0011-Optimize-Bit-Operations-by-inlining.patch b/paper-server/patches/features/0011-Optimize-Bit-Operations-by-inlining.patch index f7f00fa36ba6..f4b1300a9e1c 100644 --- a/paper-server/patches/features/0011-Optimize-Bit-Operations-by-inlining.patch +++ b/paper-server/patches/features/0011-Optimize-Bit-Operations-by-inlining.patch @@ -7,10 +7,10 @@ Inline bit operations and reduce instruction count to make these hot operations faster diff --git a/net/minecraft/core/BlockPos.java b/net/minecraft/core/BlockPos.java -index e694be770b49268b77566c3fd7b111298fb8920b..e87473fcb36e18d960ef3e4082995785d64b9c0f 100644 +index 1359d55f32160d74b5dd7cd1a575ad1a87d5d70c..b94e57a7776b211d509c873edbd6bc9128cc4d35 100644 --- a/net/minecraft/core/BlockPos.java +++ b/net/minecraft/core/BlockPos.java -@@ -49,15 +49,17 @@ public class BlockPos extends Vec3i { +@@ -48,15 +48,17 @@ public class BlockPos extends Vec3i { } }; public static final BlockPos ZERO = new BlockPos(0, 0, 0); @@ -34,79 +34,79 @@ index e694be770b49268b77566c3fd7b111298fb8920b..e87473fcb36e18d960ef3e4082995785 + public static final int MAX_HORIZONTAL_COORDINATE = 33554431; + // Paper end - Optimize Bit Operations by inlining - public BlockPos(int x, int y, int z) { + public BlockPos(final int x, final int y, final int z) { super(x, y, z); -@@ -67,28 +69,29 @@ public class BlockPos extends Vec3i { - this(vector.getX(), vector.getY(), vector.getZ()); +@@ -66,28 +68,29 @@ public class BlockPos extends Vec3i { + this(vec3i.getX(), vec3i.getY(), vec3i.getZ()); } + public static long getAdjacent(int baseX, int baseY, int baseZ, Direction direction) { return asLong(baseX + direction.getStepX(), baseY + direction.getStepY(), baseZ + direction.getStepZ()); } // Paper - public static long offset(long pos, Direction direction) { - return offset(pos, direction.getStepX(), direction.getStepY(), direction.getStepZ()); + public static long offset(final long blockNode, final Direction offset) { + return offset(blockNode, offset.getStepX(), offset.getStepY(), offset.getStepZ()); } - public static long offset(long pos, int dx, int dy, int dz) { -- return asLong(getX(pos) + dx, getY(pos) + dy, getZ(pos) + dz); -+ return asLong((int) (pos >> 38) + dx, (int) ((pos << 52) >> 52) + dy, (int) ((pos << 26) >> 38) + dz); // Paper - simplify/inline + public static long offset(final long blockNode, final int stepX, final int stepY, final int stepZ) { +- return asLong(getX(blockNode) + stepX, getY(blockNode) + stepY, getZ(blockNode) + stepZ); ++ return asLong((int) (blockNode >> 38) + stepX, (int) ((blockNode << 52) >> 52) + stepY, (int) ((blockNode << 26) >> 38) + stepZ); // Paper - simplify/inlin } - public static int getX(long packedPos) { -- return (int)(packedPos << 64 - X_OFFSET - PACKED_HORIZONTAL_LENGTH >> 64 - PACKED_HORIZONTAL_LENGTH); -+ return (int) (packedPos >> 38); // Paper - simplify/inline + public static int getX(final long blockNode) { +- return (int)(blockNode << 64 - X_OFFSET - PACKED_HORIZONTAL_LENGTH >> 64 - PACKED_HORIZONTAL_LENGTH); ++ return (int) (blockNode >> 38); // Paper - simplify/inline } - public static int getY(long packedPos) { -- return (int)(packedPos << 64 - PACKED_Y_LENGTH >> 64 - PACKED_Y_LENGTH); -+ return (int) ((packedPos << 52) >> 52); // Paper - simplify/inline + public static int getY(final long blockNode) { +- return (int)(blockNode << 64 - PACKED_Y_LENGTH >> 64 - PACKED_Y_LENGTH); ++ return (int) ((blockNode << 52) >> 52); // Paper - simplify/inline } - public static int getZ(long packedPos) { -- return (int)(packedPos << 64 - Z_OFFSET - PACKED_HORIZONTAL_LENGTH >> 64 - PACKED_HORIZONTAL_LENGTH); -+ return (int) ((packedPos << 26) >> 38); // Paper - simplify/inline + public static int getZ(final long blockNode) { +- return (int)(blockNode << 64 - Z_OFFSET - PACKED_HORIZONTAL_LENGTH >> 64 - PACKED_HORIZONTAL_LENGTH); ++ return (int) ((blockNode << 26) >> 38); // Paper - simplify/inline } - public static BlockPos of(long packedPos) { -- return new BlockPos(getX(packedPos), getY(packedPos), getZ(packedPos)); -+ return new BlockPos((int) (packedPos >> 38), (int) ((packedPos << 52) >> 52), (int) ((packedPos << 26) >> 38)); // Paper - simplify/inline + public static BlockPos of(final long blockNode) { +- return new BlockPos(getX(blockNode), getY(blockNode), getZ(blockNode)); ++ return new BlockPos((int) (blockNode >> 38), (int) ((blockNode << 52) >> 52), (int) ((blockNode << 26) >> 38)); // Paper - simplify/inline } - public static BlockPos containing(double x, double y, double z) { -@@ -112,10 +115,7 @@ public class BlockPos extends Vec3i { + public static BlockPos containing(final double x, final double y, final double z) { +@@ -111,10 +114,7 @@ public class BlockPos extends Vec3i { } - public static long asLong(int x, int y, int z) { -- long l = 0L; -- l |= (x & PACKED_X_MASK) << X_OFFSET; -- l |= (y & PACKED_Y_MASK) << 0; -- return l | (z & PACKED_Z_MASK) << Z_OFFSET; -+ return ((x & 67108863L) << 38) | ((y & 4095L)) | ((z & 67108863L) << 12); // Paper - inline constants and simplify + public static long asLong(final int x, final int y, final int z) { +- long node = 0L; +- node |= (x & PACKED_X_MASK) << X_OFFSET; +- node |= (y & PACKED_Y_MASK) << 0; +- return node | (z & PACKED_Z_MASK) << Z_OFFSET; ++ return ((x & 67108863L) << 38) | ((y & 4095L)) | ((z & 67108863L) << 12); // Paper - inline constants and simplify } - public static long getFlatIndex(long packedPos) { + public static long getFlatIndex(final long neighborBlockNode) { diff --git a/net/minecraft/core/SectionPos.java b/net/minecraft/core/SectionPos.java -index a31e7af294d574c2c9711de75d33022ed86984c4..7a9889333d6a82a58731a54c612bddfaa1a3ec6a 100644 +index e6595167980d93772088f63ba6dd0423489d90c4..96439461ea272b9f5101365a094f8eec1e28d582 100644 --- a/net/minecraft/core/SectionPos.java +++ b/net/minecraft/core/SectionPos.java @@ -42,7 +42,7 @@ public class SectionPos extends Vec3i { } - public static SectionPos of(BlockPos pos) { + public static SectionPos of(final BlockPos pos) { - return new SectionPos(blockToSectionCoord(pos.getX()), blockToSectionCoord(pos.getY()), blockToSectionCoord(pos.getZ())); + return new SectionPos(pos.getX() >> 4, pos.getY() >> 4, pos.getZ() >> 4); // Paper } - public static SectionPos of(ChunkPos chunkPos, int y) { + public static SectionPos of(final ChunkPos pos, final int sectionY) { @@ -58,7 +58,7 @@ public class SectionPos extends Vec3i { } - public static SectionPos of(long packed) { -- return new SectionPos(x(packed), y(packed), z(packed)); -+ return new SectionPos((int) (packed >> 42), (int) (packed << 44 >> 44), (int) (packed << 22 >> 42)); // Paper + public static SectionPos of(final long sectionNode) { +- return new SectionPos(x(sectionNode), y(sectionNode), z(sectionNode)); ++ return new SectionPos((int) (sectionNode >> 42), (int) (sectionNode << 44 >> 44), (int) (sectionNode << 22 >> 42)); // Paper } - public static SectionPos bottomOf(ChunkAccess chunk) { + public static SectionPos bottomOf(final ChunkAccess chunk) { @@ -69,8 +69,16 @@ public class SectionPos extends Vec3i { - return offset(packed, direction.getStepX(), direction.getStepY(), direction.getStepZ()); + return offset(sectionNode, offset.getStepX(), offset.getStepY(), offset.getStepZ()); } + // Paper start @@ -117,24 +117,24 @@ index a31e7af294d574c2c9711de75d33022ed86984c4..7a9889333d6a82a58731a54c612bddfa + return (((long) (x + direction.getStepX()) & 4194303L) << 42) | (((long) ((y) + direction.getStepY()) & 1048575L)) | (((long) (z + direction.getStepZ()) & 4194303L) << 20); + } + // Paper end - public static long offset(long packed, int dx, int dy, int dz) { -- return asLong(x(packed) + dx, y(packed) + dy, z(packed) + dz); -+ return (((long) ((int) (packed >> 42) + dx) & 4194303L) << 42) | (((long) ((int) (packed << 44 >> 44) + dy) & 1048575L)) | (((long) ((int) (packed << 22 >> 42) + dz) & 4194303L) << 20); // Simplify to reduce instruction count + public static long offset(final long sectionNode, final int stepX, final int stepY, final int stepZ) { +- return asLong(x(sectionNode) + stepX, y(sectionNode) + stepY, z(sectionNode) + stepZ); ++ return (((long) ((int) (sectionNode >> 42) + stepX) & 4194303L) << 42) | (((long) ((int) (sectionNode << 44 >> 44) + stepY) & 1048575L)) | (((long) ((int) (sectionNode << 22 >> 42) + stepZ) & 4194303L) << 20); // Paper - Simplify to reduce instruction count } - public static int posToSectionCoord(double pos) { + public static int posToSectionCoord(final double pos) { @@ -90,10 +98,7 @@ public class SectionPos extends Vec3i { } - public static short sectionRelativePos(BlockPos pos) { -- int relativeBlockPosX = sectionRelative(pos.getX()); -- int relativeBlockPosY = sectionRelative(pos.getY()); -- int relativeBlockPosZ = sectionRelative(pos.getZ()); -- return (short)(relativeBlockPosX << 8 | relativeBlockPosZ << 4 | relativeBlockPosY << 0); + public static short sectionRelativePos(final BlockPos pos) { +- int x = sectionRelative(pos.getX()); +- int y = sectionRelative(pos.getY()); +- int z = sectionRelative(pos.getZ()); +- return (short)(x << 8 | z << 4 | y << 0); + return (short) ((pos.getX() & 15) << 8 | (pos.getZ() & 15) << 4 | pos.getY() & 15); // Paper - simplify/inline } - public static int sectionRelativeX(short x) { + public static int sectionRelativeX(final short relative) { @@ -156,16 +161,16 @@ public class SectionPos extends Vec3i { return this.getZ(); } @@ -158,16 +158,18 @@ index a31e7af294d574c2c9711de75d33022ed86984c4..7a9889333d6a82a58731a54c612bddfa } public int maxBlockX() { -@@ -181,7 +186,7 @@ public class SectionPos extends Vec3i { +@@ -181,9 +186,7 @@ public class SectionPos extends Vec3i { } - public static long blockToSection(long pos) { -- return asLong(blockToSectionCoord(BlockPos.getX(pos)), blockToSectionCoord(BlockPos.getY(pos)), blockToSectionCoord(BlockPos.getZ(pos))); -+ return (((long) (int) (pos >> 42) & 4194303L) << 42) | (((long) (int) ((pos << 52) >> 56) & 1048575L)) | (((long) (int) ((pos << 26) >> 42) & 4194303L) << 20); // Simplify to reduce instruction count + public static long blockToSection(final long blockNode) { +- return asLong( +- blockToSectionCoord(BlockPos.getX(blockNode)), blockToSectionCoord(BlockPos.getY(blockNode)), blockToSectionCoord(BlockPos.getZ(blockNode)) +- ); ++ return (((long) (int) (blockNode >> 42) & 4194303L) << 42) | (((long) (int) ((blockNode << 52) >> 56) & 1048575L)) | (((long) (int) ((blockNode << 26) >> 42) & 4194303L) << 20); // Paper - Simplify to reduce instruction count } - public static long getZeroNode(int x, int z) { -@@ -213,15 +218,17 @@ public class SectionPos extends Vec3i { + public static long getZeroNode(final int x, final int z) { +@@ -215,15 +218,17 @@ public class SectionPos extends Vec3i { return asLong(blockToSectionCoord(pos.getX()), blockToSectionCoord(pos.getY()), blockToSectionCoord(pos.getZ())); } @@ -176,11 +178,11 @@ index a31e7af294d574c2c9711de75d33022ed86984c4..7a9889333d6a82a58731a54c612bddfa + return (((long) (x >> 4) & 4194303L) << 42) | (((long) (y >> 4) & 1048575L)) | (((long) (z >> 4) & 4194303L) << 20); + } + // Paper end - public static long asLong(int x, int y, int z) { -- long l = 0L; -- l |= (x & 4194303L) << 42; -- l |= (y & 1048575L) << 0; -- return l | (z & 4194303L) << 20; + public static long asLong(final int x, final int y, final int z) { +- long node = 0L; +- node |= (x & 4194303L) << 42; +- node |= (y & 1048575L) << 0; +- return node | (z & 4194303L) << 20; + return ((x & 4194303L) << 42) | ((y & 1048575L)) | ((z & 4194303L) << 20); // Paper - Simplify to reduce instruction count } @@ -190,15 +192,15 @@ index a31e7af294d574c2c9711de75d33022ed86984c4..7a9889333d6a82a58731a54c612bddfa } @Override -@@ -234,10 +241,7 @@ public class SectionPos extends Vec3i { +@@ -236,10 +241,7 @@ public class SectionPos extends Vec3i { } - public static Stream cube(SectionPos center, int radius) { -- int sectionX = center.x(); -- int sectionY = center.y(); -- int sectionZ = center.z(); -- return betweenClosedStream(sectionX - radius, sectionY - radius, sectionZ - radius, sectionX + radius, sectionY + radius, sectionZ + radius); + public static Stream cube(final SectionPos center, final int radius) { +- int x = center.x(); +- int y = center.y(); +- int z = center.z(); +- return betweenClosedStream(x - radius, y - radius, z - radius, x + radius, y + radius, z + radius); + return betweenClosedStream(center.getX() - radius, center.getY() - radius, center.getZ() - radius, center.getX() + radius, center.getY() + radius, center.getZ() + radius); // Paper - simplify/inline } - public static Stream aroundChunk(ChunkPos chunkPos, int radius, int minY, int maxY) { + public static Stream aroundChunk(final ChunkPos center, final int radius, final int minSection, final int maxSection) { diff --git a/paper-server/patches/features/0012-Remove-streams-from-hot-code.patch b/paper-server/patches/features/0012-Remove-streams-from-hot-code.patch index 6ada9c1dfda3..1676c4705eaf 100644 --- a/paper-server/patches/features/0012-Remove-streams-from-hot-code.patch +++ b/paper-server/patches/features/0012-Remove-streams-from-hot-code.patch @@ -7,71 +7,67 @@ Co-authored-by: Bjarne Koll Co-authored-by: Spottedleaf diff --git a/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/net/minecraft/world/entity/ai/behavior/GateBehavior.java -index c215d97c24e6501e1a48a76fc08bf48ff4dfe462..bd31d1cac0d022a72bd536c41d1ef811886e7068 100644 +index 135d932a31a557ff06643fb563262423d48cd540..9fb025b05cacc901d72d93c16cf2b7e48d4dd9a6 100644 --- a/net/minecraft/world/entity/ai/behavior/GateBehavior.java +++ b/net/minecraft/world/entity/ai/behavior/GateBehavior.java -@@ -57,7 +57,7 @@ public class GateBehavior implements BehaviorControl - if (this.hasRequiredMemories(entity)) { +@@ -69,7 +69,7 @@ public class GateBehavior implements BehaviorControl + if (this.hasRequiredMemories(body)) { this.status = Behavior.Status.RUNNING; this.orderPolicy.apply(this.behaviors); -- this.runningPolicy.apply(this.behaviors.stream(), level, entity, gameTime); -+ this.runningPolicy.apply(this.behaviors, level, entity, gameTime); // Paper - Perf: Remove streams from hot code +- this.runningPolicy.apply(this.behaviors.stream(), level, body, timestamp); ++ this.runningPolicy.apply(this.behaviors, level, body, timestamp); // Paper - Perf: Remove streams from hot code return true; } else { return false; -@@ -66,10 +66,13 @@ public class GateBehavior implements BehaviorControl +@@ -78,8 +78,14 @@ public class GateBehavior implements BehaviorControl @Override - public final void tickOrStop(ServerLevel level, E entity, long gameTime) { -- this.behaviors -- .stream() -- .filter(behavior -> behavior.getStatus() == Behavior.Status.RUNNING) -- .forEach(behavior -> behavior.tickOrStop(level, entity, gameTime)); + public final void tickOrStop(final ServerLevel level, final E body, final long timestamp) { +- this.behaviors.stream().filter(goal -> goal.getStatus() == Behavior.Status.RUNNING).forEach(goal -> goal.tickOrStop(level, body, timestamp)); +- if (this.behaviors.stream().noneMatch(g -> g.getStatus() == Behavior.Status.RUNNING)) { + // Paper start - Perf: Remove streams from hot code + for (final BehaviorControl behavior : this.behaviors) { + if (behavior.getStatus() == Behavior.Status.RUNNING) { -+ behavior.tickOrStop(level, entity, gameTime); ++ behavior.tickOrStop(level, body, timestamp); + } + } + // Paper end - Perf: Remove streams from hot code - if (this.behaviors.stream().noneMatch(behavior -> behavior.getStatus() == Behavior.Status.RUNNING)) { - this.doStop(level, entity, gameTime); ++ if (this.behaviors.stream().noneMatch(behavior -> behavior.getStatus() == Behavior.Status.RUNNING)) { + this.doStop(level, body, timestamp); } -@@ -78,11 +81,16 @@ public class GateBehavior implements BehaviorControl + } +@@ -87,8 +93,16 @@ public class GateBehavior implements BehaviorControl @Override - public final void doStop(ServerLevel level, E entity, long gameTime) { + public final void doStop(final ServerLevel level, final E body, final long timestamp) { this.status = Behavior.Status.STOPPED; -- this.behaviors -- .stream() -- .filter(behavior -> behavior.getStatus() == Behavior.Status.RUNNING) -- .forEach(behavior -> behavior.doStop(level, entity, gameTime)); -- this.exitErasedMemories.forEach(entity.getBrain()::eraseMemory); +- this.behaviors.stream().filter(goal -> goal.getStatus() == Behavior.Status.RUNNING).forEach(goal -> goal.doStop(level, body, timestamp)); +- this.exitErasedMemories.forEach(body.getBrain()::eraseMemory); + // Paper start - Perf: Remove streams from hot code + for (final BehaviorControl behavior : this.behaviors) { + if (behavior.getStatus() == Behavior.Status.RUNNING) { -+ behavior.doStop(level, entity, gameTime); ++ behavior.doStop(level, body, timestamp); + } + } + for (final MemoryModuleType exitErasedMemory : this.exitErasedMemories) { -+ entity.getBrain().eraseMemory(exitErasedMemory); ++ body.getBrain().eraseMemory(exitErasedMemory); + } + // Paper end - Perf: Remove streams from hot code } @Override -@@ -116,20 +124,30 @@ public class GateBehavior implements BehaviorControl +@@ -118,24 +132,36 @@ public class GateBehavior implements BehaviorControl public static enum RunningPolicy { RUN_ONE { + // Paper start - Perf: Remove streams from hot code @Override -- public void apply(Stream> behaviors, ServerLevel level, E owner, long gameTime) { -- behaviors.filter(behavior -> behavior.getStatus() == Behavior.Status.STOPPED) -- .filter(behavior -> behavior.tryStart(level, owner, gameTime)) -- .findFirst(); -+ public void apply(ShufflingList> behaviors, ServerLevel level, E owner, long gameTime) { + public void apply( +- final Stream> behaviors, final ServerLevel level, final E body, final long timestamp ++ final ShufflingList> behaviors, final ServerLevel level, final E body, final long timestamp + ) { +- behaviors.filter(goal -> goal.getStatus() == Behavior.Status.STOPPED).filter(goal -> goal.tryStart(level, body, timestamp)).findFirst(); + for (final BehaviorControl behavior : behaviors) { -+ if (behavior.getStatus() == Behavior.Status.STOPPED && behavior.tryStart(level, owner, gameTime)) { ++ if (behavior.getStatus() == Behavior.Status.STOPPED && behavior.tryStart(level, body, timestamp)) { + break; + } + } @@ -81,111 +77,110 @@ index c215d97c24e6501e1a48a76fc08bf48ff4dfe462..bd31d1cac0d022a72bd536c41d1ef811 TRY_ALL { + // Paper start - Perf: Remove streams from hot code @Override -- public void apply(Stream> behaviors, ServerLevel level, E owner, long gameTime) { -- behaviors.filter(behavior -> behavior.getStatus() == Behavior.Status.STOPPED).forEach(behavior -> behavior.tryStart(level, owner, gameTime)); -+ public void apply(ShufflingList> behaviors, ServerLevel level, E owner, long gameTime) { + public void apply( +- final Stream> behaviors, final ServerLevel level, final E body, final long timestamp ++ final ShufflingList> behaviors, final ServerLevel level, final E body, final long timestamp + ) { +- behaviors.filter(goal -> goal.getStatus() == Behavior.Status.STOPPED).forEach(goal -> goal.tryStart(level, body, timestamp)); + for (final BehaviorControl behavior : behaviors) { + if (behavior.getStatus() == Behavior.Status.STOPPED) { -+ behavior.tryStart(level, owner, gameTime); ++ behavior.tryStart(level, body, timestamp); + } + } + // Paper end - Perf: Remove streams from hot code } }; -- public abstract void apply(Stream> behaviors, ServerLevel level, E owner, long gameTime); -+ public abstract void apply(ShufflingList> behaviors, ServerLevel level, E owner, long gameTime); // Paper - Perf: Remove streams from hot code + public abstract void apply( +- final Stream> behaviors, final ServerLevel level, final E body, final long timestamp ++ final ShufflingList> behaviors, final ServerLevel level, final E body, final long timestamp // Paper - Perf: Remove streams from hot code + ); } } diff --git a/net/minecraft/world/entity/ai/gossip/GossipContainer.java b/net/minecraft/world/entity/ai/gossip/GossipContainer.java -index d93ef8d7ff04ffd3d7434ea6e2d476115203215b..425ca1931fb0a5c33ba7aaf4f639409c9fea836f 100644 +index 75233df622242db6c23f7a1a5812bf25942bca41..133aab614b102419ec731e901858c0347076bae9 100644 --- a/net/minecraft/world/entity/ai/gossip/GossipContainer.java +++ b/net/minecraft/world/entity/ai/gossip/GossipContainer.java @@ -28,7 +28,7 @@ import net.minecraft.util.VisibleForDebug; public class GossipContainer { public static final Codec CODEC = GossipContainer.GossipEntry.CODEC .listOf() -- .xmap(GossipContainer::new, gossipContainer -> gossipContainer.unpack().toList()); -+ .xmap(GossipContainer::new, gossipContainer -> gossipContainer.decompress()); // Paper - Perf: Remove streams from hot code +- .xmap(GossipContainer::new, container -> container.unpack().toList()); ++ .xmap(GossipContainer::new, GossipContainer::decompress); // Paper - Perf: Remove streams from hot code public static final int DISCARD_THRESHOLD = 2; public final Map gossips = new HashMap<>(); @@ -65,8 +65,22 @@ public class GossipContainer { - return this.gossips.entrySet().stream().flatMap(entry -> entry.getValue().unpack(entry.getKey())); + return this.gossips.entrySet().stream().flatMap(e -> e.getValue().unpack(e.getKey())); } + // Paper start - Perf: Remove streams from hot code + private List decompress() { -+ final List list = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); -+ for (final Map.Entry entry : this.gossips.entrySet()) { -+ for (final GossipContainer.GossipEntry cur : entry.getValue().decompress(entry.getKey())) { -+ if (cur.weightedValue() != 0) { -+ list.add(cur); ++ final List entries = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); ++ for (final Map.Entry gossips : this.gossips.entrySet()) { ++ for (final GossipContainer.GossipEntry entry : gossips.getValue().decompress(gossips.getKey())) { ++ if (entry.weightedValue() != 0) { ++ entries.add(entry); + } + } + } -+ return list; ++ return entries; + } + // Paper end - Perf: Remove streams from hot code + - private Collection selectGossipsForTransfer(RandomSource random, int amount) { -- List list = this.unpack().toList(); -+ List list = this.decompress(); // Paper - Perf: Remove streams from hot code - if (list.isEmpty()) { + private Collection selectGossipsForTransfer(final RandomSource random, final int maxCount) { +- List entries = this.unpack().toList(); ++ List entries = this.decompress(); // Paper - Perf: Remove streams from hot code + if (entries.isEmpty()) { return Collections.emptyList(); } else { -@@ -176,12 +190,23 @@ public class GossipContainer { - final Object2IntMap entries = new Object2IntOpenHashMap<>(); +@@ -176,7 +190,23 @@ public class GossipContainer { + private final Object2IntMap entries = new Object2IntOpenHashMap<>(); - public int weightedValue(Predicate gossipType) { -- return this.entries -- .object2IntEntrySet() -- .stream() -- .filter(gossip -> gossipType.test(gossip.getKey())) -- .mapToInt(gossip -> gossip.getIntValue() * gossip.getKey().weight) -- .sum(); + public int weightedValue(final Predicate types) { +- return this.entries.object2IntEntrySet().stream().filter(e -> types.test(e.getKey())).mapToInt(e -> e.getIntValue() * e.getKey().weight).sum(); + // Paper start - Perf: Remove streams from hot code + int weight = 0; -+ for (Object2IntMap.Entry entry : entries.object2IntEntrySet()) { -+ if (gossipType.test(entry.getKey())) { ++ for (Object2IntMap.Entry entry : this.entries.object2IntEntrySet()) { ++ if (types.test(entry.getKey())) { + weight += entry.getIntValue() * entry.getKey().weight; + } + } + return weight; + } + -+ public List decompress(UUID uuid) { -+ List list = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); -+ for (Object2IntMap.Entry entry : entries.object2IntEntrySet()) { -+ list.add(new GossipContainer.GossipEntry(uuid, entry.getKey(), entry.getIntValue())); ++ public List decompress(final UUID target) { ++ List entries = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); ++ for (Object2IntMap.Entry entry : this.entries.object2IntEntrySet()) { ++ entries.add(new GossipContainer.GossipEntry(target, entry.getKey(), entry.getIntValue())); + } -+ return list; ++ return entries; + // Paper end - Perf: Remove streams from hot code } - public Stream unpack(UUID identifier) { + public Stream unpack(final UUID target) { diff --git a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java -index 38873e56e95dc772b184e4271f7af1fb411ac9f8..09fd13e2d958da8326276c4dadf25bf488aff5ac 100644 +index e08da5b08dca5849fe0fd6d0ed237fbd36881e15..8aeea55380462421cde43afb4b2fb0ce44b65195 100644 --- a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java +++ b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java @@ -24,13 +24,17 @@ public class NearestItemSensor extends Sensor { @Override - protected void doTick(ServerLevel level, Mob entity) { - Brain brain = entity.getBrain(); -- List entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> true); -+ List entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(level, itemEntity.getItem())); // Paper - Perf: Move predicate into getEntities - entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr)); -- Optional optional = entitiesOfClass.stream() -- .filter(itemEntity -> entity.wantsToPickUp(level, itemEntity.getItem())) -- .filter(itemEntity -> itemEntity.closerThan(entity, 32.0)) -- .filter(entity::hasLineOfSight) + protected void doTick(final ServerLevel level, final Mob body) { + Brain brain = body.getBrain(); +- List items = level.getEntitiesOfClass(ItemEntity.class, body.getBoundingBox().inflate(32.0, 16.0, 32.0), item -> true); ++ List items = level.getEntitiesOfClass(ItemEntity.class, body.getBoundingBox().inflate(32.0, 16.0, 32.0), item -> item.closerThan(body, MAX_DISTANCE_TO_WANTED_ITEM) && body.wantsToPickUp(level, item.getItem())); // Paper - Perf: Move predicate into getEntities + items.sort(Comparator.comparingDouble(body::distanceToSqr)); +- Optional nearestVisibleLovedItem = items.stream() +- .filter(itemEntity -> body.wantsToPickUp(level, itemEntity.getItem())) +- .filter(itemEntity -> itemEntity.closerThan(body, 32.0)) +- .filter(body::hasLineOfSight) - .findFirst(); -- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, optional); +- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, nearestVisibleLovedItem); + // Paper start - Perf: remove streams from hot code + ItemEntity nearest = null; -+ for (final ItemEntity itemEntity : entitiesOfClass) { -+ if (entity.hasLineOfSight(itemEntity)) { // Paper - Perf: Move predicate into getEntities -+ nearest = itemEntity; ++ for (final ItemEntity item : items) { ++ if (body.hasLineOfSight(item)) { // Paper - Perf: Move predicate into getEntities ++ nearest = item; + break; + } + } diff --git a/paper-server/patches/features/0013-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch b/paper-server/patches/features/0013-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch index ca4e5337cdbf..b1512ccfe900 100644 --- a/paper-server/patches/features/0013-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch +++ b/paper-server/patches/features/0013-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch @@ -16,135 +16,120 @@ This lets us get faster foreach iteration, as well as avoids map lookups on the values when needed. diff --git a/net/minecraft/world/level/pathfinder/PathFinder.java b/net/minecraft/world/level/pathfinder/PathFinder.java -index d1cd00f0842f6fba1fa188de59bf111a4235bf8a..168b475b38b2872b27c1ab15f6846323ac16dd2c 100644 +index bb36ebf3a57cf1de98506a6391d3273b0f8e0f76..7564a67d2d63c3a7f95e1d6008e7308620653774 100644 --- a/net/minecraft/world/level/pathfinder/PathFinder.java +++ b/net/minecraft/world/level/pathfinder/PathFinder.java -@@ -47,28 +47,32 @@ public class PathFinder { - if (start == null) { +@@ -54,8 +54,12 @@ public class PathFinder { + if (from == null) { return null; } else { -- Map map = targets.stream() +- Map tos = targets.stream() - .collect(Collectors.toMap(pos -> this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), Function.identity())); + // Paper start - Perf: remove streams and optimize collection -+ List> map = Lists.newArrayList(); ++ List> tos = Lists.newArrayList(); + for (BlockPos pos : targets) { -+ map.add(new java.util.AbstractMap.SimpleEntry<>(this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos)); ++ tos.add(new java.util.AbstractMap.SimpleEntry<>(this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos)); + } + // Paper end - Perf: remove streams and optimize collection - Path path = this.findPath(start, map, maxRange, reachRange, maxVisitedNodesMultiplier); + Path path = this.findPath(from, tos, maxPathLength, reachRange, maxVisitedNodesMultiplier); this.nodeEvaluator.done(); return path; - } +@@ -63,12 +67,12 @@ public class PathFinder { } -- private @Nullable Path findPath(Node node, Map targets, float maxRange, int reachRange, float maxVisitedNodesMultiplier) { -+ private @Nullable Path findPath(Node node, List> positions, float maxRange, int reachRange, float maxVisitedNodesMultiplier) { // Paper - optimize collection - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("find_path"); - profilerFiller.markForCharting(MetricCategory.PATH_FINDING); -- Set set = targets.keySet(); -+ // Set set = targetPositions.keySet(); // Paper - unused - node.g = 0.0F; -- node.h = this.getBestH(node, set); -+ node.h = this.getBestH(node, positions); // Paper - optimize collection - node.f = node.h; - this.openSet.clear(); - this.openSet.insert(node); - boolean asBoolean = this.captureDebug.getAsBoolean(); - Set set1 = asBoolean ? new HashSet<>() : Set.of(); - int i = 0; -- Set set2 = Sets.newHashSetWithExpectedSize(set.size()); -+ List> entryList = Lists.newArrayListWithExpectedSize(positions.size()); // Paper - optimize collection - int i1 = (int)(this.maxVisitedNodes * maxVisitedNodesMultiplier); + private @Nullable Path findPath( +- final Node from, final Map targetMap, final float maxPathLength, final int reachRange, final float maxVisitedNodesMultiplier ++ final Node from, final List> targets, final float maxPathLength, final int reachRange, final float maxVisitedNodesMultiplier // Paper - optimize collection + ) { + ProfilerFiller profiler = Profiler.get(); + profiler.push("find_path"); + profiler.markForCharting(MetricCategory.PATH_FINDING); +- Set targets = targetMap.keySet(); ++ //Set targets = targetMap.keySet(); // Paper - unused + from.g = 0.0F; + from.h = this.getBestH(from, targets); + from.f = from.h; +@@ -77,7 +81,7 @@ public class PathFinder { + boolean captureDebug = this.captureDebug.getAsBoolean(); + Set closedSet = captureDebug ? new HashSet<>() : Set.of(); + int count = 0; +- Set reachedTargets = Sets.newHashSetWithExpectedSize(targets.size()); ++ List> reachedTargets = Lists.newArrayListWithExpectedSize(targets.size()); // Paper - optimize collection + int maxVisitedNodesAdjusted = (int)(this.maxVisitedNodes * maxVisitedNodesMultiplier); while (!this.openSet.isEmpty()) { -@@ -79,14 +83,18 @@ public class PathFinder { - Node node1 = this.openSet.pop(); - node1.closed = true; +@@ -88,10 +92,14 @@ public class PathFinder { + Node current = this.openSet.pop(); + current.closed = true; -- for (Target target : set) { +- for (Target target : targets) { + // Paper start - optimize collection -+ for (int positionIndex = 0, size = positions.size(); positionIndex < size; positionIndex++) { -+ final Map.Entry entry = positions.get(positionIndex); ++ for (int positionIndex = 0, size = targets.size(); positionIndex < size; positionIndex++) { ++ final Map.Entry entry = targets.get(positionIndex); + Target target = entry.getKey(); - if (node1.distanceManhattan(target) <= reachRange) { + if (current.distanceManhattan(target) <= reachRange) { target.setReached(); -- set2.add(target); -+ entryList.add(entry); +- reachedTargets.add(target); ++ reachedTargets.add(entry); + // Paper end - Perf: remove streams and optimize collection } } -- if (!set2.isEmpty()) { -+ if (!entryList.isEmpty()) { // Paper - Perf: remove streams and optimize collection; rename - break; - } - -@@ -105,7 +113,7 @@ public class PathFinder { - if (node2.walkedDistance < maxRange && (!node2.inOpenSet() || f1 < node2.g)) { - node2.cameFrom = node1; - node2.g = f1; -- node2.h = this.getBestH(node2, set) * 1.5F; -+ node2.h = this.getBestH(node2, positions) * 1.5F; // Paper - Perf: remove streams and optimize collection - if (node2.inOpenSet()) { - this.openSet.changeCost(node2, node2.g + node2.h); - } else { -@@ -117,34 +125,41 @@ public class PathFinder { +@@ -126,34 +134,40 @@ public class PathFinder { } } -- Optional optional = !set2.isEmpty() -- ? set2.stream() -- .map(pathfinder -> this.reconstructPath(pathfinder.getBestNode(), targets.get(pathfinder), true)) +- Optional optPath = !reachedTargets.isEmpty() +- ? reachedTargets.stream() +- .map(targetx -> this.reconstructPath(targetx.getBestNode(), targetMap.get(targetx), true)) - .min(Comparator.comparingInt(Path::getNodeCount)) -- : set.stream() -- .map(pathfinder -> this.reconstructPath(pathfinder.getBestNode(), targets.get(pathfinder), false)) +- : targets.stream() +- .map(targetx -> this.reconstructPath(targetx.getBestNode(), targetMap.get(targetx), false)) - .min(Comparator.comparingDouble(Path::getDistToTarget).thenComparingInt(Path::getNodeCount)); -+ // Paper start - Perf: remove streams and optimize collection + Path best = null; -+ boolean entryListIsEmpty = entryList.isEmpty(); ++ boolean entryListIsEmpty = reachedTargets.isEmpty(); + Comparator comparator = entryListIsEmpty + ? Comparator.comparingInt(Path::getNodeCount) + : Comparator.comparingDouble(Path::getDistToTarget).thenComparingInt(Path::getNodeCount); -+ for (Map.Entry entry : entryListIsEmpty ? positions : entryList) { ++ for (Map.Entry entry : entryListIsEmpty ? targets : reachedTargets) { + Path path = this.reconstructPath(entry.getKey().getBestNode(), entry.getValue(), !entryListIsEmpty); + if (best == null || comparator.compare(path, best) < 0) { + best = path; + } + } - profilerFiller.pop(); -- if (optional.isEmpty()) { + profiler.pop(); +- if (optPath.isEmpty()) { - return null; - } else { -- Path path = optional.get(); -- if (asBoolean) { -- path.setDebug(this.openSet.getHeap(), set1.toArray(Node[]::new), set); -+ if(asBoolean && best != null) { +- Path path = optPath.get(); +- if (captureDebug) { +- path.setDebug(this.openSet.getHeap(), closedSet.toArray(Node[]::new), targets); ++ if (captureDebug && best != null) { + Set set = Sets.newHashSet(); -+ for(Map.Entry entry : positions) { ++ for(Map.Entry entry : targets) { + set.add(entry.getKey()); } - - return path; -+ best.setDebug(this.openSet.getHeap(), set1.toArray(Node[]::new), set); ++ best.setDebug(this.openSet.getHeap(), closedSet.toArray(Node[]::new), set); } + return best; + // Paper end - Perf: remove streams and optimize collection } - protected float distance(Node first, Node second) { - return first.distanceTo(second); + protected float distance(final Node from, final Node to) { + return from.distanceTo(to); } -- private float getBestH(Node node, Set targets) { -+ private float getBestH(Node node, List> targets) { // Paper - Perf: remove streams and optimize collection; Set -> List> - float f = Float.MAX_VALUE; +- private float getBestH(final Node from, final Set targets) { ++ private float getBestH(final Node from, final List> targets) { // Paper - Perf: remove streams and optimize collection; Set -> List> + float bestH = Float.MAX_VALUE; - for (Target target : targets) { + // Paper start - Perf: remove streams and optimize collection + for (int i = 0, targetsSize = targets.size(); i < targetsSize; i++) { + final Target target = targets.get(i).getKey(); + // Paper end - Perf: remove streams and optimize collection - float f1 = node.distanceTo(target); - target.updateBest(f1, node); - f = Math.min(f1, f); + float h = from.distanceTo(target); + target.updateBest(h, from); + bestH = Math.min(h, bestH); diff --git a/paper-server/patches/features/0014-Fix-entity-tracker-desync-when-new-players-are-added.patch b/paper-server/patches/features/0014-Fix-entity-tracker-desync-when-new-players-are-added.patch index 10cb5e053233..cee2bb03b86a 100644 --- a/paper-server/patches/features/0014-Fix-entity-tracker-desync-when-new-players-are-added.patch +++ b/paper-server/patches/features/0014-Fix-entity-tracker-desync-when-new-players-are-added.patch @@ -29,7 +29,7 @@ client will not tick the entity and thus not lerp the entity from its old position to its new position. diff --git a/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java b/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java -index 2a86ca1de6920017be6cd4a3a5251e892067f07c..f571ff64ae1007f8f0632febd2dcca7056a29b3c 100644 +index cff05f64af7f425b77cbbd7680c5cc592faa798c..0ab4080c8e30d5ee14ce5fcbac64b0316ea6a949 100644 --- a/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java +++ b/net/minecraft/network/protocol/game/ClientboundAddEntityPacket.java @@ -38,9 +38,11 @@ public class ClientboundAddEntityPacket implements Packet= 1 || Math.abs(b1 - this.lastSentXRot) >= 1; + byte yRotn = Mth.packDegrees(this.entity.getYRot()); + byte xRotn = Mth.packDegrees(this.entity.getXRot()); + boolean shouldSendRotation = Math.abs(yRotn - this.lastSentYRot) >= 1 || Math.abs(xRotn - this.lastSentXRot) >= 1; @@ -164,7 +171,7 @@ public class ServerEntity { - long l1 = this.positionCodec.encodeY(vec3); - long l2 = this.positionCodec.encodeZ(vec3); - boolean flag5 = l < -32768L || l > 32767L || l1 < -32768L || l1 > 32767L || l2 < -32768L || l2 > 32767L; + long ya = this.positionCodec.encodeY(currentPosition); + long za = this.positionCodec.encodeZ(currentPosition); + boolean deltaTooBig = xa < -32768L || xa > 32767L || ya < -32768L || ya > 32767L || za < -32768L || za > 32767L; - if (this.entity.getRequiresPrecisePosition() + if (this.forceStateResync || this.entity.getRequiresPrecisePosition() // Paper - fix desync when a player is added to the tracker - || flag5 + || deltaTooBig || this.teleportDelay > 400 || this.wasRiding @@ -233,6 +240,7 @@ public class ServerEntity { diff --git a/paper-server/patches/features/0015-Eigencraft-redstone-implementation.patch b/paper-server/patches/features/0015-Eigencraft-redstone-implementation.patch index ef84f13c5404..d79e6b852df3 100644 --- a/paper-server/patches/features/0015-Eigencraft-redstone-implementation.patch +++ b/paper-server/patches/features/0015-Eigencraft-redstone-implementation.patch @@ -978,11 +978,11 @@ index 0000000000000000000000000000000000000000..ff747a1ecdf3c888bca0d69de4f85dcd + } +} diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java -index 417e12d3dfabdfce40d6a53d217b86a39defc553..d7ff98f4ecadd37f22d0a38d94a5975db60d6285 100644 +index 0ee7bc3a10896a4f59fa94df158bce741aea0464..a677c3bfddfa0b7947394a3975ec43794be07d7e 100644 --- a/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/net/minecraft/world/level/block/RedStoneWireBlock.java -@@ -264,6 +264,60 @@ public class RedStoneWireBlock extends Block { - return state.isFaceSturdy(level, pos, Direction.UP) || state.is(Blocks.HOPPER); +@@ -269,6 +269,60 @@ public class RedStoneWireBlock extends Block { + return relativeState.isFaceSturdy(level, relativePos, Direction.UP) || relativeState.is(Blocks.HOPPER); } + // Paper start - Optimize redstone @@ -1039,19 +1039,19 @@ index 417e12d3dfabdfce40d6a53d217b86a39defc553..d7ff98f4ecadd37f22d0a38d94a5975d + } + // Paper end + - private void updatePowerStrength(Level level, BlockPos pos, BlockState state, @Nullable Orientation orientation, boolean updateShape) { - if (useExperimentalEvaluator(level)) { - new ExperimentalRedstoneWireEvaluator(this).updatePowerStrength(level, pos, state, orientation, updateShape); -@@ -292,7 +346,7 @@ public class RedStoneWireBlock extends Block { + private void updatePowerStrength( + final Level level, + final BlockPos pos, +@@ -303,7 +357,7 @@ public class RedStoneWireBlock extends Block { @Override - protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) { if (!oldState.is(state.getBlock()) && !level.isClientSide()) { - this.updatePowerStrength(level, pos, state, null, true); + this.updateSurroundingRedstone(level, pos, state, null, true); // Paper - Optimize redstone for (Direction direction : Direction.Plane.VERTICAL) { level.updateNeighborsAt(pos.relative(direction), this); -@@ -309,7 +363,7 @@ public class RedStoneWireBlock extends Block { +@@ -320,7 +374,7 @@ public class RedStoneWireBlock extends Block { level.updateNeighborsAt(pos.relative(direction), this); } @@ -1060,9 +1060,9 @@ index 417e12d3dfabdfce40d6a53d217b86a39defc553..d7ff98f4ecadd37f22d0a38d94a5975d this.updateNeighborsOfNeighboringWires(level, pos); } } -@@ -334,7 +388,7 @@ public class RedStoneWireBlock extends Block { +@@ -347,7 +401,7 @@ public class RedStoneWireBlock extends Block { if (!level.isClientSide()) { - if (neighborBlock != this || !useExperimentalEvaluator(level)) { + if (block != this || !useExperimentalEvaluator(level)) { if (state.canSurvive(level, pos)) { - this.updatePowerStrength(level, pos, state, orientation, false); + this.updateSurroundingRedstone(level, pos, state, orientation, false); // Paper - Optimize redstone @@ -1070,15 +1070,15 @@ index 417e12d3dfabdfce40d6a53d217b86a39defc553..d7ff98f4ecadd37f22d0a38d94a5975d dropResources(state, level, pos); level.removeBlock(pos, false); diff --git a/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java b/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java -index 82a45a598cd3d7520b15bac9b47117ef7b1f98ba..53b8df8e6dd379126af3e1feb2220e222430ce94 100644 +index fe52dd304bc03b172654b89acb382c7f410d7946..afdcbb9e5a4c859d82bfb33a1d253514c17eed39 100644 --- a/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java +++ b/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java -@@ -45,7 +45,7 @@ public class DefaultRedstoneWireEvaluator extends RedstoneWireEvaluator { +@@ -47,7 +47,7 @@ public class DefaultRedstoneWireEvaluator extends RedstoneWireEvaluator { } } -- private int calculateTargetStrength(Level level, BlockPos pos) { -+ public int calculateTargetStrength(Level level, BlockPos pos) { // Paper - Optimize redstone +- private int calculateTargetStrength(final Level level, final BlockPos pos) { ++ public int calculateTargetStrength(final Level level, final BlockPos pos) { // Paper - Optimize redstone int blockSignal = this.getBlockSignal(level, pos); - return blockSignal == 15 ? blockSignal : Math.max(blockSignal, this.getIncomingWireSignal(level, pos)); + return blockSignal == Redstone.SIGNAL_MAX ? blockSignal : Math.max(blockSignal, this.getIncomingWireSignal(level, pos)); } diff --git a/paper-server/patches/features/0016-Add-Alternate-Current-redstone-implementation.patch b/paper-server/patches/features/0016-Add-Alternate-Current-redstone-implementation.patch index 68867b6dfbd7..a758eba78e6d 100644 --- a/paper-server/patches/features/0016-Add-Alternate-Current-redstone-implementation.patch +++ b/paper-server/patches/features/0016-Add-Alternate-Current-redstone-implementation.patch @@ -2326,18 +2326,18 @@ index 0000000000000000000000000000000000000000..298076a0db4e6ee6e4775ac43bf749d9 + } +} diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index bdbc15bfbbfeae2bc5b884e300b86cb25c88840f..ef7e24716c2fc6a643d107cadcf743f80b39af41 100644 +index ff0c463a64255af4eb417b8fa84d0ba38397d9fa..18eb79f76fb558a1ba9f989f59d2f273975be3a1 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -227,6 +227,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - public final net.minecraft.server.level.progress.LevelLoadListener levelLoadListener; +@@ -232,6 +232,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + public final WorldGenSettings worldGenSettings; public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent + private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current) @Override public @Nullable LevelChunk getChunkIfLoaded(int x, int z) { -@@ -2696,6 +2697,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -2766,6 +2767,13 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet return this.debugSynchronizers; } @@ -2348,14 +2348,14 @@ index bdbc15bfbbfeae2bc5b884e300b86cb25c88840f..ef7e24716c2fc6a643d107cadcf743f8 + } + // Paper end - optimize redstone (Alternate Current) + - public boolean isAllowedToEnterPortal(Level level) { - return level.dimension() != Level.NETHER || this.getGameRules().get(GameRules.ALLOW_ENTERING_NETHER_USING_PORTALS); + public boolean isAllowedToEnterPortal(final Level toLevel) { + 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 9e88d83b31129cc853afe60d841838852a33fc45..935aacd607bb1960bc5f01b3fd025603edd1dbd6 100644 +index 83041532ee49be800c66995ad64d570270349fb6..ae9bc7cb3ebb245d78e50b69702facec8f0b75ac 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java -@@ -2049,6 +2049,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -2151,6 +2151,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl return this.palettedContainerFactory; } @@ -2374,11 +2374,11 @@ index 9e88d83b31129cc853afe60d841838852a33fc45..935aacd607bb1960bc5f01b3fd025603 NONE("none"), BLOCK("block"), diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java -index d7ff98f4ecadd37f22d0a38d94a5975db60d6285..6d98bc37d88459d1e0a171b52bbff5810d775c38 100644 +index a677c3bfddfa0b7947394a3975ec43794be07d7e..ed471efa3ab705de62aec81c3940c1b9c5acdbc5 100644 --- a/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/net/minecraft/world/level/block/RedStoneWireBlock.java -@@ -264,7 +264,7 @@ public class RedStoneWireBlock extends Block { - return state.isFaceSturdy(level, pos, Direction.UP) || state.is(Blocks.HOPPER); +@@ -269,7 +269,7 @@ public class RedStoneWireBlock extends Block { + return relativeState.isFaceSturdy(level, relativePos, Direction.UP) || relativeState.is(Blocks.HOPPER); } - // Paper start - Optimize redstone @@ -2386,9 +2386,9 @@ index d7ff98f4ecadd37f22d0a38d94a5975db60d6285..6d98bc37d88459d1e0a171b52bbff581 // The bulk of the new functionality is found in RedstoneWireTurbo.java io.papermc.paper.redstone.RedstoneWireTurbo turbo = new io.papermc.paper.redstone.RedstoneWireTurbo(this); -@@ -346,7 +346,13 @@ public class RedStoneWireBlock extends Block { +@@ -357,7 +357,13 @@ public class RedStoneWireBlock extends Block { @Override - protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) { if (!oldState.is(state.getBlock()) && !level.isClientSide()) { - this.updateSurroundingRedstone(level, pos, state, null, true); // Paper - Optimize redstone + // Paper start - optimize redstone - replace call to updatePowerStrength @@ -2401,7 +2401,7 @@ index d7ff98f4ecadd37f22d0a38d94a5975db60d6285..6d98bc37d88459d1e0a171b52bbff581 for (Direction direction : Direction.Plane.VERTICAL) { level.updateNeighborsAt(pos.relative(direction), this); -@@ -363,7 +369,13 @@ public class RedStoneWireBlock extends Block { +@@ -374,7 +380,13 @@ public class RedStoneWireBlock extends Block { level.updateNeighborsAt(pos.relative(direction), this); } @@ -2416,9 +2416,9 @@ index d7ff98f4ecadd37f22d0a38d94a5975db60d6285..6d98bc37d88459d1e0a171b52bbff581 this.updateNeighborsOfNeighboringWires(level, pos); } } -@@ -386,9 +398,15 @@ public class RedStoneWireBlock extends Block { - @Override - protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { +@@ -399,9 +411,15 @@ public class RedStoneWireBlock extends Block { + final BlockState state, final Level level, final BlockPos pos, final Block block, final @Nullable Orientation orientation, final boolean movedByPiston + ) { if (!level.isClientSide()) { + // Paper start - optimize redstone (Alternate Current) + // Alternate Current handles breaking of redstone wires in the WireHandler. @@ -2426,7 +2426,7 @@ index d7ff98f4ecadd37f22d0a38d94a5975db60d6285..6d98bc37d88459d1e0a171b52bbff581 + level.getWireHandler().onWireUpdated(pos, state, orientation); + } else + // Paper end - optimize redstone (Alternate Current) - if (neighborBlock != this || !useExperimentalEvaluator(level)) { + if (block != this || !useExperimentalEvaluator(level)) { if (state.canSurvive(level, pos)) { - this.updateSurroundingRedstone(level, pos, state, orientation, false); // Paper - Optimize redstone + this.updateSurroundingRedstone(level, pos, state, orientation, false); // Paper - Optimize redstone (Eigencraft) @@ -2434,7 +2434,7 @@ index d7ff98f4ecadd37f22d0a38d94a5975db60d6285..6d98bc37d88459d1e0a171b52bbff581 dropResources(state, level, pos); level.removeBlock(pos, false); diff --git a/net/minecraft/world/level/redstone/ExperimentalRedstoneUtils.java b/net/minecraft/world/level/redstone/ExperimentalRedstoneUtils.java -index 413d6b66a7e368ebb590064b0d0dd47b64c68659..05e99653bb1f3b392be86f9fa9814b87ca301bca 100644 +index aee5036475c11f9419fdc7cde3bd96633499ceb5..9e85806bddd2295c373003b2a56396fe0be078df 100644 --- a/net/minecraft/world/level/redstone/ExperimentalRedstoneUtils.java +++ b/net/minecraft/world/level/redstone/ExperimentalRedstoneUtils.java @@ -16,6 +16,11 @@ public class ExperimentalRedstoneUtils { diff --git a/paper-server/patches/features/0017-Only-write-chunk-data-to-disk-if-it-serializes-witho.patch b/paper-server/patches/features/0017-Only-write-chunk-data-to-disk-if-it-serializes-witho.patch index d0845664ca6b..e8d5af42de9b 100644 --- a/paper-server/patches/features/0017-Only-write-chunk-data-to-disk-if-it-serializes-witho.patch +++ b/paper-server/patches/features/0017-Only-write-chunk-data-to-disk-if-it-serializes-witho.patch @@ -8,10 +8,10 @@ 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 944a01c34d19225d8366f6f97cbc3cacbb5d5498..f2ef716788c1ed1933139c1f53a6d73769362809 100644 +index d628e06a8dbf11b491b323c10a512fde22e72a12..fc712f9dbc893a6dee8ccecc54d0af07659dee62 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; +@@ -25,6 +25,7 @@ import org.slf4j.Logger; public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile { // Paper - rewrite chunk system private static final Logger LOGGER = LogUtils.getLogger(); @@ -19,7 +19,7 @@ index 944a01c34d19225d8366f6f97cbc3cacbb5d5498..f2ef716788c1ed1933139c1f53a6d737 private static final int SECTOR_BYTES = 4096; @VisibleForTesting protected static final int SECTOR_INTS = 1024; -@@ -452,6 +453,24 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche +@@ -453,6 +454,24 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche this.pos = pos; } @@ -43,9 +43,9 @@ index 944a01c34d19225d8366f6f97cbc3cacbb5d5498..f2ef716788c1ed1933139c1f53a6d737 + @Override public void close() throws IOException { - ByteBuffer byteBuffer = ByteBuffer.wrap(this.buf, 0, this.count); + 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 98b3f20c720d70293c6fcf77fdc081b44c71dd6b..3ca75289ac9eb05eadb74c87c0c94cbc026fd08f 100644 +index aa36e241d49725eb13f00e57d020d4da24add53d..5fa64ff30b8252f139ca7f77c14352c384c8c0e5 100644 --- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java @@ -16,6 +16,7 @@ import net.minecraft.world.level.ChunkPos; @@ -56,7 +56,7 @@ index 98b3f20c720d70293c6fcf77fdc081b44c71dd6b..3ca75289ac9eb05eadb74c87c0c94cbc public static final String ANVIL_EXTENSION = ".mca"; private static final int MAX_CACHE_SIZE = 256; public final Long2ObjectLinkedOpenHashMap regionCache = new Long2ObjectLinkedOpenHashMap<>(); -@@ -120,11 +121,24 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise +@@ -122,11 +123,24 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise // (and, the regionfile parameter is unused for writing until the write call) final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData writeData = ((ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile)regionFile).moonrise$startWrite(compound, pos); @@ -81,26 +81,27 @@ index 98b3f20c720d70293c6fcf77fdc081b44c71dd6b..3ca75289ac9eb05eadb74c87c0c94cbc return writeData; } -@@ -327,9 +341,17 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - if (chunkData == null) { - regionFile.clear(chunkPos); +@@ -331,9 +345,18 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + if (value == null) { + region.clear(pos); } else { -- try (DataOutputStream chunkDataOutputStream = regionFile.getChunkDataOutputStream(chunkPos)) { -+ DataOutputStream chunkDataOutputStream = regionFile.getChunkDataOutputStream(chunkPos); // Paper - Only write if successful -+ try { // Paper - Only write if successful - NbtIo.write(chunkData, chunkDataOutputStream); - regionFile.setOversized(chunkPos.x, chunkPos.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 -+ chunkDataOutputStream.close(); -+ } catch (final RegionFileSizeException ex) { -+ regionFile.clear(chunkPos); -+ final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024); -+ LOGGER.error("Chunk at (" + chunkPos.x + "," + chunkPos.z + ") in regionfile '" + regionFile.getPath().toString() + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk."); -+ // Paper end - don't write garbage data to disk if writing serialization fails +- try (DataOutputStream output = region.getChunkDataOutputStream(pos)) { ++ // Paper - Only write if successful ++ 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) { ++ region.clear(pos); ++ final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024); ++ LOGGER.error("Chunk at (" + pos.x() + "," + pos.z() + ") in regionfile '" + region.getPath().toString() + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk."); ++ // Paper end - don't write garbage data to disk if writing serialization fails } } } -@@ -372,4 +394,13 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise +@@ -376,4 +399,13 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise public RegionStorageInfo info() { return this.info; } diff --git a/paper-server/patches/features/0018-Entity-load-save-limit-per-chunk.patch b/paper-server/patches/features/0018-Entity-load-save-limit-per-chunk.patch index 74c39921a062..8aad6f7a88e2 100644 --- a/paper-server/patches/features/0018-Entity-load-save-limit-per-chunk.patch +++ b/paper-server/patches/features/0018-Entity-load-save-limit-per-chunk.patch @@ -9,7 +9,7 @@ defaults are only included for certain entites, this allows setting limits for any entity type. diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java -index c2363cfa5e93942fe837efd9f39478698f6d1a98..2dfd412344a0e57f25a08d9c65656a13113baa4e 100644 +index 0cfdbdee091f43ca011bc68f7479eb7b84801b09..8d9ce3d301d5f7e4106587ae00adb8dd5b8b6f88 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java @@ -116,8 +116,19 @@ public final class ChunkEntitySlices { @@ -18,7 +18,7 @@ index c2363cfa5e93942fe837efd9f39478698f6d1a98..2dfd412344a0e57f25a08d9c65656a13 final ListTag entitiesTag = new ListTag(); + final java.util.Map, Integer> savedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk try (final ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(ChunkAccess.problemPath(chunkPos), LOGGER)) { - for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x, chunkPos.z, entities)) { + for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x(), chunkPos.z(), entities)) { + // Paper start - Entity load/save limit per chunk + final EntityType entityType = entity.getType(); + final int saveLimit = world.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1); @@ -33,15 +33,15 @@ index c2363cfa5e93942fe837efd9f39478698f6d1a98..2dfd412344a0e57f25a08d9c65656a13 scopedCollector.forChild(entity.problemPath()), entity.registryAccess() ); diff --git a/net/minecraft/world/entity/EntityType.java b/net/minecraft/world/entity/EntityType.java -index 3cf2378a2ccf117fab9fc6fc60fcb0ecdf638d45..abccad13c2bb3a33e98ad8eb6d7f08c0ef021811 100644 +index 2cdbb5f9bace96a8c4ad7fc4de17678ec0db8a6c..6b19f8cd8655cc52a0d8deadb9ca9f24473637a4 100644 --- a/net/minecraft/world/entity/EntityType.java +++ b/net/minecraft/world/entity/EntityType.java -@@ -1615,7 +1615,18 @@ public class EntityType implements FeatureElement, EntityTypeT +@@ -1647,7 +1647,18 @@ public class EntityType implements EntityTypeTest, } - public static Stream loadEntitiesRecursive(ValueInput.ValueInputList input, Level level, EntitySpawnReason spawnReason) { + public static Stream loadEntitiesRecursive(final ValueInput.ValueInputList entities, final Level level, final EntitySpawnReason reason) { + final java.util.Map, Integer> loadedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk - return input.stream().mapMulti((valueInput, consumer) -> loadEntityRecursive(valueInput, level, spawnReason, entity -> { + return entities.stream().mapMulti((tag, output) -> loadEntityRecursive(tag, level, reason, entity -> { + // Paper start - Entity load/save limit per chunk + final EntityType entityType = entity.getType(); + final int saveLimit = level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1); @@ -52,21 +52,21 @@ index 3cf2378a2ccf117fab9fc6fc60fcb0ecdf638d45..abccad13c2bb3a33e98ad8eb6d7f08c0 + loadedEntityCounts.merge(entityType, 1, Integer::sum); + } + // Paper end - Entity load/save limit per chunk - consumer.accept(entity); + output.accept(entity); return entity; })); diff --git a/net/minecraft/world/level/chunk/storage/EntityStorage.java b/net/minecraft/world/level/chunk/storage/EntityStorage.java -index bfba76d8f442905e4d3a62b65edabbca12fdb10e..2ce9caafffc86fee82abf0a33423a172a8158e37 100644 +index 04045397dff89870316d0d374b9e472540c3298d..b1cd822c114b99a25fad2e649ca44596109aa0e5 100644 --- a/net/minecraft/world/level/chunk/storage/EntityStorage.java +++ b/net/minecraft/world/level/chunk/storage/EntityStorage.java @@ -95,7 +95,18 @@ public class EntityStorage implements EntityPersistentStorage { } else { - try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(ChunkAccess.problemPath(pos), LOGGER)) { - ListTag listTag = new ListTag(); + try (ProblemReporter.ScopedCollector reporter = new ProblemReporter.ScopedCollector(ChunkAccess.problemPath(pos), LOGGER)) { + ListTag entities = new ListTag(); + final java.util.Map, Integer> savedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk - entities.getEntities().forEach(entity -> { + chunk.getEntities().forEach(e -> { + // Paper start - Entity load/save limit per chunk -+ final EntityType entityType = entity.getType(); ++ final EntityType entityType = e.getType(); + final int saveLimit = this.level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1); + if (saveLimit > -1) { + if (savedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) { @@ -75,6 +75,6 @@ index bfba76d8f442905e4d3a62b65edabbca12fdb10e..2ce9caafffc86fee82abf0a33423a172 + savedEntityCounts.merge(entityType, 1, Integer::sum); + } + // Paper end - Entity load/save limit per chunk - TagValueOutput tagValueOutput = TagValueOutput.createWithContext(scopedCollector.forChild(entity.problemPath()), entity.registryAccess()); - if (entity.save(tagValueOutput)) { - CompoundTag compoundTag1 = tagValueOutput.buildResult(); + TagValueOutput output = TagValueOutput.createWithContext(reporter.forChild(e.problemPath()), e.registryAccess()); + if (e.save(output)) { + CompoundTag result = output.buildResult(); diff --git a/paper-server/patches/features/0019-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch b/paper-server/patches/features/0019-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch index 0865b24e7776..98f9ce1e98d2 100644 --- a/paper-server/patches/features/0019-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch +++ b/paper-server/patches/features/0019-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch @@ -10,10 +10,10 @@ 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 344e155420de9148cd402f626af11d4c523cacff..93776be67380f3f8993348b9cb81bc183aa93a56 100644 +index 27eeb842e6b22bd12b582379eaaa76b622dfca5e..d4606e3d95a98e745cd565c0c23b2a657249602a 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java -@@ -1467,7 +1467,7 @@ public final class MoonriseRegionFileIO { +@@ -1466,7 +1466,7 @@ public final class MoonriseRegionFileIO { public abstract void finishWrite(final int chunkX, final int chunkZ, final WriteData writeData) throws IOException; @@ -23,7 +23,7 @@ index 344e155420de9148cd402f626af11d4c523cacff..93776be67380f3f8993348b9cb81bc18 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 64a718c98f799c62a5bb28e1e8e5f66cc96c915d..666f2e967c99f78422c83fb20e1a3bf3efa7845e 100644 +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; @@ -51,14 +51,14 @@ index 64a718c98f799c62a5bb28e1e8e5f66cc96c915d..666f2e967c99f78422c83fb20e1a3bf3 + } + // Paper end - Attempt to recalculate regionfile header if it is corrupt + - public void force(int sectorOffset, int sectorCount) { - this.used.set(sectorOffset, sectorOffset + sectorCount); + 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 f2ef716788c1ed1933139c1f53a6d73769362809..c6adcb44bc96eaeba74bfc228a6a61765b1628e7 100644 +index fc712f9dbc893a6dee8ccecc54d0af07659dee62..b4a59b855b45018853e8122a5829a48ebd220a5f 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 +@@ -47,6 +47,363 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche @VisibleForTesting protected final RegionBitmap usedSectors = new RegionBitmap(); @@ -136,7 +136,7 @@ index f2ef716788c1ed1933139c1f53a6d73769362809..c6adcb44bc96eaeba74bfc228a6a6176 + } + + private static boolean inSameRegionfile(ChunkPos first, ChunkPos second) { -+ return (first.x & ~31) == (second.x & ~31) && (first.z & ~31) == (second.z & ~31); ++ return (first.x() & ~31) == (second.x() & ~31) && (first.z() & ~31) == (second.z() & ~31); + } + + // note: only call for CHUNK regionfiles @@ -182,10 +182,10 @@ index f2ef716788c1ed1933139c1f53a6d73769362809..c6adcb44bc96eaeba74bfc228a6a6176 + + ChunkPos chunkPos = SerializableChunkData.getChunkCoordinate(compound); + if (!inSameRegionfile(ourLowerLeftPosition, chunkPos)) { -+ LOGGER.error("Ignoring absolute chunk " + chunkPos + " in regionfile as it is not contained in the bounds of the regionfile '" + this.path.toAbsolutePath() + "'. It should be in regionfile (" + (chunkPos.x >> 5) + "," + (chunkPos.z >> 5) + ")"); ++ LOGGER.error("Ignoring absolute chunk " + chunkPos + " in regionfile as it is not contained in the bounds of the regionfile '" + this.path.toAbsolutePath() + "'. It should be in regionfile (" + (chunkPos.x() >> 5) + "," + (chunkPos.z() >> 5) + ")"); + continue; + } -+ int location = (chunkPos.x & 31) | ((chunkPos.z & 31) << 5); ++ int location = (chunkPos.x() & 31) | ((chunkPos.z() & 31) << 5); + + net.minecraft.nbt.CompoundTag otherCompound = compounds[location]; + @@ -194,17 +194,17 @@ index f2ef716788c1ed1933139c1f53a6d73769362809..c6adcb44bc96eaeba74bfc228a6a6176 + } + + // aikar oversized? -+ Path aikarOversizedFile = this.getOversizedFile(chunkPos.x, chunkPos.z); ++ 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); ++ 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 (" + chunkPos.x + "," + chunkPos.z + ") in regionfile " + this.path.toAbsolutePath() + ", oversized data for this chunk will be lost", ex); ++ LOGGER.error("Failed to read aikar oversized data for absolute chunk (" + chunkPos.x() + "," + chunkPos.z() + ") in regionfile " + this.path.toAbsolutePath() + ", oversized data for this chunk will be lost", ex); + // fall through, if we can't read aikar oversized we can't risk corrupting chunk data + } + } @@ -229,8 +229,8 @@ index f2ef716788c1ed1933139c1f53a6d73769362809..c6adcb44bc96eaeba74bfc228a6a6176 + RegionFileVersion[] oversizedCompressionTypes = new RegionFileVersion[32 * 32]; + + if (regionFiles != null) { -+ int lowerXBound = ourLowerLeftPosition.x; // inclusive -+ int lowerZBound = ourLowerLeftPosition.z; // inclusive ++ int lowerXBound = ourLowerLeftPosition.x(); // inclusive ++ int lowerZBound = ourLowerLeftPosition.z(); // inclusive + int upperXBound = lowerXBound + 32 - 1; // inclusive + int upperZBound = lowerZBound + 32 - 1; // inclusive + @@ -241,13 +241,13 @@ index f2ef716788c1ed1933139c1f53a6d73769362809..c6adcb44bc96eaeba74bfc228a6a6176 + continue; + } + -+ if ((oversizedCoords.x < lowerXBound || oversizedCoords.x > upperXBound) || (oversizedCoords.z < lowerZBound || oversizedCoords.z > upperZBound)) { ++ 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); ++ int location = (oversizedCoords.x() & 31) | ((oversizedCoords.z() & 31) << 5); + + byte[] chunkData; + try { @@ -422,52 +422,52 @@ index f2ef716788c1ed1933139c1f53a6d73769362809..c6adcb44bc96eaeba74bfc228a6a6176 // 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 { -@@ -74,6 +431,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche +@@ -75,6 +432,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche throw new IllegalArgumentException("Expected directory, got " + externalFileDir.toAbsolutePath()); } else { this.externalFileDir = externalFileDir; -+ this.canRecalcHeader = RegionFileStorage.isChunkDataFolder(this.externalFileDir); // Paper - add can recalc flag ++ 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); -@@ -94,11 +452,13 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche +@@ -95,11 +453,13 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche long size = Files.size(path); -- for (int i1 = 0; i1 < 1024; i1++) { +- 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 i1 = 0; i1 < 1024; i1++) { final int headerLocation = i1; // Paper - we expect this to be the header location - int i2 = this.offsets.get(i1); - if (i2 != 0) { -- int sectorNumber = getSectorNumber(i2); -- int numSectors = getNumSectors(i2); -+ final int sectorNumber = getSectorNumber(i2); // Paper - we expect this to be offset in file in sectors -+ int numSectors = getNumSectors(i2); // Paper - diff on change, we expect this to be sector length of region - watch out for reassignments ++ for (int i = 0; i < 1024; i++) { final int headerLocation = i; // Paper - 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 - 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 -@@ -109,18 +469,62 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche +@@ -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, i1, sectorNumber); -- this.offsets.put(i1, 0); -+ //this.offsets.put(i1, 0); // Paper - we catch this, but need it in the header for the summary change + 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, i1); -- this.offsets.put(i1, 0); -+ //this.offsets.put(i1, 0); // Paper - we catch this, but need it in the header for the summary change + 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, i1, sectorNumber); -- this.offsets.put(i1, 0); -+ //this.offsets.put(i1, 0); // Paper - we catch this, but need it in the header for the summary change + 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 (canRecalcHeader) { ++ if (this.canRecalcHeader) { + LOGGER.error("Detected invalid header for regionfile " + this.path.toAbsolutePath() + "! Recalculating header..."); + needsHeaderRecalc = true; + break; @@ -483,12 +483,12 @@ index f2ef716788c1ed1933139c1f53a6d73769362809..c6adcb44bc96eaeba74bfc228a6a6176 + 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 (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") in regionfile " + this.path.toAbsolutePath()); - } -+ if (failedToAllocate & !canRecalcHeader) { ++ } ++ if (failedToAllocate & !this.canRecalcHeader) { + // location = chunkX | (chunkZ << 5); + LOGGER.error("Detected invalid header for regionfile " + this.path.toAbsolutePath() + + "! Cannot recalculate, removing local chunk (" + (headerLocation & 31) + "," + (headerLocation >>> 5) + ") from header"); @@ -514,13 +514,13 @@ index f2ef716788c1ed1933139c1f53a6d73769362809..c6adcb44bc96eaeba74bfc228a6a6176 } } } -@@ -130,10 +534,35 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche +@@ -131,10 +535,34 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche } - private Path getExternalChunkPath(ChunkPos chunkPos) { -- String string = "c." + chunkPos.x + "." + chunkPos.z + ".mcc"; -+ String string = "c." + chunkPos.x + "." + chunkPos.z + ".mcc"; // Paper - diff on change - return this.externalFileDir.resolve(string); + 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 @@ -546,82 +546,81 @@ index f2ef716788c1ed1933139c1f53a6d73769362809..c6adcb44bc96eaeba74bfc228a6a6176 + return null; + } + } -+ // Paper end + - public synchronized @Nullable DataInputStream getChunkDataInputStream(ChunkPos chunkPos) throws IOException { - int offset = this.getOffset(chunkPos); + public synchronized @Nullable DataInputStream getChunkDataInputStream(final ChunkPos pos) throws IOException { + int offset = this.getOffset(pos); if (offset == 0) { -@@ -154,30 +583,67 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche - byteBuffer.flip(); - if (byteBuffer.remaining() < 5) { - LOGGER.error("Chunk {} header is truncated: expected {} but read {}", chunkPos, i, byteBuffer.remaining()); +@@ -155,30 +583,67 @@ 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(chunkPos); ++ return this.getChunkDataInputStream(pos); + } + // Paper end - recalculate header on regionfile corruption return null; } else { - int _int = byteBuffer.getInt(); - byte b = byteBuffer.get(); - if (_int == 0) { - LOGGER.warn("Chunk {} is allocated, but stream is missing", chunkPos); + int length = buffer.getInt(); + 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(chunkPos); ++ return this.getChunkDataInputStream(pos); + } + // Paper end - recalculate header on regionfile corruption return null; } else { - int i1 = _int - 1; - if (isExternalStreamChunk(b)) { - if (i1 != 0) { + int streamLength = length - 1; + 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(chunkPos); ++ return this.getChunkDataInputStream(pos); + } + // Paper end - recalculate header on regionfile corruption } -- return this.createExternalChunkInputStream(chunkPos, getExternalChunkVersion(b)); +- return this.createExternalChunkInputStream(pos, getExternalChunkVersion(versionId)); + // Paper start - recalculate header on regionfile corruption -+ final DataInputStream ret = this.createExternalChunkInputStream(chunkPos, getExternalChunkVersion(b)); ++ final DataInputStream ret = this.createExternalChunkInputStream(pos, getExternalChunkVersion(versionId)); + if (ret == null && this.canRecalcHeader && this.recalculateHeader()) { -+ return this.getChunkDataInputStream(chunkPos); ++ return this.getChunkDataInputStream(pos); + } + return ret; + // Paper end - recalculate header on regionfile corruption - } else if (i1 > byteBuffer.remaining()) { - LOGGER.error("Chunk {} stream is truncated: expected {} but read {}", chunkPos, i1, byteBuffer.remaining()); + } 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(chunkPos); ++ return this.getChunkDataInputStream(pos); + } + // Paper end - recalculate header on regionfile corruption return null; - } else if (i1 < 0) { - LOGGER.error("Declared size {} of chunk {} is negative", _int, chunkPos); + } 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(chunkPos); ++ return this.getChunkDataInputStream(pos); + } + // Paper end - recalculate header on regionfile corruption return null; } else { - JvmProfiler.INSTANCE.onRegionFileRead(this.info, chunkPos, this.version, i1); -- return this.createChunkInputStream(chunkPos, b, createStream(byteBuffer, i1)); + 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(chunkPos, b, createStream(byteBuffer, i1)); ++ final DataInputStream ret = this.createChunkInputStream(pos, versionId, createStream(buffer, streamLength)); + if (ret == null && this.canRecalcHeader && this.recalculateHeader()) { -+ return this.getChunkDataInputStream(chunkPos); ++ return this.getChunkDataInputStream(pos); + } + return ret; + // Paper end - recalculate header on regionfile corruption } } } -@@ -358,9 +824,14 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche +@@ -358,9 +823,14 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche } private ByteBuffer createExternalStub() { @@ -630,28 +629,24 @@ index f2ef716788c1ed1933139c1f53a6d73769362809..c6adcb44bc96eaeba74bfc228a6a6176 + } + private ByteBuffer createExternalStub(RegionFileVersion version) { + // Paper end - add version param - ByteBuffer byteBuffer = ByteBuffer.allocate(5); - byteBuffer.putInt(1); -- byteBuffer.put((byte)(this.version.getId() | 128)); -+ byteBuffer.put((byte)(version.getId() | 128)); - byteBuffer.flip(); - return byteBuffer; + ByteBuffer stub = ByteBuffer.allocate(5); + stub.putInt(1); +- stub.put((byte)(this.version.getId() | 128)); ++ stub.put((byte)(version.getId() | 128)); // 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 3ca75289ac9eb05eadb74c87c0c94cbc026fd08f..1e9e061ec694ad64fc2724b83a66dfec7e069a43 100644 +index 5fa64ff30b8252f139ca7f77c14352c384c8c0e5..eae4963b6d18c5f92871477b93f81c2c79331cd9 100644 --- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -@@ -24,6 +24,36 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise +@@ -24,6 +24,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; + -+ public static boolean isChunkDataFolder(Path path) { -+ return path.toFile().getName().equalsIgnoreCase("region"); -+ } -+ + @Nullable + public static ChunkPos getRegionFileCoordinates(Path file) { + String fileName = file.getFileName().toString(); @@ -678,7 +673,7 @@ index 3ca75289ac9eb05eadb74c87c0c94cbc026fd08f..1e9e061ec694ad64fc2724b83a66dfec // Paper start - rewrite chunk system private static final int REGION_SHIFT = 5; private static final int MAX_NON_EXISTING_CACHE = 1024 * 4; -@@ -170,12 +200,12 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise +@@ -172,12 +198,12 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise if (input == null) { return new ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData( @@ -693,7 +688,7 @@ index 3ca75289ac9eb05eadb74c87c0c94cbc026fd08f..1e9e061ec694ad64fc2724b83a66dfec ); if (!(input instanceof ca.spottedleaf.moonrise.patches.chunk_system.util.stream.ExternalChunkStreamMarker)) { -@@ -191,7 +221,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise +@@ -193,7 +219,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise } return new ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData( @@ -702,7 +697,7 @@ index 3ca75289ac9eb05eadb74c87c0c94cbc026fd08f..1e9e061ec694ad64fc2724b83a66dfec ); } -@@ -201,7 +231,32 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise +@@ -203,7 +229,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 { @@ -736,27 +731,27 @@ index 3ca75289ac9eb05eadb74c87c0c94cbc026fd08f..1e9e061ec694ad64fc2724b83a66dfec } finally { readData.input().close(); } -@@ -217,6 +272,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise +@@ -219,6 +270,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise this.folder = folder; this.sync = sync; this.info = info; -+ this.isChunkData = isChunkDataFolder(this.folder); // Paper - recalculate region file headers ++ this.isChunkData = info.dfuType()[0] == net.minecraft.util.datafix.DataFixTypes.CHUNK; // Paper - recalculate region file headers } - @org.jetbrains.annotations.Contract("_, false -> !null") @Nullable private RegionFile getRegionFile(ChunkPos chunkPos, boolean existingOnly) throws IOException { // CraftBukkit -@@ -309,6 +365,19 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + @org.jetbrains.annotations.Contract("_, false -> !null") private @Nullable RegionFile getRegionFile(final ChunkPos pos, boolean existingOnly) throws IOException { // CraftBukkit +@@ -313,6 +365,19 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise } - var4 = NbtIo.read(chunkDataInputStream); + var4 = NbtIo.read(regionChunkInputStream); + // Paper start - recover from corrupt regionfile header + if (this.isChunkData) { + ChunkPos headerChunkPos = SerializableChunkData.getChunkCoordinate(var4); -+ if (!headerChunkPos.equals(chunkPos)) { -+ net.minecraft.server.MinecraftServer.LOGGER.error("Attempting to read chunk data at " + chunkPos + " but got chunk data for " + headerChunkPos + " instead! Attempting regionfile recalculation for regionfile " + regionFile.getPath().toAbsolutePath()); -+ if (regionFile.recalculateHeader()) { -+ return this.read(chunkPos); ++ if (!headerChunkPos.equals(pos)) { ++ net.minecraft.server.MinecraftServer.LOGGER.error("Attempting to read chunk data at " + pos + " but got chunk data for " + headerChunkPos + " instead! Attempting regionfile recalculation for regionfile " + region.getPath().toAbsolutePath()); ++ if (region.recalculateHeader()) { ++ return this.read(pos); + } -+ net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + chunkPos + " for " + regionFile.getPath().toAbsolutePath()); ++ net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + pos + " for " + region.getPath().toAbsolutePath()); + return null; + } + } @@ -765,7 +760,7 @@ index 3ca75289ac9eb05eadb74c87c0c94cbc026fd08f..1e9e061ec694ad64fc2724b83a66dfec return var4; diff --git a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java -index d857a69ad3a0c4eeec52585e8d92f0f8673684f3..e813dedf8d70ae58d595bcd7bc92a1de845fe4a9 100644 +index 97d3da90c270a06d85e70d261658f71d97ae4e3c..11b65c688144e7ef5b7cba603949843a11ccd084 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; @@ -776,9 +771,27 @@ index d857a69ad3a0c4eeec52585e8d92f0f8673684f3..e813dedf8d70ae58d595bcd7bc92a1de + public static final Int2ObjectMap VERSIONS = new Int2ObjectOpenHashMap<>(); // Paper - private -> public private static final Object2ObjectMap VERSIONS_BY_NAME = new Object2ObjectOpenHashMap<>(); public static final RegionFileVersion VERSION_GZIP = register( - new RegionFileVersion( + new RegionFileVersion(1, null, in -> new FastBufferedInputStream(new GZIPInputStream(in)), out -> new BufferedOutputStream(new GZIPOutputStream(out))) +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 dimension, String type) { ++// Paper start - crazy hack to indicate what type the region file is ++public record RegionStorageInfo(String level, ResourceKey dimension, String type, net.minecraft.util.datafix.DataFixTypes[] dfuType) { ++ public RegionStorageInfo(String level, ResourceKey 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 b2363fa364be15d0a28f5966615131282b7aaa8f..68874c0b6f6f7c5d8e3a79c9a7b391b9624ab748 100644 +index 22a83f312d357176a5489e5b6b71c3ef3e737df7..74158a5d7ea8058c99ab215265987746aa043fc7 100644 --- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java +++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java @@ -120,6 +120,18 @@ public record SerializableChunkData( @@ -800,12 +813,24 @@ index b2363fa364be15d0a28f5966615131282b7aaa8f..68874c0b6f6f7c5d8e3a79c9a7b391b9 // 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(); -@@ -552,7 +564,7 @@ public record SerializableChunkData( - compoundTag.putInt("xPos", this.chunkPos.x); - compoundTag.putInt("yPos", this.minSectionY); - compoundTag.putInt("zPos", this.chunkPos.z); -- compoundTag.putLong("LastUpdate", this.lastUpdateTime); -+ compoundTag.putLong("LastUpdate", this.lastUpdateTime); // Paper - Diff on change - compoundTag.putLong("InhabitedTime", this.inhabitedTime); - compoundTag.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(this.chunkStatus).toString()); - compoundTag.storeNullable("blending_data", BlendingData.Packed.CODEC, this.blendingData); +@@ -544,7 +556,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 884fa9545a93008fa92863c845aef8f18d88e25a..15da3686bb1b5fddd388a88a7b300aa221c83f58 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 diff --git a/paper-server/patches/features/0020-Incremental-chunk-and-player-saving.patch b/paper-server/patches/features/0020-Incremental-chunk-and-player-saving.patch index 2617ba67518f..df783e6f2d5b 100644 --- a/paper-server/patches/features/0020-Incremental-chunk-and-player-saving.patch +++ b/paper-server/patches/features/0020-Incremental-chunk-and-player-saving.patch @@ -5,37 +5,81 @@ Subject: [PATCH] Incremental chunk and player saving diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index a9751f4bda862ef597ba39700b83c434f7a457d9..90e23b96ed3fc5a84f52e6f94ad40bdb028fedf7 100644 +index 65a971379f35c4b1b519b93787f690d891e29523..dadad4106e774884bc32af0ce37591b08c6d9a4c 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -972,7 +972,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0 && this.ticksUntilAutosave <= 0) { // CraftBukkit - this.autoSave(); + // Paper start - Incremental chunk and player saving -+ final ProfilerFiller profiler = Profiler.get(); + int playerSaveInterval = io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.rate; + if (playerSaveInterval < 0) { -+ playerSaveInterval = autosavePeriod; ++ playerSaveInterval = this.autosavePeriod; + } -+ profiler.push("save"); -+ final boolean fullSave = autosavePeriod > 0 && this.tickCount % autosavePeriod == 0; ++ Profiler.get().push("save"); ++ final boolean fullSave = this.autosavePeriod > 0 && this.tickCount % this.autosavePeriod == 0; + try { + this.isSaving = true; + if (playerSaveInterval > 0) { + this.playerList.saveAll(playerSaveInterval); + } ++ if (fullSave) { ++ this.saveGlobalData(false); ++ } + for (final ServerLevel level : this.getAllLevels()) { + if (level.paperConfig().chunks.autoSaveInterval.value() > 0) { + level.saveIncrementally(fullSave); @@ -44,47 +88,36 @@ index a9751f4bda862ef597ba39700b83c434f7a457d9..90e23b96ed3fc5a84f52e6f94ad40bdb + } finally { + this.isSaving = false; } -+ profiler.pop(); ++ Profiler.get().pop(); + // Paper end - Incremental chunk and player saving - ProfilerFiller profilerFiller = Profiler.get(); + 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 ef7e24716c2fc6a643d107cadcf743f80b39af41..45550619778b6a6b7f1f03467ece6bfe3d7b1e51 100644 +index 18eb79f76fb558a1ba9f989f59d2f273975be3a1..7a55657f9fb106dc3e95ef808d103a66cf2e56bd 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -1416,6 +1416,26 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1441,6 +1441,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet + public boolean mayInteract(final Entity entity, final BlockPos pos) { return !(entity instanceof Player player && (this.server.isUnderSpawnProtection(this, pos, player) || !this.getWorldBorder().isWithinBounds(pos))); } - + // Paper start - Incremental chunk and player saving -+ public void saveIncrementally(boolean doFull) { ++ public void saveIncrementally(final boolean doFull) { + if (doFull) { + org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(this.getWorld())); -+ } -+ -+ if (doFull) { + this.saveLevelData(false); + } + // chunk autosave is already called by the ChunkSystem during unload processing (ChunkMap#processUnloads) -+ // Copied from save() -+ // CraftBukkit start - moved from MinecraftServer.saveChunks -+ if (doFull) { // Paper -+ this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save(this.registryAccess())); -+ this.levelStorageAccess.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData()); -+ } -+ // CraftBukkit end + } + // Paper end - Incremental chunk and player saving -+ - public void save(@Nullable ProgressListener progress, boolean flush, boolean skipSave) { + + public void save(final @Nullable ProgressListener progressListener, final boolean flush, final boolean noSave) { // Paper start - add close param - this.save(progress, flush, skipSave, false); diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 1776f98636ca53a7cdbd160fbf6c22526fdb26a4..ea0853d09f17ee6dab3f063fd3abf10248d92ccd 100644 +index 41816ca8d4ef3d959d2b88cd4b2eedaaa541e612..82e87270d1ad89ad7aa56e028ced6b998ea0eca5 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -203,6 +203,7 @@ import org.slf4j.Logger; +@@ -205,6 +205,7 @@ import org.slf4j.Logger; public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer { // Paper - rewrite chunk system private static final Logger LOGGER = LogUtils.getLogger(); @@ -93,39 +126,42 @@ index 1776f98636ca53a7cdbd160fbf6c22526fdb26a4..ea0853d09f17ee6dab3f063fd3abf102 private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10; private static final int FLY_STAT_RECORDING_SPEED = 25; diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index 38c30d98a200dd2a5c888cc080e9c77795f0e0c4..dec9dd8e42f90ccf7e5e3ad945e459d07159250d 100644 +index 0613b053f4a75c0884280011ee092d9d108b3857..b69bcaa1e27a4fbe9a9568f3c9e1843167abe1f5 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java -@@ -412,6 +412,7 @@ public abstract class PlayerList { +@@ -418,6 +418,7 @@ public abstract class PlayerList { - protected void save(ServerPlayer player) { + protected void save(final ServerPlayer player) { if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit + player.lastSave = MinecraftServer.currentTick; // Paper - Incremental chunk and player saving this.playerIo.save(player); - ServerStatsCounter serverStatsCounter = player.getStats(); // CraftBukkit - if (serverStatsCounter != null) { -@@ -941,9 +942,23 @@ public abstract class PlayerList { + ServerStatsCounter stats = player.getStats(); // CraftBukkit + if (stats != null) { +@@ -951,10 +952,24 @@ public abstract class PlayerList { } public void saveAll() { + // Paper start - Incremental chunk and player saving + this.saveAll(-1); + } -+ + public void saveAll(final int interval) { ++ // Paper end - Incremental chunk and player saving io.papermc.paper.util.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main -+ int numSaved = 0; -+ final long now = MinecraftServer.currentTick; - for (int i = 0; i < this.players.size(); i++) { +- for (int i = 0; i < this.players.size(); i++) { - this.save(this.players.get(i)); -+ final ServerPlayer player = this.players.get(i); +- } ++ // Paper start - Incremental chunk and player saving ++ int numSaved = 0; ++ final long now = MinecraftServer.currentTick; ++ for (final ServerPlayer player : this.players) { + if (interval == -1 || now - player.lastSave >= interval) { + this.save(player); + if (interval != -1 && ++numSaved >= io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.maxPerTick()) { + break; + } + } -+ // Paper end - Incremental chunk and player saving - } ++ } ++ // Paper end - Incremental chunk and player saving return null; }); // Paper - ensure main } + diff --git a/paper-server/patches/features/0022-Flush-regionfiles-on-save-configuration-option.patch b/paper-server/patches/features/0021-Flush-regionfiles-on-save-configuration-option.patch similarity index 91% rename from paper-server/patches/features/0022-Flush-regionfiles-on-save-configuration-option.patch rename to paper-server/patches/features/0021-Flush-regionfiles-on-save-configuration-option.patch index 7b29282a28bc..78358aeb89e8 100644 --- a/paper-server/patches/features/0022-Flush-regionfiles-on-save-configuration-option.patch +++ b/paper-server/patches/features/0021-Flush-regionfiles-on-save-configuration-option.patch @@ -14,10 +14,10 @@ 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 93776be67380f3f8993348b9cb81bc183aa93a56..aa3d5a6ea59ac77665f2cfaa56a7d383016d08d0 100644 +index d4606e3d95a98e745cd565c0c23b2a657249602a..3bb9b58ee97464687e348e23b99159226415c267 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java -@@ -1272,6 +1272,14 @@ public final class MoonriseRegionFileIO { +@@ -1271,6 +1271,14 @@ public final class MoonriseRegionFileIO { try { this.regionDataController.finishWrite(this.chunkX, this.chunkZ, writeData); diff --git a/paper-server/patches/features/0021-Optimise-general-POI-access.patch b/paper-server/patches/features/0021-Optimise-general-POI-access.patch deleted file mode 100644 index 46816d7c6b8c..000000000000 --- a/paper-server/patches/features/0021-Optimise-general-POI-access.patch +++ /dev/null @@ -1,1062 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Sun, 31 Jan 2021 02:29:24 -0800 -Subject: [PATCH] Optimise general POI access - -There are a couple of problems with mojang's POI code. -Firstly, it's all streams. Unsurprisingly, stacking -streams on top of each other is horrible for performance -and ultimately took up half of a villager's tick! - -Secondly, sometime's the search radius is large and there are -a significant number of poi entries per chunk section. Even -removing streams at this point doesn't help much. The only solution -is to start at the search point and iterate outwards. This -type of approach shows massive gains for portals, simply because -we can avoid sync loading a large area of chunks. I also tested -a massive farm I found in JellySquid's discord, which showed -to benefit significantly simply because the farm had so many -portal blocks that searching through them all was very slow. - -Great care has been taken so that behavior remains identical to -vanilla, however I cannot account for oddball Stream API -implementations, if they even exist (streams can technically -be loose with iteration order in a sorted stream given its -source stream is not tagged with ordered, and mojang does not -tag the source stream as ordered). However in my testing on openjdk -there showed no difference, as expected. - -This patch also specifically optimises other areas of code to -use PoiAccess. For example, some villager AI and portaling code -had to be specifically modified. - -diff --git a/io/papermc/paper/util/PoiAccess.java b/io/papermc/paper/util/PoiAccess.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f39294b1f83c4022be5ced4da781103a1eee2daf ---- /dev/null -+++ b/io/papermc/paper/util/PoiAccess.java -@@ -0,0 +1,806 @@ -+package io.papermc.paper.util; -+ -+import ca.spottedleaf.moonrise.common.util.CoordinateUtils; -+import ca.spottedleaf.moonrise.common.util.WorldUtil; -+import com.mojang.datafixers.util.Pair; -+import it.unimi.dsi.fastutil.doubles.Double2ObjectMap; -+import it.unimi.dsi.fastutil.doubles.Double2ObjectRBTreeMap; -+import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; -+import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -+import java.util.function.BiPredicate; -+import net.minecraft.core.BlockPos; -+import net.minecraft.core.Holder; -+import net.minecraft.util.Mth; -+import net.minecraft.world.entity.ai.village.poi.PoiManager; -+import net.minecraft.world.entity.ai.village.poi.PoiRecord; -+import net.minecraft.world.entity.ai.village.poi.PoiSection; -+import net.minecraft.world.entity.ai.village.poi.PoiType; -+import java.util.ArrayList; -+import java.util.HashSet; -+import java.util.Iterator; -+import java.util.List; -+import java.util.Map; -+import java.util.Optional; -+import java.util.Set; -+import java.util.function.Predicate; -+ -+/** -+ * Provides optimised access to POI data. All returned values will be identical to vanilla. -+ */ -+public final class PoiAccess { -+ -+ protected static double clamp(final double val, final double min, final double max) { -+ return (val < min ? min : (val > max ? max : val)); -+ } -+ -+ protected static double getSmallestDistanceSquared(final double boxMinX, final double boxMinY, final double boxMinZ, -+ final double boxMaxX, final double boxMaxY, final double boxMaxZ, -+ -+ final double circleX, final double circleY, final double circleZ) { -+ // is the circle center inside the box? -+ if (circleX >= boxMinX && circleX <= boxMaxX && circleY >= boxMinY && circleY <= boxMaxY && circleZ >= boxMinZ && circleZ <= boxMaxZ) { -+ return 0.0; -+ } -+ -+ final double boxWidthX = (boxMaxX - boxMinX) / 2.0; -+ final double boxWidthY = (boxMaxY - boxMinY) / 2.0; -+ final double boxWidthZ = (boxMaxZ - boxMinZ) / 2.0; -+ -+ final double boxCenterX = (boxMinX + boxMaxX) / 2.0; -+ final double boxCenterY = (boxMinY + boxMaxY) / 2.0; -+ final double boxCenterZ = (boxMinZ + boxMaxZ) / 2.0; -+ -+ double centerDiffX = circleX - boxCenterX; -+ double centerDiffY = circleY - boxCenterY; -+ double centerDiffZ = circleZ - boxCenterZ; -+ -+ centerDiffX = circleX - (clamp(centerDiffX, -boxWidthX, boxWidthX) + boxCenterX); -+ centerDiffY = circleY - (clamp(centerDiffY, -boxWidthY, boxWidthY) + boxCenterY); -+ centerDiffZ = circleZ - (clamp(centerDiffZ, -boxWidthZ, boxWidthZ) + boxCenterZ); -+ -+ return (centerDiffX * centerDiffX) + (centerDiffY * centerDiffY) + (centerDiffZ * centerDiffZ); -+ } -+ -+ -+ // key is: -+ // upper 32 bits: -+ // upper 16 bits: max y section -+ // lower 16 bits: min y section -+ // lower 32 bits: -+ // upper 16 bits: section -+ // lower 16 bits: radius -+ protected static long getKey(final int minSection, final int maxSection, final int section, final int radius) { -+ return ( -+ (maxSection & 0xFFFFL) << (64 - 16) -+ | (minSection & 0xFFFFL) << (64 - 32) -+ | (section & 0xFFFFL) << (64 - 48) -+ | (radius & 0xFFFFL) << (64 - 64) -+ ); -+ } -+ -+ // only includes x/z axis -+ // finds the closest poi data by distance. -+ public static BlockPos findClosestPoiDataPosition(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ // position predicate must not modify chunk POI -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final double maxDistanceSquared, -+ final PoiManager.Occupancy occupancy, -+ final boolean load) { -+ final PoiRecord ret = findClosestPoiDataRecord( -+ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistanceSquared, occupancy, load -+ ); -+ -+ return ret == null ? null : ret.getPos(); -+ } -+ -+ // only includes x/z axis -+ // finds the closest poi data by distance. -+ public static Pair, BlockPos> findClosestPoiDataTypeAndPosition(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ // position predicate must not modify chunk POI -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final double maxDistanceSquared, -+ final PoiManager.Occupancy occupancy, -+ final boolean load) { -+ final PoiRecord ret = findClosestPoiDataRecord( -+ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistanceSquared, occupancy, load -+ ); -+ -+ return ret == null ? null : Pair.of(ret.getPoiType(), ret.getPos()); -+ } -+ -+ // only includes x/z axis -+ // finds the closest poi data by distance. if multiple match the same distance, then they all are returned. -+ public static void findClosestPoiDataPositions(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ // position predicate must not modify chunk POI -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final double maxDistanceSquared, -+ final PoiManager.Occupancy occupancy, -+ final boolean load, -+ final Set ret) { -+ final Set positions = new HashSet<>(); -+ // pos predicate is last thing that runs before adding to ret. -+ final Predicate newPredicate = (final BlockPos pos) -> { -+ if (positionPredicate != null && !positionPredicate.test(pos)) { -+ return false; -+ } -+ return positions.add(pos.immutable()); -+ }; -+ -+ final List toConvert = new ArrayList<>(); -+ findClosestPoiDataRecords( -+ poiStorage, villagePlaceType, newPredicate, sourcePosition, range, maxDistanceSquared, occupancy, load, toConvert -+ ); -+ -+ for (final PoiRecord record : toConvert) { -+ ret.add(record.getPos()); -+ } -+ } -+ -+ // only includes x/z axis -+ // finds the closest poi data by distance. -+ public static PoiRecord findClosestPoiDataRecord(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ // position predicate must not modify chunk POI -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final double maxDistanceSquared, -+ final PoiManager.Occupancy occupancy, -+ final boolean load) { -+ final List ret = new ArrayList<>(); -+ findClosestPoiDataRecords( -+ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistanceSquared, occupancy, load, ret -+ ); -+ return ret.isEmpty() ? null : ret.get(0); -+ } -+ -+ // only includes x/z axis -+ // finds the closest poi data by distance. -+ public static PoiRecord findClosestPoiDataRecord(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ // position predicate must not modify chunk POI -+ final BiPredicate, BlockPos> predicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final double maxDistanceSquared, -+ final PoiManager.Occupancy occupancy, -+ final boolean load) { -+ final List ret = new ArrayList<>(); -+ findClosestPoiDataRecords( -+ poiStorage, villagePlaceType, predicate, sourcePosition, range, maxDistanceSquared, occupancy, load, ret -+ ); -+ return ret.isEmpty() ? null : ret.get(0); -+ } -+ -+ // only includes x/z axis -+ // finds the closest poi data by distance. if multiple match the same distance, then they all are returned. -+ public static void findClosestPoiDataRecords(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ // position predicate must not modify chunk POI -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final double maxDistanceSquared, -+ final PoiManager.Occupancy occupancy, -+ final boolean load, -+ final List ret) { -+ final BiPredicate, BlockPos> predicate = positionPredicate != null ? (type, pos) -> positionPredicate.test(pos) : null; -+ findClosestPoiDataRecords(poiStorage, villagePlaceType, predicate, sourcePosition, range, maxDistanceSquared, occupancy, load, ret); -+ } -+ -+ public static void findClosestPoiDataRecords(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ // position predicate must not modify chunk POI -+ final BiPredicate, BlockPos> predicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final double maxDistanceSquared, -+ final PoiManager.Occupancy occupancy, -+ final boolean load, -+ final List ret) { -+ final Predicate occupancyFilter = occupancy.getTest(); -+ -+ final List closestRecords = new ArrayList<>(); -+ double closestDistanceSquared = maxDistanceSquared; -+ -+ final int lowerX = Mth.floor(sourcePosition.getX() - range) >> 4; -+ final int lowerY = WorldUtil.getMinSection(poiStorage.moonrise$getWorld()); -+ final int lowerZ = Mth.floor(sourcePosition.getZ() - range) >> 4; -+ final int upperX = Mth.floor(sourcePosition.getX() + range) >> 4; -+ final int upperY = WorldUtil.getMaxSection(poiStorage.moonrise$getWorld()); -+ final int upperZ = Mth.floor(sourcePosition.getZ() + range) >> 4; -+ -+ final int centerX = sourcePosition.getX() >> 4; -+ final int centerY = Mth.clamp(sourcePosition.getY() >> 4, lowerY, upperY); -+ final int centerZ = sourcePosition.getZ() >> 4; -+ final long centerKey = CoordinateUtils.getChunkSectionKey(centerX, centerY, centerZ); -+ -+ final LongArrayFIFOQueue queue = new LongArrayFIFOQueue(); -+ final LongOpenHashSet seen = new LongOpenHashSet(); -+ seen.add(centerKey); -+ queue.enqueue(centerKey); -+ -+ while (!queue.isEmpty()) { -+ final long key = queue.dequeueLong(); -+ final int sectionX = CoordinateUtils.getChunkSectionX(key); -+ final int sectionY = CoordinateUtils.getChunkSectionY(key); -+ final int sectionZ = CoordinateUtils.getChunkSectionZ(key); -+ -+ if (sectionX < lowerX || sectionX > upperX || sectionY < lowerY || sectionY > upperY || sectionZ < lowerZ || sectionZ > upperZ) { -+ // out of bound chunk -+ continue; -+ } -+ -+ final double sectionDistanceSquared = getSmallestDistanceSquared( -+ (sectionX << 4) + 0.5, -+ (sectionY << 4) + 0.5, -+ (sectionZ << 4) + 0.5, -+ (sectionX << 4) + 15.5, -+ (sectionY << 4) + 15.5, -+ (sectionZ << 4) + 15.5, -+ (double)sourcePosition.getX(), (double)sourcePosition.getY(), (double)sourcePosition.getZ() -+ ); -+ if (sectionDistanceSquared > closestDistanceSquared) { -+ continue; -+ } -+ -+ // queue all neighbours -+ for (int dz = -1; dz <= 1; ++dz) { -+ for (int dx = -1; dx <= 1; ++dx) { -+ for (int dy = -1; dy <= 1; ++dy) { -+ // -1 and 1 have the 1st bit set. so just add up the first bits, and it will tell us how many -+ // values are set. we only care about cardinal neighbours, so, we only care if one value is set -+ if ((dx & 1) + (dy & 1) + (dz & 1) != 1) { -+ continue; -+ } -+ -+ final int neighbourX = sectionX + dx; -+ final int neighbourY = sectionY + dy; -+ final int neighbourZ = sectionZ + dz; -+ -+ final long neighbourKey = CoordinateUtils.getChunkSectionKey(neighbourX, neighbourY, neighbourZ); -+ if (seen.add(neighbourKey)) { -+ queue.enqueue(neighbourKey); -+ } -+ } -+ } -+ } -+ -+ final Optional poiSectionOptional = load ? poiStorage.getOrLoad(key) : poiStorage.get(key); -+ -+ if (poiSectionOptional == null || !poiSectionOptional.isPresent()) { -+ continue; -+ } -+ -+ final PoiSection poiSection = poiSectionOptional.get(); -+ -+ final Map, Set> sectionData = poiSection.getData(); -+ if (sectionData.isEmpty()) { -+ continue; -+ } -+ -+ // now we search the section data -+ for (final Map.Entry, Set> entry : sectionData.entrySet()) { -+ if (!villagePlaceType.test(entry.getKey())) { -+ // filter out by poi type -+ continue; -+ } -+ -+ // now we can look at the poi data -+ for (final PoiRecord poiData : entry.getValue()) { -+ if (!occupancyFilter.test(poiData)) { -+ // filter by occupancy -+ continue; -+ } -+ -+ final BlockPos poiPosition = poiData.getPos(); -+ -+ if (Math.abs(poiPosition.getX() - sourcePosition.getX()) > range -+ || Math.abs(poiPosition.getZ() - sourcePosition.getZ()) > range) { -+ // out of range for square radius -+ continue; -+ } -+ -+ // it's important that it's poiPosition.distSqr(source) : the value actually is different IF the values are swapped! -+ final double dataRange = poiPosition.distSqr(sourcePosition); -+ -+ if (dataRange > closestDistanceSquared) { -+ // out of range for distance check -+ continue; -+ } -+ -+ if (predicate != null && !predicate.test(poiData.getPoiType(), poiPosition)) { -+ // filter by position -+ continue; -+ } -+ -+ if (dataRange < closestDistanceSquared) { -+ closestRecords.clear(); -+ closestDistanceSquared = dataRange; -+ } -+ closestRecords.add(poiData); -+ } -+ } -+ } -+ -+ // uh oh! we might have multiple records that match the distance sorting! -+ // we need to re-order our results by the way vanilla would have iterated over them. -+ closestRecords.sort((record1, record2) -> { -+ // vanilla iterates the same way we do for data inside sections, so we know the ordering inside a section -+ // is fine and should be preserved (this sort is stable so we're good there) -+ // but they iterate sections by x then by z (like the following) -+ // for (int x = -dx; x <= dx; ++x) -+ // for (int z = -dz; z <= dz; ++z) -+ // .... -+ // so we need to reorder such that records with lower chunk z, then lower chunk x come first -+ final BlockPos pos1 = record1.getPos(); -+ final BlockPos pos2 = record2.getPos(); -+ -+ final int cx1 = pos1.getX() >> 4; -+ final int cz1 = pos1.getZ() >> 4; -+ -+ final int cx2 = pos2.getX() >> 4; -+ final int cz2 = pos2.getZ() >> 4; -+ -+ if (cz2 != cz1) { -+ // want smaller z -+ return Integer.compare(cz1, cz2); -+ } -+ -+ if (cx2 != cx1) { -+ // want smaller x -+ return Integer.compare(cx1, cx2); -+ } -+ -+ // same chunk -+ // once vanilla has the chunk, it will iterate from all of the chunk sections starting from smaller y -+ // so now we just compare section y, wanting smaller y -+ -+ return Integer.compare(pos1.getY() >> 4, pos2.getY() >> 4); -+ }); -+ -+ // now we match perfectly what vanilla would have outputted, without having to search the whole radius (hopefully). -+ ret.addAll(closestRecords); -+ } -+ -+ // finds the closest poi entry pos. -+ public static BlockPos findNearestPoiPosition(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ // position predicate must not modify chunk POI -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final double maxDistanceSquared, -+ final PoiManager.Occupancy occupancy, -+ final boolean load) { -+ final PoiRecord ret = findNearestPoiRecord( -+ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistanceSquared, occupancy, load -+ ); -+ return ret == null ? null : ret.getPos(); -+ } -+ -+ // finds the closest `max` poi entry positions. -+ public static void findNearestPoiPositions(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ // position predicate must not modify chunk POI -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final double maxDistanceSquared, -+ final PoiManager.Occupancy occupancy, -+ final boolean load, -+ final int max, -+ final List, BlockPos>> ret) { -+ final Set positions = new HashSet<>(); -+ // pos predicate is last thing that runs before adding to ret. -+ final Predicate newPredicate = (final BlockPos pos) -> { -+ if (positionPredicate != null && !positionPredicate.test(pos)) { -+ return false; -+ } -+ return positions.add(pos.immutable()); -+ }; -+ -+ final List toConvert = new ArrayList<>(); -+ findNearestPoiRecords( -+ poiStorage, villagePlaceType, newPredicate, sourcePosition, range, maxDistanceSquared, occupancy, load, max, toConvert -+ ); -+ -+ for (final PoiRecord record : toConvert) { -+ ret.add(Pair.of(record.getPoiType(), record.getPos())); -+ } -+ } -+ -+ // finds the closest poi entry. -+ public static PoiRecord findNearestPoiRecord(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ // position predicate must not modify chunk POI -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final double maxDistanceSquared, -+ final PoiManager.Occupancy occupancy, -+ final boolean load) { -+ final List ret = new ArrayList<>(); -+ findNearestPoiRecords( -+ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, maxDistanceSquared, occupancy, load, -+ 1, ret -+ ); -+ return ret.isEmpty() ? null : ret.get(0); -+ } -+ -+ // finds the closest `max` poi entries. -+ public static void findNearestPoiRecords(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ // position predicate must not modify chunk POI -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final double maxDistanceSquared, -+ final PoiManager.Occupancy occupancy, -+ final boolean load, -+ final int max, -+ final List ret) { -+ final Predicate occupancyFilter = occupancy.getTest(); -+ -+ final Double2ObjectRBTreeMap> closestRecords = new Double2ObjectRBTreeMap<>(); -+ int totalRecords = 0; -+ double furthestDistanceSquared = maxDistanceSquared; -+ -+ final int lowerX = Mth.floor(sourcePosition.getX() - range) >> 4; -+ final int lowerY = WorldUtil.getMinSection(poiStorage.moonrise$getWorld()); -+ final int lowerZ = Mth.floor(sourcePosition.getZ() - range) >> 4; -+ final int upperX = Mth.floor(sourcePosition.getX() + range) >> 4; -+ final int upperY = WorldUtil.getMaxSection(poiStorage.moonrise$getWorld()); -+ final int upperZ = Mth.floor(sourcePosition.getZ() + range) >> 4; -+ -+ final int centerX = sourcePosition.getX() >> 4; -+ final int centerY = Mth.clamp(sourcePosition.getY() >> 4, lowerY, upperY); -+ final int centerZ = sourcePosition.getZ() >> 4; -+ final long centerKey = CoordinateUtils.getChunkSectionKey(centerX, centerY, centerZ); -+ -+ final LongArrayFIFOQueue queue = new LongArrayFIFOQueue(); -+ final LongOpenHashSet seen = new LongOpenHashSet(); -+ seen.add(centerKey); -+ queue.enqueue(centerKey); -+ -+ while (!queue.isEmpty()) { -+ final long key = queue.dequeueLong(); -+ final int sectionX = CoordinateUtils.getChunkSectionX(key); -+ final int sectionY = CoordinateUtils.getChunkSectionY(key); -+ final int sectionZ = CoordinateUtils.getChunkSectionZ(key); -+ -+ if (sectionX < lowerX || sectionX > upperX || sectionY < lowerY || sectionY > upperY || sectionZ < lowerZ || sectionZ > upperZ) { -+ // out of bound chunk -+ continue; -+ } -+ -+ final double sectionDistanceSquared = getSmallestDistanceSquared( -+ (sectionX << 4) + 0.5, -+ (sectionY << 4) + 0.5, -+ (sectionZ << 4) + 0.5, -+ (sectionX << 4) + 15.5, -+ (sectionY << 4) + 15.5, -+ (sectionZ << 4) + 15.5, -+ (double) sourcePosition.getX(), (double) sourcePosition.getY(), (double) sourcePosition.getZ() -+ ); -+ -+ if (sectionDistanceSquared > (totalRecords >= max ? furthestDistanceSquared : maxDistanceSquared)) { -+ continue; -+ } -+ -+ // queue all neighbours -+ for (int dz = -1; dz <= 1; ++dz) { -+ for (int dx = -1; dx <= 1; ++dx) { -+ for (int dy = -1; dy <= 1; ++dy) { -+ // -1 and 1 have the 1st bit set. so just add up the first bits, and it will tell us how many -+ // values are set. we only care about cardinal neighbours, so, we only care if one value is set -+ if ((dx & 1) + (dy & 1) + (dz & 1) != 1) { -+ continue; -+ } -+ -+ final int neighbourX = sectionX + dx; -+ final int neighbourY = sectionY + dy; -+ final int neighbourZ = sectionZ + dz; -+ -+ final long neighbourKey = CoordinateUtils.getChunkSectionKey(neighbourX, neighbourY, neighbourZ); -+ if (seen.add(neighbourKey)) { -+ queue.enqueue(neighbourKey); -+ } -+ } -+ } -+ } -+ -+ final Optional poiSectionOptional = load ? poiStorage.getOrLoad(key) : poiStorage.get(key); -+ -+ if (poiSectionOptional == null || !poiSectionOptional.isPresent()) { -+ continue; -+ } -+ -+ final PoiSection poiSection = poiSectionOptional.get(); -+ -+ final Map, Set> sectionData = poiSection.getData(); -+ if (sectionData.isEmpty()) { -+ continue; -+ } -+ -+ // now we search the section data -+ for (final Map.Entry, Set> entry : sectionData.entrySet()) { -+ if (!villagePlaceType.test(entry.getKey())) { -+ // filter out by poi type -+ continue; -+ } -+ -+ // now we can look at the poi data -+ for (final PoiRecord poiData : entry.getValue()) { -+ if (!occupancyFilter.test(poiData)) { -+ // filter by occupancy -+ continue; -+ } -+ -+ final BlockPos poiPosition = poiData.getPos(); -+ -+ if (Math.abs(poiPosition.getX() - sourcePosition.getX()) > range -+ || Math.abs(poiPosition.getZ() - sourcePosition.getZ()) > range) { -+ // out of range for square radius -+ continue; -+ } -+ -+ // it's important that it's poiPosition.distSqr(source) : the value actually is different IF the values are swapped! -+ final double dataRange = poiPosition.distSqr(sourcePosition); -+ -+ if (dataRange > maxDistanceSquared) { -+ // out of range for distance check -+ continue; -+ } -+ -+ if (dataRange > furthestDistanceSquared && totalRecords >= max) { -+ // out of range for distance check -+ continue; -+ } -+ -+ if (positionPredicate != null && !positionPredicate.test(poiPosition)) { -+ // filter by position -+ continue; -+ } -+ -+ if (dataRange > furthestDistanceSquared) { -+ // we know totalRecords < max, so this entry is now our furthest -+ furthestDistanceSquared = dataRange; -+ } -+ -+ closestRecords.computeIfAbsent(dataRange, (final double unused) -> { -+ return new ArrayList<>(); -+ }).add(poiData); -+ -+ if (++totalRecords >= max) { -+ if (closestRecords.size() >= 2) { -+ int entriesInClosest = 0; -+ final Iterator>> iterator = closestRecords.double2ObjectEntrySet().iterator(); -+ double nextFurthestDistanceSquared = 0.0; -+ -+ for (int i = 0, len = closestRecords.size() - 1; i < len; ++i) { -+ final Double2ObjectMap.Entry> recordEntry = iterator.next(); -+ entriesInClosest += recordEntry.getValue().size(); -+ nextFurthestDistanceSquared = recordEntry.getDoubleKey(); -+ } -+ -+ if (entriesInClosest >= max) { -+ // the last set of entries at range wont even be considered for sure... nuke em -+ final Double2ObjectMap.Entry> recordEntry = iterator.next(); -+ totalRecords -= recordEntry.getValue().size(); -+ iterator.remove(); -+ -+ furthestDistanceSquared = nextFurthestDistanceSquared; -+ } -+ } -+ } -+ } -+ } -+ } -+ -+ final List closestRecordsUnsorted = new ArrayList<>(); -+ -+ // we're done here, so now just flatten the map and sort it. -+ -+ for (final List records : closestRecords.values()) { -+ closestRecordsUnsorted.addAll(records); -+ } -+ -+ // uh oh! we might have multiple records that match the distance sorting! -+ // we need to re-order our results by the way vanilla would have iterated over them. -+ closestRecordsUnsorted.sort((record1, record2) -> { -+ // vanilla iterates the same way we do for data inside sections, so we know the ordering inside a section -+ // is fine and should be preserved (this sort is stable so we're good there) -+ // but they iterate sections by x then by z (like the following) -+ // for (int x = -dx; x <= dx; ++x) -+ // for (int z = -dz; z <= dz; ++z) -+ // .... -+ // so we need to reorder such that records with lower chunk z, then lower chunk x come first -+ final BlockPos pos1 = record1.getPos(); -+ final BlockPos pos2 = record2.getPos(); -+ -+ final int cx1 = pos1.getX() >> 4; -+ final int cz1 = pos1.getZ() >> 4; -+ -+ final int cx2 = pos2.getX() >> 4; -+ final int cz2 = pos2.getZ() >> 4; -+ -+ if (cz2 != cz1) { -+ // want smaller z -+ return Integer.compare(cz1, cz2); -+ } -+ -+ if (cx2 != cx1) { -+ // want smaller x -+ return Integer.compare(cx1, cx2); -+ } -+ -+ // same chunk -+ // once vanilla has the chunk, it will iterate from all of the chunk sections starting from smaller y -+ // so now we just compare section y, wanting smaller section y -+ -+ return Integer.compare(pos1.getY() >> 4, pos2.getY() >> 4); -+ }); -+ -+ // trim out any entries exceeding our maximum -+ for (int i = closestRecordsUnsorted.size() - 1; i >= max; --i) { -+ closestRecordsUnsorted.remove(i); -+ } -+ -+ // now we match perfectly what vanilla would have outputted, without having to search the whole radius (hopefully). -+ ret.addAll(closestRecordsUnsorted); -+ } -+ -+ public static BlockPos findAnyPoiPosition(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final PoiManager.Occupancy occupancy, -+ final boolean load) { -+ final PoiRecord ret = findAnyPoiRecord( -+ poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, occupancy, load -+ ); -+ -+ return ret == null ? null : ret.getPos(); -+ } -+ -+ public static void findAnyPoiPositions(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final PoiManager.Occupancy occupancy, -+ final boolean load, -+ final int max, -+ final List, BlockPos>> ret) { -+ final Set positions = new HashSet<>(); -+ // pos predicate is last thing that runs before adding to ret. -+ final Predicate newPredicate = (final BlockPos pos) -> { -+ if (positionPredicate != null && !positionPredicate.test(pos)) { -+ return false; -+ } -+ return positions.add(pos.immutable()); -+ }; -+ -+ final List toConvert = new ArrayList<>(); -+ findAnyPoiRecords( -+ poiStorage, villagePlaceType, newPredicate, sourcePosition, range, occupancy, load, max, toConvert -+ ); -+ -+ for (final PoiRecord record : toConvert) { -+ ret.add(Pair.of(record.getPoiType(), record.getPos())); -+ } -+ } -+ -+ public static PoiRecord findAnyPoiRecord(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final PoiManager.Occupancy occupancy, -+ final boolean load) { -+ final List ret = new ArrayList<>(); -+ findAnyPoiRecords(poiStorage, villagePlaceType, positionPredicate, sourcePosition, range, occupancy, load, 1, ret); -+ return ret.isEmpty() ? null : ret.get(0); -+ } -+ -+ public static void findAnyPoiRecords(final PoiManager poiStorage, -+ final Predicate> villagePlaceType, -+ final Predicate positionPredicate, -+ final BlockPos sourcePosition, -+ final int range, // distance on x y z axis -+ final PoiManager.Occupancy occupancy, -+ final boolean load, -+ final int max, -+ final List ret) { -+ // the biggest issue with the original mojang implementation is that they chain so many streams together -+ // the amount of streams chained just rolls performance, even if nothing is iterated over -+ final Predicate occupancyFilter = occupancy.getTest(); -+ final double rangeSquared = range * range; -+ -+ int added = 0; -+ -+ // First up, we need to iterate the chunks -+ // all the values here are in chunk sections -+ final int lowerX = Mth.floor(sourcePosition.getX() - range) >> 4; -+ final int lowerY = Math.max(WorldUtil.getMinSection(poiStorage.moonrise$getWorld()), Mth.floor(sourcePosition.getY() - range) >> 4); -+ final int lowerZ = Mth.floor(sourcePosition.getZ() - range) >> 4; -+ final int upperX = Mth.floor(sourcePosition.getX() + range) >> 4; -+ final int upperY = Math.min(WorldUtil.getMaxSection(poiStorage.moonrise$getWorld()), Mth.floor(sourcePosition.getY() + range) >> 4); -+ final int upperZ = Mth.floor(sourcePosition.getZ() + range) >> 4; -+ -+ // Vanilla iterates by x until max is reached then increases z -+ // vanilla also searches by increasing Y section value -+ for (int currZ = lowerZ; currZ <= upperZ; ++currZ) { -+ for (int currX = lowerX; currX <= upperX; ++currX) { -+ for (int currY = lowerY; currY <= upperY; ++currY) { // vanilla searches the entire chunk because they're actually stupid. just search the sections we need -+ final Optional poiSectionOptional = load ? poiStorage.getOrLoad(CoordinateUtils.getChunkSectionKey(currX, currY, currZ)) : -+ poiStorage.get(CoordinateUtils.getChunkSectionKey(currX, currY, currZ)); -+ final PoiSection poiSection = poiSectionOptional == null ? null : poiSectionOptional.orElse(null); -+ if (poiSection == null) { -+ continue; -+ } -+ -+ final Map, Set> sectionData = poiSection.getData(); -+ if (sectionData.isEmpty()) { -+ continue; -+ } -+ -+ // now we search the section data -+ for (final Map.Entry, Set> entry : sectionData.entrySet()) { -+ if (!villagePlaceType.test(entry.getKey())) { -+ // filter out by poi type -+ continue; -+ } -+ -+ // now we can look at the poi data -+ for (final PoiRecord poiData : entry.getValue()) { -+ if (!occupancyFilter.test(poiData)) { -+ // filter by occupancy -+ continue; -+ } -+ -+ final BlockPos poiPosition = poiData.getPos(); -+ -+ if (Math.abs(poiPosition.getX() - sourcePosition.getX()) > range -+ || Math.abs(poiPosition.getZ() - sourcePosition.getZ()) > range) { -+ // out of range for square radius -+ continue; -+ } -+ -+ if (poiPosition.distSqr(sourcePosition) > rangeSquared) { -+ // out of range for distance check -+ continue; -+ } -+ -+ if (positionPredicate != null && !positionPredicate.test(poiPosition)) { -+ // filter by position -+ continue; -+ } -+ -+ // found one! -+ ret.add(poiData); -+ if (++added >= max) { -+ return; -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ -+ private PoiAccess() { -+ throw new RuntimeException(); -+ } -+} -diff --git a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -index 44bab3a41693601c86b33686bfdcd754aab6ff70..278addb7dbe4f57e99fb91ce1cd1bf3559e239a3 100644 ---- a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -+++ b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -@@ -83,12 +83,16 @@ public class AcquirePoi { - return true; - } - }; -- Set, BlockPos>> set = poiManager.findAllClosestFirstWithType( -- acquirablePois, predicate1, mob.blockPosition(), 48, PoiManager.Occupancy.HAS_SPACE -- ) -- .limit(5L) -- .filter(pair1 -> predicate.test(level, pair1.getSecond())) -- .collect(Collectors.toSet()); -+ // Paper start - optimise POI access -+ final java.util.List, BlockPos>> poiposes = new java.util.ArrayList<>(); -+ io.papermc.paper.util.PoiAccess.findNearestPoiPositions(poiManager, acquirablePois, predicate1, mob.blockPosition(), 48, 48*48, PoiManager.Occupancy.HAS_SPACE, false, 5, poiposes); -+ final Set, BlockPos>> set = new java.util.HashSet<>(poiposes.size()); -+ for (final Pair, BlockPos> poiPose : poiposes) { -+ if (predicate.test(level, poiPose.getSecond())) { -+ set.add(poiPose); -+ } -+ } -+ // Paper end - optimise POI access - Path path = findPathToPois(mob, set); - if (path != null && path.canReach()) { - BlockPos target = path.getTarget(); -diff --git a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -index 6e9325f0800a35637fdec5edb8a514ea03741762..066faa704338c573472381e1ebd063e0d52aaaa4 100644 ---- a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -+++ b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -@@ -53,11 +53,12 @@ public class NearestBedSensor extends Sensor { - return true; - } - }; -- Set, BlockPos>> set = poiManager.findAllWithType( -- holder -> holder.is(PoiTypes.HOME), predicate, entity.blockPosition(), 48, PoiManager.Occupancy.ANY -- ) -- .collect(Collectors.toSet()); -- Path path = AcquirePoi.findPathToPois(entity, set); -+ // Paper start - optimise POI access -+ java.util.List, BlockPos>> poiposes = new java.util.ArrayList<>(); -+ // don't ask me why it's unbounded. ask mojang. -+ io.papermc.paper.util.PoiAccess.findAnyPoiPositions(poiManager, type -> type.is(PoiTypes.HOME), predicate, entity.blockPosition(), 48, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes); -+ Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes)); -+ // Paper end - optimise POI access - if (path != null && path.canReach()) { - BlockPos target = path.getTarget(); - Optional> type = poiManager.getType(target); -diff --git a/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/net/minecraft/world/entity/ai/village/poi/PoiManager.java -index e2b05800736d29f6061a9f2117e51c8a0d5bfeb6..95fa0d9487542ba5308c60d29095efa6c90e86d6 100644 ---- a/net/minecraft/world/entity/ai/village/poi/PoiManager.java -+++ b/net/minecraft/world/entity/ai/village/poi/PoiManager.java -@@ -256,36 +256,47 @@ public class PoiManager extends SectionStorage im - public Optional find( - Predicate> typePredicate, Predicate posPredicate, BlockPos pos, int distance, PoiManager.Occupancy status - ) { -- return this.findAll(typePredicate, posPredicate, pos, distance, status).findFirst(); -+ // Paper start - re-route to faster logic -+ BlockPos ret = io.papermc.paper.util.PoiAccess.findAnyPoiPosition(this, typePredicate, posPredicate, pos, distance, status, false); -+ return Optional.ofNullable(ret); -+ // Paper end - } - - public Optional findClosest(Predicate> typePredicate, BlockPos pos, int distance, PoiManager.Occupancy status) { -- return this.getInRange(typePredicate, pos, distance, status).map(PoiRecord::getPos).min(Comparator.comparingDouble(blockPos -> blockPos.distSqr(pos))); -+ // Paper start - re-route to faster logic -+ BlockPos closestPos = io.papermc.paper.util.PoiAccess.findClosestPoiDataPosition(this, typePredicate, null, pos, distance, distance * distance, status, false); -+ return Optional.ofNullable(closestPos); -+ // Paper end - re-route to faster logic - } - - public Optional, BlockPos>> findClosestWithType( - Predicate> typePredicate, BlockPos pos, int distance, PoiManager.Occupancy status - ) { -- return this.getInRange(typePredicate, pos, distance, status) -- .min(Comparator.comparingDouble(poiRecord -> poiRecord.getPos().distSqr(pos))) -- .map(poiRecord -> Pair.of(poiRecord.getPoiType(), poiRecord.getPos())); -+ // Paper start - re-route to faster logic -+ return Optional.ofNullable(io.papermc.paper.util.PoiAccess.findClosestPoiDataTypeAndPosition( -+ this, typePredicate, null, pos, distance, distance * distance, status, false -+ )); -+ // Paper end - re-route to faster logic - } - - public Optional findClosest( - Predicate> typePredicate, Predicate posPredicate, BlockPos pos, int distance, PoiManager.Occupancy status - ) { -- return this.getInRange(typePredicate, pos, distance, status) -- .map(PoiRecord::getPos) -- .filter(posPredicate) -- .min(Comparator.comparingDouble(blockPos -> blockPos.distSqr(pos))); -+ // Paper start - re-route to faster logic -+ BlockPos closestPos = io.papermc.paper.util.PoiAccess.findClosestPoiDataPosition(this, typePredicate, posPredicate, pos, distance, distance * distance, status, false); -+ return Optional.ofNullable(closestPos); -+ // Paper end - re-route to faster logic - } - - public Optional take( - Predicate> typePredicate, BiPredicate, BlockPos> combinedTypePosPredicate, BlockPos pos, int distance - ) { -- return this.getInRange(typePredicate, pos, distance, PoiManager.Occupancy.HAS_SPACE) -- .filter(poiRecord -> combinedTypePosPredicate.test(poiRecord.getPoiType(), poiRecord.getPos())) -- .findFirst() -+ // Paper start - re-route to faster logic -+ final @javax.annotation.Nullable PoiRecord closest = io.papermc.paper.util.PoiAccess.findClosestPoiDataRecord( -+ this, typePredicate, combinedTypePosPredicate, pos, distance, distance * distance, Occupancy.HAS_SPACE, false -+ ); -+ return Optional.ofNullable(closest) -+ // Paper end - re-route to faster logic - .map(poiRecord -> { - poiRecord.acquireTicket(); - return poiRecord.getPos(); -@@ -300,8 +311,21 @@ public class PoiManager extends SectionStorage im - int distance, - RandomSource random - ) { -- List list = Util.toShuffledList(this.getInRange(typePredicate, pos, distance, status), random); -- return list.stream().filter(poiRecord -> posPredicate.test(poiRecord.getPos())).findFirst().map(PoiRecord::getPos); -+ // Paper start - re-route to faster logic -+ List list = new java.util.ArrayList<>(); -+ io.papermc.paper.util.PoiAccess.findAnyPoiRecords( -+ this, typePredicate, posPredicate, pos, distance, status, false, Integer.MAX_VALUE, list -+ ); -+ -+ // the old method shuffled the list and then tried to find the first element in it that -+ // matched positionPredicate, however we moved positionPredicate into the poi search. This means we can avoid a -+ // shuffle entirely, and just pick a random element from list -+ if (list.isEmpty()) { -+ return Optional.empty(); -+ } -+ -+ return Optional.of(list.get(random.nextInt(list.size())).getPos()); -+ // Paper end - re-route to faster logic - } - - public boolean release(BlockPos pos) { -diff --git a/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/net/minecraft/world/entity/ai/village/poi/PoiSection.java -index f209696e0158df5cd32e9949b3054452d6a3e5cd..49cbdd80057250c37464ca5f5dd39a20dbaa9f2c 100644 ---- a/net/minecraft/world/entity/ai/village/poi/PoiSection.java -+++ b/net/minecraft/world/entity/ai/village/poi/PoiSection.java -@@ -28,7 +28,7 @@ import org.slf4j.Logger; - public class PoiSection implements ca.spottedleaf.moonrise.patches.chunk_system.level.poi.ChunkSystemPoiSection { // Paper - rewrite chunk system - private static final Logger LOGGER = LogUtils.getLogger(); - private final Short2ObjectMap records = new Short2ObjectOpenHashMap<>(); -- private final Map, Set> byType = Maps.newHashMap(); -+ private final Map, Set> byType = Maps.newHashMap(); public final Map, Set> getData() { return this.byType; } // Paper - public accessor - private final Runnable setDirty; - private boolean isValid; - -diff --git a/net/minecraft/world/level/chunk/storage/SectionStorage.java b/net/minecraft/world/level/chunk/storage/SectionStorage.java -index 42699c82321233eaa3647ca0ac4d1f24b6a6227f..0f8e1dcdd8de00c7669de0b0d2329b6d9d032cef 100644 ---- a/net/minecraft/world/level/chunk/storage/SectionStorage.java -+++ b/net/minecraft/world/level/chunk/storage/SectionStorage.java -@@ -130,11 +130,11 @@ public class SectionStorage implements AutoCloseable, ca.spottedleaf.moonr - return !this.dirtyChunks.isEmpty(); - } - -- protected @Nullable Optional get(long sectionKey) { -+ public @Nullable Optional get(long sectionKey) { // Paper - optimize poi access - public - return this.storage.get(sectionKey); - } - -- protected Optional getOrLoad(long sectionKey) { -+ public Optional getOrLoad(long sectionKey) { // Paper - optimize poi access - public - if (this.outsideStoredRange(sectionKey)) { - return Optional.empty(); - } else { -diff --git a/net/minecraft/world/level/portal/PortalForcer.java b/net/minecraft/world/level/portal/PortalForcer.java -index 491affe4343b3843bcdcbae0cca670d120ea24d0..9c552437faa20d10bb5bf67a15282b1e8dec94c5 100644 ---- a/net/minecraft/world/level/portal/PortalForcer.java -+++ b/net/minecraft/world/level/portal/PortalForcer.java -@@ -49,13 +49,38 @@ public class PortalForcer { - PoiManager poiManager = this.level.getPoiManager(); - // int i = isNether ? 16 : 128; - // CraftBukkit end -- poiManager.ensureLoadedAndValid(this.level, exitPos, i); -- return poiManager.getInSquare(holder -> holder.is(PoiTypes.NETHER_PORTAL), exitPos, i, PoiManager.Occupancy.ANY) -- .map(PoiRecord::getPos) -- .filter(worldBorder::isWithinBounds) -- .filter(pos -> !(this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> pos.getY() >= v))) // Paper - Configurable nether ceiling damage -- .filter(blockPos -> this.level.getBlockState(blockPos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS)) -- .min(Comparator.comparingDouble(blockPos -> blockPos.distSqr(exitPos)).thenComparingInt(Vec3i::getY)); -+ // Paper start - optimise portals -+ java.util.List records = new java.util.ArrayList<>(); -+ io.papermc.paper.util.PoiAccess.findClosestPoiDataRecords( -+ poiManager, -+ type -> type.is(PoiTypes.NETHER_PORTAL), -+ (BlockPos pos) -> { -+ net.minecraft.world.level.chunk.ChunkAccess lowest = this.level.getChunk(pos.getX() >> 4, pos.getZ() >> 4, net.minecraft.world.level.chunk.status.ChunkStatus.EMPTY); -+ if (!lowest.getPersistedStatus().isOrAfter(net.minecraft.world.level.chunk.status.ChunkStatus.FULL) -+ && (lowest.getBelowZeroRetrogen() == null || !lowest.getBelowZeroRetrogen().targetStatus().isOrAfter(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN))) { -+ // why would we generate the chunk? -+ return false; -+ } -+ if (!worldBorder.isWithinBounds(pos) || (this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> pos.getY() >= v))) { // Paper - Configurable nether ceiling damage -+ return false; -+ } -+ return lowest.getBlockState(pos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS); -+ }, -+ exitPos, i, Double.MAX_VALUE, PoiManager.Occupancy.ANY, true, records -+ ); -+ -+ // this gets us most of the way there, but we bias towards lower y values. -+ BlockPos lowestPos = null; -+ for (PoiRecord record : records) { -+ if (lowestPos == null) { -+ lowestPos = record.getPos(); -+ } else if (lowestPos.getY() > record.getPos().getY()) { -+ lowestPos = record.getPos(); -+ } -+ } -+ // now we're done -+ return Optional.ofNullable(lowestPos); -+ // Paper end - optimise portals - } - - public Optional createPortal(BlockPos pos, Direction.Axis axis) { diff --git a/paper-server/patches/features/0023-Optimise-collision-checking-in-player-move-packet-ha.patch b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch similarity index 61% rename from paper-server/patches/features/0023-Optimise-collision-checking-in-player-move-packet-ha.patch rename to paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch index eb7f987dfa2b..9a872c3879c1 100644 --- a/paper-server/patches/features/0023-Optimise-collision-checking-in-player-move-packet-ha.patch +++ b/paper-server/patches/features/0022-Optimise-collision-checking-in-player-move-packet-ha.patch @@ -6,46 +6,40 @@ 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 214613e9422606b7b1f37716fc060db63c38849a..e158d614abed8d16e80192c0c9abd8537c92b9dc 100644 +index 05a700ffc844b41f4fa129226f78dc6c52520f32..1d93a864c5fc040b5ec7c48b4db0bd54cd6255ff 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -624,6 +624,7 @@ public class ServerGamePacketListenerImpl +@@ -634,6 +634,7 @@ public class ServerGamePacketListenerImpl } - rootVehicle.move(MoverType.PLAYER, new Vec3(d3, d4, d5)); -+ final boolean didCollide = toX != rootVehicle.getX() || toY != rootVehicle.getY() || toZ != rootVehicle.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be... - double verticalDelta = d4; - d3 = d - rootVehicle.getX(); - d4 = d1 - rootVehicle.getY(); -@@ -635,12 +636,21 @@ public class ServerGamePacketListenerImpl - d7 = d3 * d3 + d4 * d4 + d5 * d5; - boolean flag1 = false; - if (d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot -- flag1 = true; -+ flag1 = true; // Paper - diff on change, this should be moved wrongly - LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(d7)); + vehicle.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist)); ++ final boolean didCollide = targetX != vehicle.getX() || targetY != vehicle.getY() || targetZ != vehicle.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be... + double oyDist = yDist; + xDist = targetX - vehicle.getX(); + yDist = targetY - vehicle.getY(); +@@ -649,7 +650,17 @@ public class ServerGamePacketListenerImpl + LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", vehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(movedDist)); } -- if (flag1 && serverLevel.noCollision(rootVehicle, boundingBox) -- || this.isEntityCollidingWithAnythingNew(serverLevel, rootVehicle, boundingBox, d, d1, d2)) { +- if (fail && level.noCollision(vehicle, oldAABB) || this.isEntityCollidingWithAnythingNew(level, vehicle, oldAABB, targetX, targetY, targetZ)) { + // Paper start - optimise out extra getCubes -+ boolean teleportBack = flag1; ++ boolean teleportBack = fail; + if (!teleportBack) { + // note: only call after setLocation, or else getBoundingBox is wrong -+ final AABB newBox = rootVehicle.getBoundingBox(); -+ if (didCollide || !boundingBox.equals(newBox)) { -+ teleportBack = this.hasNewCollision(serverLevel, rootVehicle, boundingBox, newBox); ++ final AABB newBox = vehicle.getBoundingBox(); ++ if (didCollide || !oldAABB.equals(newBox)) { ++ teleportBack = this.hasNewCollision(level, vehicle, oldAABB, newBox); + } // else: no collision at all detected, why do we care? + } + if (teleportBack) { + // Paper end - optimise out extra getCubes - rootVehicle.absSnapTo(x, y, z, f, f1); - this.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle)); - rootVehicle.removeLatestMovementRecording(); -@@ -719,9 +729,32 @@ public class ServerGamePacketListenerImpl + vehicle.absSnapTo(oldX, oldY, oldZ, targetYRot, targetXRot); + this.send(ClientboundMoveVehiclePacket.fromEntity(vehicle)); + vehicle.removeLatestMovementRecording(); +@@ -728,9 +739,32 @@ public class ServerGamePacketListenerImpl } - private boolean noBlocksAround(Entity entity) { + private boolean noBlocksAround(final Entity entity) { - return entity.level() - .getBlockStates(entity.getBoundingBox().inflate(0.0625).expandTowards(0.0, -0.55, 0.0)) - .allMatch(BlockBehaviour.BlockStateBase::isAir); @@ -78,43 +72,43 @@ index 214613e9422606b7b1f37716fc060db63c38849a..e158d614abed8d16e80192c0c9abd853 } @Override -@@ -1503,7 +1536,7 @@ public class ServerGamePacketListenerImpl +@@ -1542,7 +1576,7 @@ public class ServerGamePacketListenerImpl } } -- AABB boundingBox = this.player.getBoundingBox(); -+ AABB boundingBox = this.player.getBoundingBox(); // Paper - diff on change, should be old AABB - d3 = d - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above - d4 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above - d5 = d2 - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above -@@ -1542,6 +1575,7 @@ public class ServerGamePacketListenerImpl - boolean flag1 = this.player.verticalCollisionBelow; - this.player.move(MoverType.PLAYER, new Vec3(d3, d4, d5)); +- AABB oldAABB = this.player.getBoundingBox(); ++ AABB oldAABB = this.player.getBoundingBox(); // Paper - diff on change, should be old AABB + xDist = targetX - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above + yDist = targetY - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above + zDist = targetZ - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above +@@ -1581,6 +1615,7 @@ public class ServerGamePacketListenerImpl + boolean playerStandsOnSomething = this.player.verticalCollisionBelow; + this.player.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist)); this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move -+ final boolean didCollide = toX != this.player.getX() || toY != this.player.getY() || toZ != this.player.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be... ++ final boolean didCollide = targetX != this.player.getX() || targetY != this.player.getY() || targetZ != this.player.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be... // Paper start - prevent position desync if (this.awaitingPositionFromClient != null) { return; // ... thanks Mojang for letting move calls teleport across dimensions. -@@ -1575,7 +1609,17 @@ public class ServerGamePacketListenerImpl +@@ -1614,7 +1649,17 @@ public class ServerGamePacketListenerImpl } // Paper start - Add fail move event -- boolean allowMovement = this.player.noPhysics || this.player.isSleeping() || (!movedWrongly || !serverLevel.noCollision(this.player, boundingBox)) && !this.isEntityCollidingWithAnythingNew(serverLevel, this.player, boundingBox, d, d1, d2); +- boolean allowMovement = this.player.noPhysics || this.player.isSleeping() || (!movedWrongly || !level.noCollision(this.player, oldAABB)) && !this.isEntityCollidingWithAnythingNew(level, this.player, oldAABB, targetX, targetY, targetZ); + // Paper start - optimise out extra getCubes + boolean allowMovement = this.player.noPhysics || this.player.isSleeping() || !movedWrongly; -+ this.player.absSnapTo(d, d1, d2, f, f1); // prevent desync by tping to the set position, dropped for unknown reasons by mojang ++ this.player.absSnapTo(targetX, targetY, targetZ, targetYRot, targetXRot); // prevent desync by tping to the set position, dropped for unknown reasons by mojang + if (!this.player.noPhysics && !this.player.isSleeping() && allowMovement) { + final AABB newBox = this.player.getBoundingBox(); -+ if (didCollide || !boundingBox.equals(newBox)) { ++ if (didCollide || !oldAABB.equals(newBox)) { + // note: only call after setLocation, or else getBoundingBox is wrong -+ allowMovement = !this.hasNewCollision(serverLevel, this.player, boundingBox, newBox); ++ allowMovement = !this.hasNewCollision(level, this.player, oldAABB, newBox); + } // else: no collision at all detected, why do we care? + } + // Paper end - optimise out extra getCubes if (!allowMovement) { io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK, - toX, toY, toZ, toYaw, toPitch, false); -@@ -1710,7 +1754,7 @@ public class ServerGamePacketListenerImpl + targetX, targetY, targetZ, targetYRot, targetXRot, false); +@@ -1752,7 +1797,7 @@ public class ServerGamePacketListenerImpl private boolean updateAwaitingTeleport() { if (this.awaitingPositionFromClient != null) { @@ -123,7 +117,7 @@ index 214613e9422606b7b1f37716fc060db63c38849a..e158d614abed8d16e80192c0c9abd853 this.awaitingTeleportTime = this.tickCount; this.teleport( this.awaitingPositionFromClient.x, -@@ -1729,6 +1773,34 @@ public class ServerGamePacketListenerImpl +@@ -1771,6 +1816,34 @@ public class ServerGamePacketListenerImpl } } @@ -155,6 +149,6 @@ index 214613e9422606b7b1f37716fc060db63c38849a..e158d614abed8d16e80192c0c9abd853 + } + // Paper end - optimise out extra getCubes + - private boolean isEntityCollidingWithAnythingNew(LevelReader level, Entity entity, AABB box, double x, double y, double z) { - AABB aabb = entity.getBoundingBox().move(x - entity.getX(), y - entity.getY(), z - entity.getZ()); - Iterable preMoveCollisions = level.getPreMoveCollisions(entity, aabb.deflate(1.0E-5F), box.getBottomCenter()); + private boolean isEntityCollidingWithAnythingNew( + final LevelReader level, final Entity entity, final AABB oldAABB, final double newX, final double newY, final double newZ + ) { diff --git a/paper-server/patches/features/0032-Add-explicit-flush-support-to-Log4j-AsyncAppender.patch b/paper-server/patches/features/0023-Add-explicit-flush-support-to-Log4j-AsyncAppender.patch similarity index 100% rename from paper-server/patches/features/0032-Add-explicit-flush-support-to-Log4j-AsyncAppender.patch rename to paper-server/patches/features/0023-Add-explicit-flush-support-to-Log4j-AsyncAppender.patch diff --git a/paper-server/patches/features/0024-Improve-keepalive-ping-system.patch b/paper-server/patches/features/0024-Improve-keepalive-ping-system.patch index f046496ff6a4..fb5c4be07597 100644 --- a/paper-server/patches/features/0024-Improve-keepalive-ping-system.patch +++ b/paper-server/patches/features/0024-Improve-keepalive-ping-system.patch @@ -85,22 +85,22 @@ index 0000000000000000000000000000000000000000..4a2520f554c2ee74faf86d7c93baccf0 + } +} diff --git a/net/minecraft/server/network/CommonListenerCookie.java b/net/minecraft/server/network/CommonListenerCookie.java -index 21d50675bfe90c2276890779eb23de58ac915b9a..7be34e37562875313b8e43357921b5fec5079d7a 100644 +index d87a00ee3cd04ce25dbe46ae6b386b7f0799102c..ace440c2b82a6368aa27a85ff433ba2cbc8f39a5 100644 --- a/net/minecraft/server/network/CommonListenerCookie.java +++ b/net/minecraft/server/network/CommonListenerCookie.java @@ -3,8 +3,8 @@ package net.minecraft.server.network; import com.mojang.authlib.GameProfile; import net.minecraft.server.level.ClientInformation; --public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @javax.annotation.Nullable String brandName, java.util.Set channels) { // Paper -+public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @javax.annotation.Nullable String brandName, java.util.Set channels, io.papermc.paper.util.KeepAlive keepAlive) { // Paper - public static CommonListenerCookie createInitial(GameProfile gameProfile, boolean transferred) { +-public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jspecify.annotations.Nullable String brandName, java.util.Set channels) { // Paper ++public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jspecify.annotations.Nullable String brandName, java.util.Set channels, io.papermc.paper.util.KeepAlive keepAlive) { // Paper // Paper + public static CommonListenerCookie createInitial(final GameProfile gameProfile, final boolean transferred) { - return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new java.util.HashSet<>()); // Paper -+ return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new java.util.HashSet<>(), new io.papermc.paper.util.KeepAlive()); // Paper ++ return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new java.util.HashSet<>(), new io.papermc.paper.util.KeepAlive()); // Paper // Paper } } diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -index 079ab378920c0e52ef4e42ef20b37bd389f40870..b8a4b4cc02a2fc6b70f4b840796eed501aad6239 100644 +index f54d99c59bbca7fb5e0615f8fee9c3f6fbffa18a..d9b6f3826e2126d339e926afed782f6d0e890508 100644 --- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java @@ -39,12 +39,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack @@ -122,7 +122,7 @@ index 079ab378920c0e52ef4e42ef20b37bd389f40870..b8a4b4cc02a2fc6b70f4b840796eed50 // CraftBukkit start public final org.bukkit.craftbukkit.CraftServer cserver; @@ -61,13 +62,14 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) { + public ServerCommonPacketListenerImpl(final MinecraftServer server, final Connection connection, final CommonListenerCookie cookie) { this.server = server; this.connection = connection; - this.keepAliveTime = Util.getMillis(); @@ -140,10 +140,10 @@ index 079ab378920c0e52ef4e42ef20b37bd389f40870..b8a4b4cc02a2fc6b70f4b840796eed50 @@ -100,13 +102,41 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack @Override - public void handleKeepAlive(ServerboundKeepAlivePacket packet) { + public void handleKeepAlive(final ServerboundKeepAlivePacket packet) { - if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) { -- int i = (int)(Util.getMillis() - this.keepAliveTime); -- this.latency = (this.latency * 3 + i) / 4; +- int time = (int)(Util.getMillis() - this.keepAliveTime); +- this.latency = (this.latency * 3 + time) / 4; - this.keepAlivePending = false; - } else if (!this.isSingleplayerOwner()) { - this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, io.papermc.paper.connection.DisconnectionReason.TIMEOUT); // Paper - add proper async disconnect @@ -188,29 +188,29 @@ index 079ab378920c0e52ef4e42ef20b37bd389f40870..b8a4b4cc02a2fc6b70f4b840796eed50 @@ -233,20 +263,23 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack protected void keepConnectionAlive() { Profiler.get().push("keepAlive"); - long millis = Util.getMillis(); + long now = Util.getMillis(); - // Paper start - give clients a longer time to respond to pings as per pre 1.12.2 timings - // This should effectively place the keepalive handling back to "as it was" before 1.12.2 -- final long elapsedTime = millis - this.keepAliveTime; +- final long elapsedTime = now - this.keepAliveTime; - if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // use vanilla's 15000L between keep alive packets - if (this.keepAlivePending) { - if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected - this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, io.papermc.paper.connection.DisconnectionReason.TIMEOUT); // Paper - kick event cause - } - // Paper end - give clients a longer time to respond to pings as per pre 1.12.2 timings -- } else if (this.checkIfClosed(millis)) { +- } else if (this.checkIfClosed(now)) { - this.keepAlivePending = true; -- this.keepAliveTime = millis; -- this.keepAliveChallenge = millis; +- this.keepAliveTime = now; +- this.keepAliveChallenge = now; - this.send(new ClientboundKeepAlivePacket(this.keepAliveChallenge)); + // Paper start - improve keepalives -+ if (this.checkIfClosed(millis) && !this.processedDisconnect) { ++ if (this.checkIfClosed(now) && !this.processedDisconnect) { + long currTime = System.nanoTime(); + + if ((currTime - this.keepAlive.lastKeepAliveTx) >= java.util.concurrent.TimeUnit.SECONDS.toNanos(1L)) { + this.keepAlive.lastKeepAliveTx = currTime; + -+ io.papermc.paper.util.KeepAlive.PendingKeepAlive pka = new io.papermc.paper.util.KeepAlive.PendingKeepAlive(currTime, millis); ++ io.papermc.paper.util.KeepAlive.PendingKeepAlive pka = new io.papermc.paper.util.KeepAlive.PendingKeepAlive(currTime, now); + this.keepAlive.pendingKeepAlives.add(pka); + this.send(new ClientboundKeepAlivePacket(pka.challengeId())); + } @@ -223,11 +223,11 @@ index 079ab378920c0e52ef4e42ef20b37bd389f40870..b8a4b4cc02a2fc6b70f4b840796eed50 } } -@@ -427,6 +460,6 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack +@@ -424,6 +457,6 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack } - protected CommonListenerCookie createCookie(ClientInformation clientInformation) { + protected CommonListenerCookie createCookie(final ClientInformation clientInformation) { - return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels); // Paper -+ return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels, this.keepAlive); // Paper ++ return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels, this.keepAlive); // Paper // Paper } } diff --git a/paper-server/patches/features/0025-Optimise-EntityScheduler-ticking.patch b/paper-server/patches/features/0025-Optimise-EntityScheduler-ticking.patch index 15b1b432be52..d7029584d276 100644 --- a/paper-server/patches/features/0025-Optimise-EntityScheduler-ticking.patch +++ b/paper-server/patches/features/0025-Optimise-EntityScheduler-ticking.patch @@ -20,18 +20,18 @@ index 2bc436cdf5180a7943c45fabb9fbbedae6f7db56..f312a7f5b1b2a777ab36b94ce7cbf387 @Override diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 90e23b96ed3fc5a84f52e6f94ad40bdb028fedf7..1885c0378a329ef24ee3ac7556ceff4b328db089 100644 +index dadad4106e774884bc32af0ce37591b08c6d9a4c..b7a0963390540c283cede90108f96437bfdd4dab 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -1748,33 +1748,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop serverPlayer1.connection.suspendFlushing()); + protected void tickChildren(final BooleanSupplier haveTime) { + ProfilerFiller profiler = Profiler.get(); + this.getPlayerList().getPlayers().forEach(playerx -> playerx.connection.suspendFlushing()); this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit - // Paper start - Folia scheduler API - ((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler) org.bukkit.Bukkit.getGlobalRegionScheduler()).tick(); @@ -61,16 +61,15 @@ index 90e23b96ed3fc5a84f52e6f94ad40bdb028fedf7..1885c0378a329ef24ee3ac7556ceff4b - } - } - }); -- // Paper end - Folia scheduler API + // Paper end - optimise Folia entity scheduler + // Paper end - Folia scheduler API 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 - profilerFiller.push("commandFunctions"); diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index d3d97b0b6ff6312f1deb0bb8b1beef8b3578bc53..3bf618f1d756bad7755a87803132bd731e7c41be 100644 +index 93d3fb313d538cc1067b1db010458169a1320122..9cd46a8619e575a865f4d00ce71d249c9b028b2f 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -5216,6 +5216,11 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -5147,6 +5147,11 @@ public abstract class Entity this.getBukkitEntity().taskScheduler.retire(); } // Paper end - Folia schedulers @@ -81,4 +80,4 @@ index d3d97b0b6ff6312f1deb0bb8b1beef8b3578bc53..3bf618f1d756bad7755a87803132bd73 + // Paper end - optimise Folia entity scheduler @Override - public void setLevelCallback(EntityInLevelCallback levelCallback) { + public void setLevelCallback(final EntityInLevelCallback levelCallback) { diff --git a/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch new file mode 100644 index 000000000000..9d566a9a3f63 --- /dev/null +++ b/paper-server/patches/features/0026-DataConverter-Moonrise-co-fixes.patch @@ -0,0 +1,76 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> +Date: Sat, 7 Jun 2025 15:05:05 -0400 +Subject: [PATCH] DataConverter, Moonrise & co fixes + + +diff --git a/ca/spottedleaf/dataconverter/minecraft/converters/attributes/ConverterAbstractOldAttributesRename.java b/ca/spottedleaf/dataconverter/minecraft/converters/attributes/ConverterAbstractOldAttributesRename.java +index 1a6d02df13f3461402fed429f7fbdb5acee877c4..712cca21a6b4736a15d49dceaf7e6d641096b0a9 100644 +--- a/ca/spottedleaf/dataconverter/minecraft/converters/attributes/ConverterAbstractOldAttributesRename.java ++++ b/ca/spottedleaf/dataconverter/minecraft/converters/attributes/ConverterAbstractOldAttributesRename.java +@@ -38,7 +38,13 @@ public final class ConverterAbstractOldAttributesRename { + MCTypeRegistry.ITEM_STACK.addStructureConverter(new DataConverter<>(version, versionStep) { + @Override + public MapType convert(final MapType data, final long sourceVersion, final long toVersion) { +- final ListType attributes = data.getList("AttributeModifiers", ObjectType.MAP); ++ final MapType tag = data.getMap("tag"); ++ ++ if (tag == null) { ++ return null; ++ } ++ ++ final ListType attributes = tag.getList("AttributeModifiers", ObjectType.MAP); + + if (attributes == null) { + return null; +diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V4307.java b/ca/spottedleaf/dataconverter/minecraft/versions/V4307.java +index d2877c20f389d0131e1dd208b464f590671e5d82..27bdc70d861ca39487ad16cb3afb89d604b462c8 100644 +--- a/ca/spottedleaf/dataconverter/minecraft/versions/V4307.java ++++ b/ca/spottedleaf/dataconverter/minecraft/versions/V4307.java +@@ -87,10 +87,10 @@ public final class V4307 { + + @Override + public MapType convert(final MapType root, final long sourceVersion, final long toVersion) { +- final Set hiddenComponents = new LinkedHashSet<>(); +- +- unwrapBlockPredicates(root, "minecraft:can_break", hiddenComponents); ++ // Don't use a linked hash set, and ensure that it is added in the same (undefined) order as the vanilla datafixer ++ final Set hiddenComponents = new java.util.HashSet<>(); + unwrapBlockPredicates(root, "minecraft:can_place_on", hiddenComponents); ++ unwrapBlockPredicates(root, "minecraft:can_break", hiddenComponents); + + updateComponent(root, "minecraft:trim", hiddenComponents); + updateComponent(root, "minecraft:unbreakable", hiddenComponents); +diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 1d93a864c5fc040b5ec7c48b4db0bd54cd6255ff..33763013daa99ed9146cd526e245e91a78851aa5 100644 +--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1820,9 +1820,14 @@ public class ServerGamePacketListenerImpl + private boolean hasNewCollision(final ServerLevel level, final Entity entity, final AABB oldBox, final AABB newBox) { + final List collisionsBB = new java.util.ArrayList<>(); + final List collisionsVoxel = new java.util.ArrayList<>(); ++ ++ int flags = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER; ++ if (level.paperConfig().chunks.preventMovingIntoUnloadedChunks) { ++ flags |= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS; ++ } + ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisions( + level, entity, newBox, collisionsVoxel, collisionsBB, +- ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS | ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER, ++ flags, + null, null + ); + +diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java +index ae9bc7cb3ebb245d78e50b69702facec8f0b75ac..72f8f3ca5d0c5ac99a9850809f8412e281376732 100644 +--- a/net/minecraft/world/level/Level.java ++++ b/net/minecraft/world/level/Level.java +@@ -1527,7 +1527,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + if (entity instanceof net.minecraft.world.entity.decoration.ArmorStand && !entity.level().paperConfig().entities.armorStands.doCollisionEntityLookups) + return false; + // Paper start - optimise collisions +- final int flags = entity == null ? (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER | ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_ONLY) : ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_ONLY; ++ final int flags = entity != null ? (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER | ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_ONLY) : ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_ONLY; + if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisionsForBlocksOrWorldBorder((Level)(Object)this, entity, box, null, null, flags, null)) { + return false; + } diff --git a/paper-server/patches/features/0031-DataConverter-Fixes.patch b/paper-server/patches/features/0031-DataConverter-Fixes.patch deleted file mode 100644 index 78f9a419add2..000000000000 --- a/paper-server/patches/features/0031-DataConverter-Fixes.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> -Date: Sat, 7 Jun 2025 15:05:05 -0400 -Subject: [PATCH] DataConverter Fixes - - -diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V4307.java b/ca/spottedleaf/dataconverter/minecraft/versions/V4307.java -index d2877c20f389d0131e1dd208b464f590671e5d82..27bdc70d861ca39487ad16cb3afb89d604b462c8 100644 ---- a/ca/spottedleaf/dataconverter/minecraft/versions/V4307.java -+++ b/ca/spottedleaf/dataconverter/minecraft/versions/V4307.java -@@ -87,10 +87,10 @@ public final class V4307 { - - @Override - public MapType convert(final MapType root, final long sourceVersion, final long toVersion) { -- final Set hiddenComponents = new LinkedHashSet<>(); -- -- unwrapBlockPredicates(root, "minecraft:can_break", hiddenComponents); -+ // Don't use a linked hash set, and ensure that it is added in the same (undefined) order as the vanilla datafixer -+ final Set hiddenComponents = new java.util.HashSet<>(); - unwrapBlockPredicates(root, "minecraft:can_place_on", hiddenComponents); -+ unwrapBlockPredicates(root, "minecraft:can_break", hiddenComponents); - - updateComponent(root, "minecraft:trim", hiddenComponents); - updateComponent(root, "minecraft:unbreakable", hiddenComponents); diff --git a/paper-server/patches/features/0026-Optional-per-player-mob-spawns.patch b/paper-server/patches/features_unapplied/0027-Optional-per-player-mob-spawns.patch similarity index 100% rename from paper-server/patches/features/0026-Optional-per-player-mob-spawns.patch rename to paper-server/patches/features_unapplied/0027-Optional-per-player-mob-spawns.patch diff --git a/paper-server/patches/features/0027-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch b/paper-server/patches/features_unapplied/0028-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch similarity index 100% rename from paper-server/patches/features/0027-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch rename to paper-server/patches/features_unapplied/0028-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch diff --git a/paper-server/patches/features/0028-Optimize-Hoppers.patch b/paper-server/patches/features_unapplied/0029-Optimize-Hoppers.patch similarity index 100% rename from paper-server/patches/features/0028-Optimize-Hoppers.patch rename to paper-server/patches/features_unapplied/0029-Optimize-Hoppers.patch diff --git a/paper-server/patches/features/0029-Anti-Xray.patch b/paper-server/patches/features_unapplied/0030-Anti-Xray.patch similarity index 100% rename from paper-server/patches/features/0029-Anti-Xray.patch rename to paper-server/patches/features_unapplied/0030-Anti-Xray.patch diff --git a/paper-server/patches/features/0030-Improve-exact-choice-recipe-ingredients.patch b/paper-server/patches/features_unapplied/0031-Improve-exact-choice-recipe-ingredients.patch similarity index 100% rename from paper-server/patches/features/0030-Improve-exact-choice-recipe-ingredients.patch rename to paper-server/patches/features_unapplied/0031-Improve-exact-choice-recipe-ingredients.patch diff --git a/paper-server/patches/resources/data/minecraft/worldgen/noise_settings/caves.json.patch b/paper-server/patches/resources/data/minecraft/worldgen/noise_settings/caves.json.patch index 64ecd4557529..73fabf039b85 100644 --- a/paper-server/patches/resources/data/minecraft/worldgen/noise_settings/caves.json.patch +++ b/paper-server/patches/resources/data/minecraft/worldgen/noise_settings/caves.json.patch @@ -1,6 +1,6 @@ --- a/data/minecraft/worldgen/noise_settings/caves.json +++ b/data/minecraft/worldgen/noise_settings/caves.json -@@ -110,7 +_,8 @@ +@@ -94,7 +_,8 @@ "if_true": { "type": "minecraft:not", "invert": { @@ -10,7 +10,7 @@ "false_at_and_above": { "below_top": 0 }, -@@ -130,7 +_,8 @@ +@@ -114,7 +_,8 @@ { "type": "minecraft:condition", "if_true": { diff --git a/paper-server/patches/sources/ca/spottedleaf/moonrise/paper/PaperHooks.java.patch b/paper-server/patches/sources/ca/spottedleaf/moonrise/paper/PaperHooks.java.patch index a49f46afc2c1..d13b6e206ca6 100644 --- a/paper-server/patches/sources/ca/spottedleaf/moonrise/paper/PaperHooks.java.patch +++ b/paper-server/patches/sources/ca/spottedleaf/moonrise/paper/PaperHooks.java.patch @@ -213,7 +213,7 @@ + + @Override + public boolean configFixMC159283() { -+ return true; ++ return io.papermc.paper.configuration.GlobalConfiguration.get().misc.fixFarEndTerrainGeneration; + } + + @Override diff --git a/paper-server/patches/sources/com/mojang/brigadier/exceptions/CommandSyntaxException.java.patch b/paper-server/patches/sources/com/mojang/brigadier/exceptions/CommandSyntaxException.java.patch index 5a9ff8c8d0a8..8bbf644d9f1a 100644 --- a/paper-server/patches/sources/com/mojang/brigadier/exceptions/CommandSyntaxException.java.patch +++ b/paper-server/patches/sources/com/mojang/brigadier/exceptions/CommandSyntaxException.java.patch @@ -16,7 +16,7 @@ + + // Paper start - Brigadier API + @Override -+ public @org.jetbrains.annotations.Nullable net.kyori.adventure.text.Component componentMessage() { ++ public net.kyori.adventure.text.@org.jspecify.annotations.Nullable Component componentMessage() { + return io.papermc.paper.brigadier.PaperBrigadier.componentFromMessage(this.message); + } + // Paper end - Brigadier API diff --git a/paper-server/patches/sources/com/mojang/math/OctahedralGroup.java.patch b/paper-server/patches/sources/com/mojang/math/OctahedralGroup.java.patch index fa336bf0640a..590b8b09adbb 100644 --- a/paper-server/patches/sources/com/mojang/math/OctahedralGroup.java.patch +++ b/paper-server/patches/sources/com/mojang/math/OctahedralGroup.java.patch @@ -1,7 +1,7 @@ --- a/com/mojang/math/OctahedralGroup.java +++ b/com/mojang/math/OctahedralGroup.java -@@ -105,6 +_,12 @@ - .map(group -> Arrays.stream(values()).filter(octahedralGroup -> group.compose(octahedralGroup) == IDENTITY).findAny().get()) +@@ -99,6 +_,12 @@ + .map(f -> Arrays.stream(values()).filter(s -> f.compose(s) == IDENTITY).findAny().get()) .toArray(OctahedralGroup[]::new); + static { @@ -13,16 +13,16 @@ private OctahedralGroup(final String name, final SymmetricGroup3 permutation, final boolean invertX, final boolean invertY, final boolean invertZ) { this.name = name; this.invertX = invertX; -@@ -145,7 +_,7 @@ +@@ -139,7 +_,7 @@ return this.name; } -- public Direction rotate(Direction direction) { +- public Direction rotate(final Direction direction) { + public void initializeRotationDirections() { // Paper - Avoid Lazy Initialization for Enum Fields if (this.rotatedDirections == null) { - this.rotatedDirections = Util.makeEnumMap(Direction.class, direction1 -> { - Direction.Axis axis = direction1.getAxis(); -@@ -156,6 +_,11 @@ + this.rotatedDirections = Util.makeEnumMap(Direction.class, facing -> { + Direction.Axis oldAxis = facing.getAxis(); +@@ -150,6 +_,11 @@ }); } diff --git a/paper-server/patches/sources/io/papermc/paper/FeatureHooks.java.patch b/paper-server/patches/sources/io/papermc/paper/FeatureHooks.java.patch index dd764f0757aa..eeda6652aeb0 100644 --- a/paper-server/patches/sources/io/papermc/paper/FeatureHooks.java.patch +++ b/paper-server/patches/sources/io/papermc/paper/FeatureHooks.java.patch @@ -1,6 +1,6 @@ --- /dev/null +++ b/io/papermc/paper/FeatureHooks.java -@@ -1,0 +_,243 @@ +@@ -1,0 +_,248 @@ +package io.papermc.paper; + +import io.papermc.paper.command.PaperSubcommand; @@ -17,7 +17,6 @@ +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; -+import net.minecraft.world.entity.monster.spider.Spider; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; @@ -34,8 +33,8 @@ +public final class FeatureHooks { + + // this includes non-accessible entities -+ public static Iterable getAllEntities(final net.minecraft.server.level.ServerLevel world) { -+ return ((net.minecraft.world.level.entity.LevelEntityGetterAdapter)world.getEntities()).sectionStorage.getAllEntities(); ++ public static Iterable getAllEntities(final net.minecraft.server.level.ServerLevel level) { ++ return ((net.minecraft.world.level.entity.LevelEntityGetterAdapter)level.getEntities()).sectionStorage.getAllEntities(); + } + + public static void setPlayerChunkUnloadDelay(final long ticks) { @@ -63,7 +62,7 @@ + + public static Set getSentChunkKeys(final ServerPlayer player) { + final LongSet keys = new LongOpenHashSet(); -+ player.getChunkTrackingView().forEach(pos -> keys.add(pos.longKey)); ++ player.getChunkTrackingView().forEach(pos -> keys.add(pos.pack())); + return LongSets.unmodifiable(keys); + } + @@ -71,18 +70,24 @@ + final ObjectSet chunks = new ObjectOpenHashSet<>(); + final World world = player.level().getWorld(); + player.getChunkTrackingView().forEach(pos -> { -+ final org.bukkit.Chunk chunk = world.getChunkAt(pos.longKey); ++ final org.bukkit.Chunk chunk = world.getChunkAt(pos.pack()); + chunks.add(chunk); + }); + return ObjectSets.unmodifiable(chunks); + } + + public static boolean isChunkSent(final ServerPlayer player, final long chunkKey) { -+ return player.getChunkTrackingView().contains(new ChunkPos(chunkKey)); ++ return player.getChunkTrackingView().contains(ChunkPos.unpack(chunkKey)); + } + -+ public static boolean isSpiderCollidingWithWorldBorder(final Spider spider) { -+ return true; // ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isCollidingWithBorder(spider.level().getWorldBorder(), spider.getBoundingBox().inflate(ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) ++ public static boolean isCollidingWithWorldBorder(final Entity entity) { ++ final net.minecraft.world.level.border.WorldBorder border = entity.level().getWorldBorder(); ++ // Inflate by +EPSILON as collision will just barely place us outside border ++ return net.minecraft.world.phys.shapes.Shapes.joinIsNotEmpty( ++ border.getCollisionShape(), ++ net.minecraft.world.phys.shapes.Shapes.create(entity.getBoundingBox().inflate(net.minecraft.util.Util.COLLISION_EPSILON)), net.minecraft.world.phys.shapes.BooleanOp.AND ++ ) && border.isInsideCloseToBorder(entity, entity.getBoundingBox()); ++ // return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isCollidingWithBorder(border, entity.getBoundingBox().inflate(ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) && border.isInsideCloseToBorder(entity, entity.getBoundingBox()) + } + + public static void dumpAllChunkLoadInfo(net.minecraft.server.MinecraftServer server, boolean isLongTimeout) { @@ -91,38 +96,38 @@ + private static void dumpEntity(final Entity entity) { + } + -+ public static org.bukkit.entity.Entity[] getChunkEntities(net.minecraft.server.level.ServerLevel world, int chunkX, int chunkZ) { -+ world.getChunk(chunkX, chunkZ); // ensure full loaded ++ public static org.bukkit.entity.Entity[] getChunkEntities(net.minecraft.server.level.ServerLevel level, int chunkX, int chunkZ) { ++ level.getChunk(chunkX, chunkZ); // ensure full loaded + -+ net.minecraft.world.level.entity.PersistentEntitySectionManager entityManager = world.entityManager; -+ long pair = ChunkPos.asLong(chunkX, chunkZ); ++ net.minecraft.world.level.entity.PersistentEntitySectionManager entityManager = level.entityManager; ++ long chunkKey = ChunkPos.pack(chunkX, chunkZ); + -+ if (entityManager.areEntitiesLoaded(pair)) { ++ if (entityManager.areEntitiesLoaded(chunkKey)) { + return entityManager.getEntities(new ChunkPos(chunkX, chunkZ)).stream() + .map(net.minecraft.world.entity.Entity::getBukkitEntity) + .filter(java.util.Objects::nonNull).toArray(org.bukkit.entity.Entity[]::new); + } + -+ entityManager.ensureChunkQueuedForLoad(pair); // Start entity loading ++ entityManager.ensureChunkQueuedForLoad(chunkKey); // Start entity loading + + // SPIGOT-6772: Use entity mailbox and re-schedule entities if they get unloaded + net.minecraft.util.thread.ConsecutiveExecutor mailbox = ((net.minecraft.world.level.chunk.storage.EntityStorage) entityManager.permanentStorage).entityDeserializerQueue; + java.util.function.BooleanSupplier supplier = () -> { + // only execute inbox if our entities are not present -+ if (entityManager.areEntitiesLoaded(pair)) { ++ if (entityManager.areEntitiesLoaded(chunkKey)) { + return true; + } + -+ if (!entityManager.isPending(pair)) { ++ if (!entityManager.isPending(chunkKey)) { + // Our entities got unloaded, this should normally not happen. -+ entityManager.ensureChunkQueuedForLoad(pair); // Re-start entity loading ++ entityManager.ensureChunkQueuedForLoad(chunkKey); // Re-start entity loading + } + + // tick loading inbox, which loads the created entities to the world + // (if present) + entityManager.tick(); + // check if our entities are loaded -+ return entityManager.areEntitiesLoaded(pair); ++ return entityManager.areEntitiesLoaded(chunkKey); + }; + + // now we wait until the entities are loaded, @@ -141,10 +146,10 @@ + .filter(java.util.Objects::nonNull).toArray(org.bukkit.entity.Entity[]::new); + } + -+ public static java.util.Collection getPluginChunkTickets(net.minecraft.server.level.ServerLevel world, ++ public static java.util.Collection getPluginChunkTickets(net.minecraft.server.level.ServerLevel level, + int x, int z) { -+ net.minecraft.server.level.DistanceManager chunkDistanceManager = world.getChunkSource().chunkMap.distanceManager; -+ List tickets = chunkDistanceManager.ticketStorage.tickets.get(ChunkPos.asLong(x, z)); ++ net.minecraft.server.level.DistanceManager chunkDistanceManager = level.getChunkSource().chunkMap.distanceManager; ++ List tickets = chunkDistanceManager.ticketStorage.tickets.get(ChunkPos.pack(x, z)); + + if (tickets == null) { + return java.util.Collections.emptyList(); @@ -160,9 +165,9 @@ + return ret.build(); + } + -+ public static Map> getPluginChunkTickets(net.minecraft.server.level.ServerLevel world) { ++ public static Map> getPluginChunkTickets(net.minecraft.server.level.ServerLevel level) { + Map> ret = new HashMap<>(); -+ net.minecraft.server.level.DistanceManager chunkDistanceManager = world.getChunkSource().chunkMap.distanceManager; ++ net.minecraft.server.level.DistanceManager chunkDistanceManager = level.getChunkSource().chunkMap.distanceManager; + + for (it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry> chunkTickets : chunkDistanceManager.ticketStorage.tickets.long2ObjectEntrySet()) { + long chunkKey = chunkTickets.getLongKey(); @@ -175,53 +180,53 @@ + } + + if (chunk == null) { -+ chunk = world.getWorld().getChunkAt(ChunkPos.getX(chunkKey), ChunkPos.getZ(chunkKey)); ++ chunk = level.getWorld().getChunkAt(ChunkPos.getX(chunkKey), ChunkPos.getZ(chunkKey)); + } + -+ ret.computeIfAbsent((org.bukkit.plugin.Plugin) ticket.getIdentifier(), (key) -> com.google.common.collect.ImmutableList.builder()).add(chunk); ++ ret.computeIfAbsent((org.bukkit.plugin.Plugin) ticket.getIdentifier(), _ -> com.google.common.collect.ImmutableList.builder()).add(chunk); + } + } + + return ret.entrySet().stream().collect(com.google.common.collect.ImmutableMap.toImmutableMap(Map.Entry::getKey, (entry) -> entry.getValue().build())); + } + -+ public static int getViewDistance(net.minecraft.server.level.ServerLevel world) { -+ return world.getChunkSource().chunkMap.serverViewDistance; ++ public static int getViewDistance(net.minecraft.server.level.ServerLevel level) { ++ return level.getChunkSource().chunkMap.serverViewDistance; + } + -+ public static int getSimulationDistance(net.minecraft.server.level.ServerLevel world) { -+ return world.getChunkSource().chunkMap.getDistanceManager().simulationDistance; ++ public static int getSimulationDistance(net.minecraft.server.level.ServerLevel level) { ++ return level.getChunkSource().chunkMap.getDistanceManager().simulationDistance; + } + -+ public static int getSendViewDistance(net.minecraft.server.level.ServerLevel world) { -+ return getViewDistance(world); ++ public static int getSendViewDistance(net.minecraft.server.level.ServerLevel level) { ++ return getViewDistance(level); + } + -+ public static void setViewDistance(net.minecraft.server.level.ServerLevel world, int distance) { ++ public static void setViewDistance(net.minecraft.server.level.ServerLevel level, int distance) { + if (distance < 2 || distance > 32) { + throw new IllegalArgumentException("View distance " + distance + " is out of range of [2, 32]"); + } -+ world.chunkSource.chunkMap.setServerViewDistance(distance); ++ level.chunkSource.chunkMap.setServerViewDistance(distance); + } + -+ public static void setSimulationDistance(net.minecraft.server.level.ServerLevel world, int distance) { ++ public static void setSimulationDistance(net.minecraft.server.level.ServerLevel level, int distance) { + if (distance < 2 || distance > 32) { + throw new IllegalArgumentException("Simulation distance " + distance + " is out of range of [2, 32]"); + } -+ world.chunkSource.chunkMap.distanceManager.updateSimulationDistance(distance); ++ level.chunkSource.chunkMap.distanceManager.updateSimulationDistance(distance); + } + -+ public static void setSendViewDistance(net.minecraft.server.level.ServerLevel world, int distance) { ++ public static void setSendViewDistance(net.minecraft.server.level.ServerLevel level, int distance) { + throw new UnsupportedOperationException("Not implemented yet"); + } + -+ public static void tickEntityManager(net.minecraft.server.level.ServerLevel world) { -+ world.entityManager.tick(); ++ public static void tickEntityManager(net.minecraft.server.level.ServerLevel level) { ++ level.entityManager.tick(); + } + -+ public static void closeEntityManager(net.minecraft.server.level.ServerLevel world, boolean save) { ++ public static void closeEntityManager(net.minecraft.server.level.ServerLevel level, boolean save) { + try { -+ world.entityManager.close(save); ++ level.entityManager.close(save); + } catch (final java.io.IOException exception) { + throw new RuntimeException("Failed to close entity manager", exception); + } diff --git a/paper-server/patches/sources/net/minecraft/ChatFormatting.java.patch b/paper-server/patches/sources/net/minecraft/ChatFormatting.java.patch index eb3d87a70583..5a2523953d3a 100644 --- a/paper-server/patches/sources/net/minecraft/ChatFormatting.java.patch +++ b/paper-server/patches/sources/net/minecraft/ChatFormatting.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/ChatFormatting.java +++ b/net/minecraft/ChatFormatting.java -@@ -114,6 +_,19 @@ - return friendlyName == null ? null : FORMATTING_BY_NAME.get(cleanName(friendlyName)); +@@ -112,6 +_,19 @@ + return name == null ? null : FORMATTING_BY_NAME.get(cleanName(name)); } + // Paper start - add method to get by hex value @@ -17,6 +17,6 @@ + } + // Paper end - add method to get by hex value + - public static @Nullable ChatFormatting getById(int index) { - if (index < 0) { + public static @Nullable ChatFormatting getById(final int id) { + if (id < 0) { return RESET; diff --git a/paper-server/patches/sources/net/minecraft/CrashReport.java.patch b/paper-server/patches/sources/net/minecraft/CrashReport.java.patch index da57e1a408d7..be129bb6bd26 100644 --- a/paper-server/patches/sources/net/minecraft/CrashReport.java.patch +++ b/paper-server/patches/sources/net/minecraft/CrashReport.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/CrashReport.java +++ b/net/minecraft/CrashReport.java @@ -34,6 +_,7 @@ - public CrashReport(String title, Throwable exception) { + public CrashReport(final String title, final Throwable t) { this.title = title; - this.exception = exception; + this.exception = t; + this.systemReport.setDetail("CraftBukkit Information", new org.bukkit.craftbukkit.CraftCrashReport()); // CraftBukkit } diff --git a/paper-server/patches/sources/net/minecraft/advancements/AdvancementTree.java.patch b/paper-server/patches/sources/net/minecraft/advancements/AdvancementTree.java.patch index 6c17a3351155..5d37727fb21b 100644 --- a/paper-server/patches/sources/net/minecraft/advancements/AdvancementTree.java.patch +++ b/paper-server/patches/sources/net/minecraft/advancements/AdvancementTree.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/advancements/AdvancementTree.java +++ b/net/minecraft/advancements/AdvancementTree.java @@ -25,7 +_,7 @@ - this.remove(advancementNode); + this.remove(child); } - LOGGER.info("Forgot about advancement {}", node.holder()); @@ -17,4 +17,4 @@ + // LOGGER.info("Loaded {} advancements", this.nodes.size()); // CraftBukkit - moved to AdvancementDataWorld#reload // Paper - Improve logging and errors; you say it was moved... but it wasn't :) it should be moved however, since this is called when the API creates an advancement } - private boolean tryInsert(AdvancementHolder advancement) { + private boolean tryInsert(final AdvancementHolder holder) { diff --git a/paper-server/patches/sources/net/minecraft/advancements/DisplayInfo.java.patch b/paper-server/patches/sources/net/minecraft/advancements/DisplayInfo.java.patch index c7956c2de5f2..c14505baa730 100644 --- a/paper-server/patches/sources/net/minecraft/advancements/DisplayInfo.java.patch +++ b/paper-server/patches/sources/net/minecraft/advancements/DisplayInfo.java.patch @@ -7,4 +7,4 @@ + public final io.papermc.paper.advancement.AdvancementDisplay paper = new io.papermc.paper.advancement.PaperAdvancementDisplay(this); // Paper - Add more advancement API public DisplayInfo( - ItemStack icon, + final ItemStackTemplate icon, diff --git a/paper-server/patches/sources/net/minecraft/advancements/criterion/LocationPredicate.java.patch b/paper-server/patches/sources/net/minecraft/advancements/criterion/LocationPredicate.java.patch index d559a186341d..1f1c3031b202 100644 --- a/paper-server/patches/sources/net/minecraft/advancements/criterion/LocationPredicate.java.patch +++ b/paper-server/patches/sources/net/minecraft/advancements/criterion/LocationPredicate.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/advancements/criterion/LocationPredicate.java +++ b/net/minecraft/advancements/criterion/LocationPredicate.java @@ -44,7 +_,7 @@ - public boolean matches(ServerLevel level, double x, double y, double z) { + public boolean matches(final ServerLevel level, final double x, final double y, final double z) { if (this.position.isPresent() && !this.position.get().matches(x, y, z)) { return false; - } else if (this.dimension.isPresent() && this.dimension.get() != level.dimension()) { + } else if (this.dimension.isPresent() && this.dimension.get() != (io.papermc.paper.configuration.GlobalConfiguration.get().misc.strictAdvancementDimensionCheck ? level.dimension() : org.bukkit.craftbukkit.util.CraftDimensionUtil.getMainDimensionKey(level))) { // Paper - Add option for strict advancement dimension checks return false; } else { - BlockPos blockPos = BlockPos.containing(x, y, z); + BlockPos pos = BlockPos.containing(x, y, z); diff --git a/paper-server/patches/sources/net/minecraft/advancements/criterion/SimpleCriterionTrigger.java.patch b/paper-server/patches/sources/net/minecraft/advancements/criterion/SimpleCriterionTrigger.java.patch index 8764e1c98d7a..a1e2ae309c8c 100644 --- a/paper-server/patches/sources/net/minecraft/advancements/criterion/SimpleCriterionTrigger.java.patch +++ b/paper-server/patches/sources/net/minecraft/advancements/criterion/SimpleCriterionTrigger.java.patch @@ -1,52 +1,52 @@ --- a/net/minecraft/advancements/criterion/SimpleCriterionTrigger.java +++ b/net/minecraft/advancements/criterion/SimpleCriterionTrigger.java -@@ -15,41 +_,41 @@ - import net.minecraft.world.level.storage.loot.LootContext; +@@ -17,41 +_,41 @@ + import net.minecraft.world.level.storage.loot.ValidationContextSource; public abstract class SimpleCriterionTrigger implements CriterionTrigger { - private final Map>> players = Maps.newIdentityHashMap(); + // private final Map>> players = Maps.newIdentityHashMap(); // Paper - fix PlayerAdvancements leak; moved into PlayerAdvancements to fix memory leak @Override - public final void addPlayerListener(PlayerAdvancements playerAdvancements, CriterionTrigger.Listener listener) { -- this.players.computeIfAbsent(playerAdvancements, advancements -> Sets.newHashSet()).add(listener); -+ playerAdvancements.criterionData.computeIfAbsent(this, managerx -> Sets.newHashSet()).add(listener); // Paper - fix PlayerAdvancements leak + public final void addPlayerListener(final PlayerAdvancements player, final CriterionTrigger.Listener listener) { +- this.players.computeIfAbsent(player, k -> Sets.newHashSet()).add(listener); ++ player.criterionData.computeIfAbsent(this, managerx -> Sets.newHashSet()).add(listener); // Paper - fix PlayerAdvancements leak } @Override - public final void removePlayerListener(PlayerAdvancements playerAdvancements, CriterionTrigger.Listener listener) { -- Set> set = this.players.get(playerAdvancements); -+ Set> set = (Set) playerAdvancements.criterionData.get(this); // Paper - fix PlayerAdvancements leak - if (set != null) { - set.remove(listener); - if (set.isEmpty()) { -- this.players.remove(playerAdvancements); -+ playerAdvancements.criterionData.remove(this); // Paper - fix PlayerAdvancements leak + public final void removePlayerListener(final PlayerAdvancements player, final CriterionTrigger.Listener listener) { +- Set> listeners = this.players.get(player); ++ Set> listeners = (Set) player.criterionData.get(this); // Paper - fix PlayerAdvancements leak + if (listeners != null) { + listeners.remove(listener); + if (listeners.isEmpty()) { +- this.players.remove(player); ++ player.criterionData.remove(this); // Paper - fix PlayerAdvancements leak } } } @Override - public final void removePlayerListeners(PlayerAdvancements playerAdvancements) { -- this.players.remove(playerAdvancements); -+ playerAdvancements.criterionData.remove(this); // Paper - fix PlayerAdvancements leak + public final void removePlayerListeners(final PlayerAdvancements player) { +- this.players.remove(player); ++ player.criterionData.remove(this); // Paper - fix PlayerAdvancements leak } - protected void trigger(ServerPlayer player, Predicate testTrigger) { + protected void trigger(final ServerPlayer player, final Predicate matcher) { PlayerAdvancements advancements = player.getAdvancements(); -- Set> set = this.players.get(advancements); -+ Set> set = (Set) advancements.criterionData.get(this); // Paper - fix PlayerAdvancements leak - if (set != null && !set.isEmpty()) { -- LootContext lootContext = EntityPredicate.createContext(player, player); -+ LootContext lootContext = null; // EntityPredicate.createContext(player, player); // Paper - Perf: lazily create LootContext for criterions - List> list = null; +- Set> allListeners = this.players.get(advancements); ++ Set> allListeners = (Set) advancements.criterionData.get(this); // Paper - fix PlayerAdvancements leak + if (allListeners != null && !allListeners.isEmpty()) { +- LootContext playerContext = EntityPredicate.createContext(player, player); ++ LootContext playerContext = null; // EntityPredicate.createContext(player, player); // Paper - Perf: lazily create LootContext for criterions + List> listeners = null; - for (CriterionTrigger.Listener listener : set) { - T simpleInstance = listener.trigger(); - if (testTrigger.test(simpleInstance)) { - Optional optional = simpleInstance.player(); -- if (optional.isEmpty() || optional.get().matches(lootContext)) { -+ if (optional.isEmpty() || optional.get().matches(lootContext = (lootContext == null ? EntityPredicate.createContext(player, player) : lootContext))) { // Paper - Perf: lazily create LootContext for criterions - if (list == null) { - list = Lists.newArrayList(); + for (CriterionTrigger.Listener listener : allListeners) { + T triggerInstance = listener.trigger(); + if (matcher.test(triggerInstance)) { + Optional predicate = triggerInstance.player(); +- if (predicate.isEmpty() || predicate.get().matches(playerContext)) { ++ if (predicate.isEmpty() || predicate.get().matches(playerContext = (playerContext == null ? EntityPredicate.createContext(player, player) : playerContext))) { // Paper - Perf: lazily create LootContext for criterions + if (listeners == null) { + listeners = Lists.newArrayList(); } diff --git a/paper-server/patches/sources/net/minecraft/commands/CommandSourceStack.java.patch b/paper-server/patches/sources/net/minecraft/commands/CommandSourceStack.java.patch index 4266c14e397a..fc3e51faf2bf 100644 --- a/paper-server/patches/sources/net/minecraft/commands/CommandSourceStack.java.patch +++ b/paper-server/patches/sources/net/minecraft/commands/CommandSourceStack.java.patch @@ -4,8 +4,8 @@ import net.minecraft.world.phys.Vec3; import org.jspecify.annotations.Nullable; --public class CommandSourceStack implements ExecutionCommandSource, SharedSuggestionProvider { -+public class CommandSourceStack implements ExecutionCommandSource, SharedSuggestionProvider, io.papermc.paper.command.brigadier.PaperCommandSourceStack { // Paper - Brigadier API +-public class CommandSourceStack implements SharedSuggestionProvider, ExecutionCommandSource { ++public class CommandSourceStack implements SharedSuggestionProvider, ExecutionCommandSource, io.papermc.paper.command.brigadier.PaperCommandSourceStack { // Paper - Brigadier API public static final SimpleCommandExceptionType ERROR_NOT_PLAYER = new SimpleCommandExceptionType(Component.translatable("permissions.requires.player")); public static final SimpleCommandExceptionType ERROR_NOT_ENTITY = new SimpleCommandExceptionType(Component.translatable("permissions.requires.entity")); public final CommandSource source; @@ -16,7 +16,7 @@ + public boolean bypassSelectorPermissions = false; // Paper - add bypass for selector permissions public CommandSourceStack( - CommandSource source, + final CommandSource source, @@ -190,6 +_,30 @@ ); } @@ -45,7 +45,7 @@ + } + // Paper end - Expose 'with' functions from the CommandSourceStack + - public CommandSourceStack withRotation(Vec2 rotation) { + public CommandSourceStack withRotation(final Vec2 rotation) { return this.rotation.equals(rotation) ? this @@ -379,6 +_,32 @@ @@ -84,25 +84,25 @@ @@ -482,20 +_,25 @@ GameRules gameRules = this.level.getGameRules(); if (gameRules.get(GameRules.SEND_COMMAND_FEEDBACK)) { - for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) { -- if (serverPlayer.commandSource() != this.source && this.server.getPlayerList().isOp(serverPlayer.nameAndId())) { -+ if (serverPlayer.commandSource() != this.source && serverPlayer.getBukkitEntity().hasPermission("minecraft.admin.command_feedback")) { // CraftBukkit - serverPlayer.sendSystemMessage(component); + for (ServerPlayer player : this.server.getPlayerList().getPlayers()) { +- if (player.commandSource() != this.source && this.server.getPlayerList().isOp(player.nameAndId())) { ++ if (player.commandSource() != this.source && player.getBukkitEntity().hasPermission("minecraft.admin.command_feedback")) { // CraftBukkit + player.sendSystemMessage(broadcast); } } } - if (this.source != this.server && gameRules.get(GameRules.LOG_ADMIN_COMMANDS)) { + if (this.source != this.server && gameRules.get(GameRules.LOG_ADMIN_COMMANDS) && !org.spigotmc.SpigotConfig.silentCommandBlocks) { // Spigot - this.server.sendSystemMessage(component); + this.server.sendSystemMessage(broadcast); } } - public void sendFailure(Component message) { + public void sendFailure(final Component message) { + // Paper start - Add UnknownCommandEvent + this.sendFailure(message, true); + } -+ public void sendFailure(Component message, boolean withStyle) { ++ public void sendFailure(final Component message, final boolean withStyle) { + // Paper end - Add UnknownCommandEvent if (this.source.acceptsFailure() && !this.silent) { - this.source.sendSystemMessage(Component.empty().append(message).withStyle(ChatFormatting.RED)); @@ -119,7 +119,7 @@ } @Override -@@ -586,4 +_,16 @@ +@@ -584,4 +_,16 @@ public boolean isSilent() { return this.silent; } diff --git a/paper-server/patches/sources/net/minecraft/commands/Commands.java.patch b/paper-server/patches/sources/net/minecraft/commands/Commands.java.patch index dfe4e32943b8..cfed6bb42dec 100644 --- a/paper-server/patches/sources/net/minecraft/commands/Commands.java.patch +++ b/paper-server/patches/sources/net/minecraft/commands/Commands.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/commands/Commands.java +++ b/net/minecraft/commands/Commands.java -@@ -155,6 +_,7 @@ +@@ -157,6 +_,7 @@ import org.slf4j.Logger; public class Commands { @@ -8,27 +8,27 @@ public static final String COMMAND_PREFIX = "/"; private static final ThreadLocal<@Nullable ExecutionContext> CURRENT_EXECUTION_CONTEXT = new ThreadLocal<>(); private static final Logger LOGGER = LogUtils.getLogger(); -@@ -179,6 +_,7 @@ +@@ -181,6 +_,7 @@ @Override - public boolean isRestricted(CommandNode node) { + public boolean isRestricted(final CommandNode node) { + if (node.getRequirement() instanceof RestrictedMarker) return true; // Paper - restricted api Predicate requirement = node.getRequirement(); return !requirement.test(this.noPermissionSource); } -@@ -186,6 +_,11 @@ +@@ -188,6 +_,11 @@ private final CommandDispatcher dispatcher = new CommandDispatcher<>(); - public Commands(Commands.CommandSelection selection, CommandBuildContext context) { + public Commands(final Commands.CommandSelection commandSelection, final CommandBuildContext context) { + // Paper start - Brigadier API - modern minecraft overloads that do not use redirects but are copies instead -+ this(selection, context, false); ++ this(commandSelection, context, false); + } -+ public Commands(Commands.CommandSelection selection, CommandBuildContext context, final boolean modern) { ++ public Commands(final Commands.CommandSelection commandSelection, final CommandBuildContext context, final boolean modern) { + // Paper end - Brigadier API - modern minecraft overloads that do not use redirects but are copies instead AdvancementCommands.register(this.dispatcher); AttributeCommand.register(this.dispatcher, context); ExecuteCommand.register(this.dispatcher, context); -@@ -296,6 +_,42 @@ +@@ -299,6 +_,42 @@ PublishCommand.register(this.dispatcher); } @@ -71,21 +71,21 @@ this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer()); } -@@ -315,6 +_,13 @@ +@@ -318,6 +_,13 @@ } - public void performCommand(ParseResults parseResults, String command) { + public void performCommand(final ParseResults command, final String commandString) { + // Paper start -+ this.performCommand(parseResults, command, false); ++ this.performCommand(command, commandString, false); + } + -+ public void performCommand(ParseResults parseResults, String command, boolean throwCommandError) { ++ public void performCommand(final ParseResults command, final String commandString, final boolean throwCommandError) { + org.spigotmc.AsyncCatcher.catchOp("Cannot perform command async"); + // Paper end - CommandSourceStack commandSourceStack = parseResults.getContext().getSource(); - Profiler.get().push(() -> "/" + command); - ContextChain contextChain = finishParsing(parseResults, command, commandSourceStack); -@@ -328,10 +_,13 @@ + CommandSourceStack sender = command.getContext().getSource(); + Profiler.get().push(() -> "/" + commandString); + ContextChain commandChain = finishParsing(command, commandString, sender); +@@ -331,10 +_,12 @@ ) ); } @@ -93,51 +93,50 @@ + // Paper start + } catch (Throwable var12) { // always gracefully handle it, no matter how bad:tm: + if (throwCommandError) throw var12; // rethrow directly if requested -+ // Paper end - MutableComponent mutableComponent = Component.literal(var12.getMessage() == null ? var12.getClass().getName() : var12.getMessage()); + MutableComponent hover = Component.literal(var12.getMessage() == null ? var12.getClass().getName() : var12.getMessage()); - if (LOGGER.isDebugEnabled()) { -- LOGGER.error("Command exception: /{}", command, var12); -+ LOGGER.error("Command exception: /{}", command, var12); // Paper - always show execution exception in console log -+ if (commandSourceStack.getServer().isDebugging() || LOGGER.isDebugEnabled()) { // Paper - Debugging +- LOGGER.error("Command exception: /{}", commandString, var12); ++ LOGGER.error("Command exception: /{}", commandString, var12); // Paper - always show execution exception in console log ++ if (sender.getServer().isDebugging() || LOGGER.isDebugEnabled()) { // Paper - Debugging StackTraceElement[] stackTrace = var12.getStackTrace(); for (int i = 0; i < Math.min(stackTrace.length, 3); i++) { -@@ -364,7 +_,11 @@ - return ContextChain.tryFlatten(parseResults.getContext().build(command)) - .orElseThrow(() -> CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(parseResults.getReader())); +@@ -365,7 +_,11 @@ + return ContextChain.tryFlatten(command.getContext().build(commandString)) + .orElseThrow(() -> CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(command.getReader())); } catch (CommandSyntaxException var7) { -- source.sendFailure(ComponentUtils.fromMessage(var7.getRawMessage())); +- sender.sendFailure(ComponentUtils.fromMessage(var7.getRawMessage())); + // Paper start - Add UnknownCommandEvent + final net.kyori.adventure.text.TextComponent.Builder builder = net.kyori.adventure.text.Component.text(); -+ // source.sendFailure(ComponentUtils.fromMessage(var7.getRawMessage())); ++ // sender.sendFailure(ComponentUtils.fromMessage(var7.getRawMessage())); + builder.color(net.kyori.adventure.text.format.NamedTextColor.RED).append(io.papermc.paper.command.brigadier.MessageComponentSerializer.message().deserialize(var7.getRawMessage())); + // Paper end - Add UnknownCommandEvent if (var7.getInput() != null && var7.getCursor() >= 0) { - int min = Math.min(var7.getInput().length(), var7.getCursor()); - MutableComponent mutableComponent = Component.empty() -@@ -381,7 +_,17 @@ + int cursor = Math.min(var7.getInput().length(), var7.getCursor()); + MutableComponent context = Component.empty() +@@ -382,7 +_,17 @@ } - mutableComponent.append(Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC)); -- source.sendFailure(mutableComponent); + context.append(Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC)); +- sender.sendFailure(context); + // Paper start - Add UnknownCommandEvent -+ // source.sendFailure(mutableComponent); ++ // sender.sendFailure(context); + builder + .append(net.kyori.adventure.text.Component.newline()) -+ .append(io.papermc.paper.adventure.PaperAdventure.asAdventure(mutableComponent)); ++ .append(io.papermc.paper.adventure.PaperAdventure.asAdventure(context)); + } -+ org.bukkit.event.command.UnknownCommandEvent event = new org.bukkit.event.command.UnknownCommandEvent(source, command, org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty() ? null : builder.build()); ++ org.bukkit.event.command.UnknownCommandEvent event = new org.bukkit.event.command.UnknownCommandEvent(sender, commandString, org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty() ? null : builder.build()); + org.bukkit.Bukkit.getServer().getPluginManager().callEvent(event); + if (event.message() != null) { -+ source.sendFailure(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); ++ sender.sendFailure(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); + // Paper end - Add UnknownCommandEvent } return null; -@@ -409,17 +_,110 @@ +@@ -410,19 +_,112 @@ } - public void sendCommands(ServerPlayer player) { + public void sendCommands(final ServerPlayer player) { + // Paper start - Send empty commands if tab completion is disabled + if (org.spigotmc.SpigotConfig.tabComplete < 0) { + player.connection.send(new ClientboundCommandsPacket(new RootCommandNode<>(), COMMAND_NODE_INSPECTOR)); @@ -165,51 +164,53 @@ + + private void sendAsync(ServerPlayer player, java.util.Collection> dispatcherRootChildren) { + // Paper end - Perf: Async command map building - Map, CommandNode> map = new HashMap<>(); - RootCommandNode rootCommandNode = new RootCommandNode<>(); - map.put(this.dispatcher.getRoot(), rootCommandNode); -- fillUsableCommands(this.dispatcher.getRoot(), rootCommandNode, player.createCommandSourceStack(), map); -+ fillUsableCommands(dispatcherRootChildren, rootCommandNode, player.createCommandSourceStack(), map); // Paper - Perf: Async command map building; pass copy of children + Map, CommandNode> playerCommands = new HashMap<>(); + RootCommandNode root = new RootCommandNode<>(); + playerCommands.put(this.dispatcher.getRoot(), root); +- fillUsableCommands(this.dispatcher.getRoot(), root, player.createCommandSourceStack(), playerCommands); ++ fillUsableCommands(dispatcherRootChildren, root, player.createCommandSourceStack(), playerCommands); // Paper - Perf: Async command map building; pass copy of children + + java.util.Collection bukkit = new java.util.LinkedHashSet<>(); -+ for (CommandNode node : rootCommandNode.getChildren()) { ++ for (CommandNode node : root.getChildren()) { + bukkit.add(node.getName()); + } + // Paper start - Perf: Async command map building -+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootCommandNode, false).callEvent(); // Paper - Brigadier API ++ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) root, false).callEvent(); // Paper - Brigadier API + net.minecraft.server.MinecraftServer.getServer().execute(() -> { -+ runSync(player, bukkit, rootCommandNode); ++ runSync(player, bukkit, root); + }); + } + -+ private void runSync(ServerPlayer player, java.util.Collection bukkit, RootCommandNode rootCommandNode) { ++ private void runSync(ServerPlayer player, java.util.Collection bukkit, RootCommandNode root) { + // Paper end - Perf: Async command map building -+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootCommandNode, true).callEvent(); // Paper - Brigadier API ++ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) root, true).callEvent(); // Paper - Brigadier API + org.bukkit.event.player.PlayerCommandSendEvent event = new org.bukkit.event.player.PlayerCommandSendEvent(player.getBukkitEntity(), new java.util.LinkedHashSet<>(bukkit)); + event.getPlayer().getServer().getPluginManager().callEvent(event); + + // Remove labels that were removed during the event + for (String orig : bukkit) { + if (!event.getCommands().contains(orig)) { -+ rootCommandNode.removeCommand(orig); ++ root.removeCommand(orig); + } + } + // CraftBukkit end - player.connection.send(new ClientboundCommandsPacket(rootCommandNode, COMMAND_NODE_INSPECTOR)); + player.connection.send(new ClientboundCommandsPacket(root, COMMAND_NODE_INSPECTOR)); } -- private static void fillUsableCommands(CommandNode root, CommandNode current, S source, Map, CommandNode> output) { -- for (CommandNode commandNode : root.getChildren()) { -+ private static void fillUsableCommands(java.util.Collection> children, CommandNode current, S source, Map, CommandNode> output) { // Paper - Perf: Async command map building; pass copy of children -+ for (CommandNode commandNode : children) { // Paper - Perf: Async command map building; pass copy of children + private static void fillUsableCommands( +- final CommandNode source, final CommandNode target, final S commandFilter, final Map, CommandNode> converted ++ final java.util.Collection> children, final CommandNode target, final S commandFilter, final Map, CommandNode> converted // Paper - Perf: Async command map building; pass copy of children + ) { +- for (CommandNode child : source.getChildren()) { ++ for (CommandNode child : children) { // Paper - Perf: Async command map building; pass copy of children + // Paper start - Brigadier API -+ if (commandNode.clientNode != null) { -+ commandNode = commandNode.clientNode; ++ if (child.clientNode != null) { ++ child = child.clientNode; + } + // Paper end - Brigadier API -+ if (!org.spigotmc.SpigotConfig.sendNamespaced && commandNode.getName().contains(":")) continue; // Spigot - if (commandNode.canUse(source)) { - ArgumentBuilder argumentBuilder = commandNode.createBuilder(); ++ if (!org.spigotmc.SpigotConfig.sendNamespaced && child.getName().contains(":")) continue; // Spigot + if (child.canUse(commandFilter)) { + ArgumentBuilder builder = child.createBuilder(); + // Paper start + /* + Because of how commands can be yeeted right left and center due to bad bukkit practices @@ -226,34 +227,34 @@ + - Do this :) + */ + // Is there an invalid command redirect? -+ if (argumentBuilder.getRedirect() != null && output.get(argumentBuilder.getRedirect()) == null) { ++ if (builder.getRedirect() != null && converted.get(builder.getRedirect()) == null) { + // Create the argument builder with the same values as the specified node, but with a different literal and populated children + -+ final CommandNode redirect = argumentBuilder.getRedirect(); ++ final CommandNode redirect = builder.getRedirect(); + // Diff copied from LiteralCommand#createBuilder -+ final com.mojang.brigadier.builder.LiteralArgumentBuilder builder = com.mojang.brigadier.builder.LiteralArgumentBuilder.literal(commandNode.getName()); -+ builder.requires(redirect.getRequirement()); ++ final com.mojang.brigadier.builder.LiteralArgumentBuilder redirectBuilder = com.mojang.brigadier.builder.LiteralArgumentBuilder.literal(child.getName()); ++ redirectBuilder.requires(redirect.getRequirement()); + // builder.forward(redirect.getRedirect(), redirect.getRedirectModifier(), redirect.isFork()); We don't want to migrate the forward, since it's invalid. + if (redirect.getCommand() != null) { -+ builder.executes(redirect.getCommand()); ++ redirectBuilder.executes(redirect.getCommand()); + } + // Diff copied from LiteralCommand#createBuilder -+ for (final CommandNode child : redirect.getChildren()) { -+ builder.then(child); ++ for (final CommandNode redirectChild : redirect.getChildren()) { ++ redirectBuilder.then(redirectChild); + } + -+ argumentBuilder = builder; ++ builder = redirectBuilder; + } + // Paper end - if (argumentBuilder.getRedirect() != null) { - argumentBuilder.redirect(output.get(argumentBuilder.getRedirect())); + if (builder.getRedirect() != null) { + builder.redirect(converted.get(builder.getRedirect())); } -@@ -428,7 +_,7 @@ - output.put(commandNode, commandNode1); - current.addChild(commandNode1); - if (!commandNode.getChildren().isEmpty()) { -- fillUsableCommands(commandNode, commandNode1, source, output); -+ fillUsableCommands(commandNode.getChildren(), commandNode1, source, output); // Paper - Perf: Async command map building; pass copy of children +@@ -431,7 +_,7 @@ + converted.put(child, node); + target.addChild(node); + if (!child.getChildren().isEmpty()) { +- fillUsableCommands(child, node, commandFilter, converted); ++ fillUsableCommands(child.getChildren(), node, commandFilter, converted); // Paper - Perf: Async command map building; pass copy of children } } } diff --git a/paper-server/patches/sources/net/minecraft/commands/arguments/EntityArgument.java.patch b/paper-server/patches/sources/net/minecraft/commands/arguments/EntityArgument.java.patch index a1eea4fbe882..b6cb3432e751 100644 --- a/paper-server/patches/sources/net/minecraft/commands/arguments/EntityArgument.java.patch +++ b/paper-server/patches/sources/net/minecraft/commands/arguments/EntityArgument.java.patch @@ -1,35 +1,33 @@ --- a/net/minecraft/commands/arguments/EntityArgument.java +++ b/net/minecraft/commands/arguments/EntityArgument.java -@@ -106,9 +_,14 @@ +@@ -107,9 +_,14 @@ } - private EntitySelector parse(StringReader reader, boolean allowSelectors) throws CommandSyntaxException { + private EntitySelector parse(final StringReader reader, final boolean allowSelectors) throws CommandSyntaxException { + // CraftBukkit start + return this.parse(reader, allowSelectors, false); + } -+ public EntitySelector parse(StringReader reader, boolean allowSelectors, boolean overridePermissions) throws CommandSyntaxException { ++ public EntitySelector parse(final StringReader reader, final boolean allowSelectors, final boolean overridePermissions) throws CommandSyntaxException { + // CraftBukkit end - int i = 0; - EntitySelectorParser entitySelectorParser = new EntitySelectorParser(reader, allowSelectors); -- EntitySelector entitySelector = entitySelectorParser.parse(); -+ EntitySelector entitySelector = entitySelectorParser.parse(overridePermissions); // CraftBukkit - if (entitySelector.getMaxResults() > 1 && this.single) { + int start = 0; + EntitySelectorParser parser = new EntitySelectorParser(reader, allowSelectors); +- EntitySelector selector = parser.parse(); ++ EntitySelector selector = parser.parse(overridePermissions); // CraftBukkit + if (selector.getMaxResults() > 1 && this.single) { if (this.playersOnly) { reader.setCursor(0); -@@ -130,9 +_,15 @@ - if (context.getSource() instanceof SharedSuggestionProvider sharedSuggestionProvider) { - StringReader stringReader = new StringReader(builder.getInput()); - stringReader.setCursor(builder.getStart()); +@@ -131,7 +_,13 @@ + if (contextBuilder.getSource() instanceof SharedSuggestionProvider source) { + StringReader reader = new StringReader(builder.getInput()); + reader.setCursor(builder.getStart()); +- EntitySelectorParser parser = new EntitySelectorParser(reader, source.permissions().hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS)); + // Paper start - Fix EntityArgument permissions -+ final boolean permission = sharedSuggestionProvider instanceof CommandSourceStack stack ++ final boolean permission = source instanceof CommandSourceStack stack + ? stack.bypassSelectorPermissions || stack.hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS, "minecraft.command.selector") + // Only CommandSourceStack implements SharedSuggestionProvider. If *somehow* anything else ends up here, try to query its permission level, otherwise yield false. -+ : sharedSuggestionProvider.permissions().hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS); - EntitySelectorParser entitySelectorParser = new EntitySelectorParser( -- stringReader, sharedSuggestionProvider.permissions().hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS) -+ stringReader, permission - ); ++ : source.permissions().hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS); ++ EntitySelectorParser parser = new EntitySelectorParser(reader, permission); + // Paper end - Fix EntityArgument permissions try { - entitySelectorParser.parse(); + parser.parse(); diff --git a/paper-server/patches/sources/net/minecraft/commands/arguments/MessageArgument.java.patch b/paper-server/patches/sources/net/minecraft/commands/arguments/MessageArgument.java.patch index 9ec0535601f1..df018a25de7d 100644 --- a/paper-server/patches/sources/net/minecraft/commands/arguments/MessageArgument.java.patch +++ b/paper-server/patches/sources/net/minecraft/commands/arguments/MessageArgument.java.patch @@ -1,41 +1,42 @@ --- a/net/minecraft/commands/arguments/MessageArgument.java +++ b/net/minecraft/commands/arguments/MessageArgument.java -@@ -41,6 +_,11 @@ +@@ -41,6 +_,12 @@ - public static void resolveChatMessage(CommandContext context, String key, Consumer callback) throws CommandSyntaxException { - MessageArgument.Message message = context.getArgument(key, MessageArgument.Message.class); + public static void resolveChatMessage(final CommandContext context, final String name, final Consumer task) throws CommandSyntaxException { + MessageArgument.Message message = context.getArgument(name, MessageArgument.Message.class); + // Paper start - brig message argument support -+ resolveChatMessage(message, context, key, callback); ++ resolveChatMessage(message, context, name, task); + } -+ public static void resolveChatMessage(MessageArgument.Message message, CommandContext context, String key, Consumer callback) throws CommandSyntaxException { ++ ++ public static void resolveChatMessage(final MessageArgument.Message message, final CommandContext context, final String name, final Consumer task) throws CommandSyntaxException { + // Paper end - brig message argument support - CommandSourceStack commandSourceStack = context.getSource(); - Component component = message.resolveComponent(commandSourceStack); - CommandSigningContext signingContext = commandSourceStack.getSigningContext(); + CommandSourceStack sender = context.getSource(); + Component formatted = message.resolveComponent(sender); + CommandSigningContext signingContext = sender.getSigningContext(); @@ -55,17 +_,21 @@ - private static void resolveSignedMessage(Consumer callback, CommandSourceStack source, PlayerChatMessage message) { - MinecraftServer server = source.getServer(); - CompletableFuture completableFuture = filterPlainText(source, message); -- Component component = server.getChatDecorator().decorate(source.getPlayer(), message.decoratedContent()); -- source.getChatMessageChainer().append(completableFuture, filteredText -> { -- PlayerChatMessage playerChatMessage = message.withUnsignedContent(component).filter(filteredText.mask()); + private static void resolveSignedMessage(final Consumer task, final CommandSourceStack sender, final PlayerChatMessage signedArgument) { + MinecraftServer server = sender.getServer(); + CompletableFuture filteredFuture = filterPlainText(sender, signedArgument); +- Component decorated = server.getChatDecorator().decorate(sender.getPlayer(), signedArgument.decoratedContent()); +- sender.getChatMessageChainer().append(filteredFuture, filtered -> { +- PlayerChatMessage filteredMessage = signedArgument.withUnsignedContent(decorated).filter(filtered.mask()); + // Paper start - support asynchronous chat decoration -+ CompletableFuture componentFuture = server.getChatDecorator().decorate(source.getPlayer(), source, message.decoratedContent()); -+ source.getChatMessageChainer().append(CompletableFuture.allOf(completableFuture, componentFuture), filtered -> { -+ PlayerChatMessage playerChatMessage = message.withUnsignedContent(componentFuture.join()).filter(completableFuture.join().mask()); ++ CompletableFuture decoratedFuture = server.getChatDecorator().decorate(sender.getPlayer(), sender, signedArgument.decoratedContent()); ++ sender.getChatMessageChainer().append(CompletableFuture.allOf(filteredFuture, decoratedFuture), filtered -> { ++ PlayerChatMessage filteredMessage = signedArgument.withUnsignedContent(decoratedFuture.join()).filter(filteredFuture.join().mask()); + // Paper end - support asynchronous chat decoration - callback.accept(playerChatMessage); + task.accept(filteredMessage); }); } - private static void resolveDisguisedMessage(Consumer callback, CommandSourceStack source, PlayerChatMessage message) { - ChatDecorator chatDecorator = source.getServer().getChatDecorator(); -- Component component = chatDecorator.decorate(source.getPlayer(), message.decoratedContent()); -- callback.accept(message.withUnsignedContent(component)); + private static void resolveDisguisedMessage(final Consumer task, final CommandSourceStack sender, final PlayerChatMessage argument) { + ChatDecorator decorator = sender.getServer().getChatDecorator(); +- Component decorated = decorator.decorate(sender.getPlayer(), argument.decoratedContent()); +- task.accept(argument.withUnsignedContent(decorated)); + // Paper start - support asynchronous chat decoration -+ CompletableFuture componentFuture = chatDecorator.decorate(source.getPlayer(), source, message.decoratedContent()); -+ source.getChatMessageChainer().append(componentFuture, (result) -> callback.accept(message.withUnsignedContent(result))); ++ CompletableFuture decoratedFuture = decorator.decorate(sender.getPlayer(), sender, argument.decoratedContent()); ++ sender.getChatMessageChainer().append(decoratedFuture, result -> task.accept(argument.withUnsignedContent(result))); + // Paper end - support asynchronous chat decoration } - private static CompletableFuture filterPlainText(CommandSourceStack source, PlayerChatMessage message) { + private static CompletableFuture filterPlainText(final CommandSourceStack sender, final PlayerChatMessage message) { diff --git a/paper-server/patches/sources/net/minecraft/commands/arguments/selector/EntitySelector.java.patch b/paper-server/patches/sources/net/minecraft/commands/arguments/selector/EntitySelector.java.patch index 936c7f5ec845..a49f86b22048 100644 --- a/paper-server/patches/sources/net/minecraft/commands/arguments/selector/EntitySelector.java.patch +++ b/paper-server/patches/sources/net/minecraft/commands/arguments/selector/EntitySelector.java.patch @@ -1,11 +1,20 @@ --- a/net/minecraft/commands/arguments/selector/EntitySelector.java +++ b/net/minecraft/commands/arguments/selector/EntitySelector.java -@@ -103,7 +_,7 @@ +@@ -50,7 +_,7 @@ + + @Override + protected String errorMessage(final String original, final CommandSyntaxException exception) { +- return "Invalid selector component: " + original + ": " + exception.getMessage(); ++ return "Invalid selector component"; // Paper - limit selector error message + } + } + ); +@@ -119,7 +_,7 @@ } - private void checkPermissions(CommandSourceStack source) throws CommandSyntaxException { -- if (this.usesSelector && !source.permissions().hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS)) { -+ if (!source.bypassSelectorPermissions && this.usesSelector && !source.hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS, "minecraft.command.selector")) { // CraftBukkit // Paper - add bypass for selector perms + private void checkPermissions(final CommandSourceStack sender) throws CommandSyntaxException { +- if (this.usesSelector && !sender.permissions().hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS)) { ++ if (!sender.bypassSelectorPermissions && this.usesSelector && !sender.hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS, "minecraft.command.selector")) { // CraftBukkit // Paper - add bypass for selector perms throw EntityArgument.ERROR_SELECTORS_NOT_ALLOWED.create(); } } diff --git a/paper-server/patches/sources/net/minecraft/commands/arguments/selector/EntitySelectorParser.java.patch b/paper-server/patches/sources/net/minecraft/commands/arguments/selector/EntitySelectorParser.java.patch index f7a84f01766c..f58e1a909da7 100644 --- a/paper-server/patches/sources/net/minecraft/commands/arguments/selector/EntitySelectorParser.java.patch +++ b/paper-server/patches/sources/net/minecraft/commands/arguments/selector/EntitySelectorParser.java.patch @@ -3,16 +3,16 @@ @@ -113,6 +_,11 @@ } - public static boolean allowSelectors(S suggestionProvider) { + public static boolean allowSelectors(final S source) { + // Paper start - Fix EntityArgument permissions -+ if (suggestionProvider instanceof net.minecraft.commands.CommandSourceStack stack) { ++ if (source instanceof net.minecraft.commands.CommandSourceStack stack) { + return stack.bypassSelectorPermissions || stack.hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS, "minecraft.command.selector"); + } + // Paper end - Fix EntityArgument permissions - return suggestionProvider instanceof PermissionSetSupplier permissionSetSupplier - && permissionSetSupplier.permissions().hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS); + return source instanceof PermissionSetSupplier sender && sender.permissions().hasPermission(Permissions.COMMANDS_ENTITY_SELECTORS); } -@@ -195,8 +_,10 @@ + +@@ -194,8 +_,10 @@ }; } @@ -25,7 +25,7 @@ this.suggestions = this::suggestSelector; if (!this.reader.canRead()) { throw ERROR_MISSING_SELECTOR_TYPE.createWithContext(this.reader); -@@ -458,6 +_,12 @@ +@@ -457,6 +_,12 @@ } public EntitySelector parse() throws CommandSyntaxException { @@ -38,7 +38,7 @@ this.startPosition = this.reader.getCursor(); this.suggestions = this::suggestNameOrSelector; if (this.reader.canRead() && this.reader.peek() == '@') { -@@ -466,7 +_,7 @@ +@@ -465,7 +_,7 @@ } this.reader.skip(); diff --git a/paper-server/patches/sources/net/minecraft/commands/arguments/selector/SelectorPattern.java.patch b/paper-server/patches/sources/net/minecraft/commands/arguments/selector/SelectorPattern.java.patch deleted file mode 100644 index 970df07ef368..000000000000 --- a/paper-server/patches/sources/net/minecraft/commands/arguments/selector/SelectorPattern.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/commands/arguments/selector/SelectorPattern.java -+++ b/net/minecraft/commands/arguments/selector/SelectorPattern.java -@@ -13,7 +_,7 @@ - EntitySelectorParser entitySelectorParser = new EntitySelectorParser(new StringReader(pattern), true); - return DataResult.success(new SelectorPattern(pattern, entitySelectorParser.parse())); - } catch (CommandSyntaxException var2) { -- return DataResult.error(() -> "Invalid selector component: " + pattern + ": " + var2.getMessage()); -+ return DataResult.error(() -> "Invalid selector component"); // Paper - limit selector error message - } - } - diff --git a/paper-server/patches/sources/net/minecraft/core/BlockPos.java.patch b/paper-server/patches/sources/net/minecraft/core/BlockPos.java.patch index 0738cd2b3dc0..120188986cf4 100644 --- a/paper-server/patches/sources/net/minecraft/core/BlockPos.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/BlockPos.java.patch @@ -1,83 +1,83 @@ --- a/net/minecraft/core/BlockPos.java +++ b/net/minecraft/core/BlockPos.java -@@ -156,67 +_,84 @@ +@@ -155,67 +_,84 @@ @Override public BlockPos above() { - return this.relative(Direction.UP); -+ return new BlockPos(this.getX(), this.getY() + 1, this.getZ()); // Paper - Perf: Optimize BlockPosition ++ return new BlockPos(this.getX(), this.getY() + 1, this.getZ()); // Paper - Perf: Optimize BlockPos } @Override - public BlockPos above(int distance) { -- return this.relative(Direction.UP, distance); -+ return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() + distance, this.getZ()); // Paper - Perf: Optimize BlockPosition + public BlockPos above(final int steps) { +- return this.relative(Direction.UP, steps); ++ return steps == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() + steps, this.getZ()); // Paper - Perf: Optimize BlockPos } @Override public BlockPos below() { - return this.relative(Direction.DOWN); -+ return new BlockPos(this.getX(), this.getY() - 1, this.getZ()); // Paper - Perf: Optimize BlockPosition ++ return new BlockPos(this.getX(), this.getY() - 1, this.getZ()); // Paper - Perf: Optimize BlockPos } @Override - public BlockPos below(int distance) { -- return this.relative(Direction.DOWN, distance); -+ return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() - distance, this.getZ()); // Paper - Perf: Optimize BlockPosition + public BlockPos below(final int steps) { +- return this.relative(Direction.DOWN, steps); ++ return steps == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() - steps, this.getZ()); // Paper - Perf: Optimize BlockPos } @Override public BlockPos north() { - return this.relative(Direction.NORTH); -+ return new BlockPos(this.getX(), this.getY(), this.getZ() - 1); // Paper - Perf: Optimize BlockPosition ++ return new BlockPos(this.getX(), this.getY(), this.getZ() - 1); // Paper - Perf: Optimize BlockPos } @Override - public BlockPos north(int distance) { -- return this.relative(Direction.NORTH, distance); -+ return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() - distance); // Paper - Perf: Optimize BlockPosition + public BlockPos north(final int steps) { +- return this.relative(Direction.NORTH, steps); ++ return steps == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() - steps); // Paper - Perf: Optimize BlockPos } @Override public BlockPos south() { - return this.relative(Direction.SOUTH); -+ return new BlockPos(this.getX(), this.getY(), this.getZ() + 1); // Paper - Perf: Optimize BlockPosition ++ return new BlockPos(this.getX(), this.getY(), this.getZ() + 1); // Paper - Perf: Optimize BlockPos } @Override - public BlockPos south(int distance) { -- return this.relative(Direction.SOUTH, distance); -+ return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() + distance); // Paper - Perf: Optimize BlockPosition + public BlockPos south(final int steps) { +- return this.relative(Direction.SOUTH, steps); ++ return steps == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() + steps); // Paper - Perf: Optimize BlockPos } @Override public BlockPos west() { - return this.relative(Direction.WEST); -+ return new BlockPos(this.getX() - 1, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition ++ return new BlockPos(this.getX() - 1, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPos } @Override - public BlockPos west(int distance) { -- return this.relative(Direction.WEST, distance); -+ return distance == 0 ? this.immutable() : new BlockPos(this.getX() - distance, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition + public BlockPos west(final int steps) { +- return this.relative(Direction.WEST, steps); ++ return steps == 0 ? this.immutable() : new BlockPos(this.getX() - steps, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPos } @Override public BlockPos east() { - return this.relative(Direction.EAST); -+ return new BlockPos(this.getX() + 1, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition ++ return new BlockPos(this.getX() + 1, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPos } @Override - public BlockPos east(int distance) { -- return this.relative(Direction.EAST, distance); -+ return distance == 0 ? this.immutable() : new BlockPos(this.getX() + distance, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition + public BlockPos east(final int steps) { +- return this.relative(Direction.EAST, steps); ++ return steps == 0 ? this.immutable() : new BlockPos(this.getX() + steps, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPos } @Override - public BlockPos relative(Direction direction) { + public BlockPos relative(final Direction direction) { - return new BlockPos(this.getX() + direction.getStepX(), this.getY() + direction.getStepY(), this.getZ() + direction.getStepZ()); -+ // Paper start - Perf: Optimize BlockPosition ++ // Paper start - Perf: Optimize BlockPos + switch(direction) { + case UP: + return new BlockPos(this.getX(), this.getY() + 1, this.getZ()); @@ -94,43 +94,43 @@ + default: + return new BlockPos(this.getX() + direction.getStepX(), this.getY() + direction.getStepY(), this.getZ() + direction.getStepZ()); + } -+ // Paper end - Perf: Optimize BlockPosition ++ // Paper end - Perf: Optimize BlockPos } @Override -@@ -651,9 +_,9 @@ +@@ -662,9 +_,9 @@ } - public BlockPos.MutableBlockPos set(int x, int y, int z) { + public BlockPos.MutableBlockPos set(final int x, final int y, final int z) { - this.setX(x); - this.setY(y); - this.setZ(z); -+ this.x = x; // Paper - Perf: Manually inline methods in BlockPosition -+ this.y = y; // Paper - Perf: Manually inline methods in BlockPosition -+ this.z = z; // Paper - Perf: Manually inline methods in BlockPosition ++ this.x = x; // Paper - Perf: Manually inline methods in BlockPos ++ this.y = y; // Paper - Perf: Manually inline methods in BlockPos ++ this.z = z; // Paper - Perf: Manually inline methods in BlockPos return this; } -@@ -711,19 +_,19 @@ +@@ -722,19 +_,19 @@ @Override - public BlockPos.MutableBlockPos setX(int x) { + public BlockPos.MutableBlockPos setX(final int x) { - super.setX(x); -+ this.x = x; // Paper - Perf: Manually inline methods in BlockPosition ++ this.x = x; // Paper - Perf: Manually inline methods in BlockPos return this; } @Override - public BlockPos.MutableBlockPos setY(int y) { + public BlockPos.MutableBlockPos setY(final int y) { - super.setY(y); -+ this.y = y; // Paper - Perf: Manually inline methods in BlockPosition ++ this.y = y; // Paper - Perf: Manually inline methods in BlockPos return this; } @Override - public BlockPos.MutableBlockPos setZ(int z) { + public BlockPos.MutableBlockPos setZ(final int z) { - super.setZ(z); -+ this.z = z; // Paper - Perf: Manually inline methods in BlockPosition ++ this.z = z; // Paper - Perf: Manually inline methods in BlockPos return this; } diff --git a/paper-server/patches/sources/net/minecraft/core/ClientAsset.java.patch b/paper-server/patches/sources/net/minecraft/core/ClientAsset.java.patch index 9567a6a67952..fa145277b054 100644 --- a/paper-server/patches/sources/net/minecraft/core/ClientAsset.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/ClientAsset.java.patch @@ -3,9 +3,9 @@ @@ -23,7 +_,7 @@ .map(ClientAsset.ResourceTexture::new, ClientAsset.ResourceTexture::id); - public ResourceTexture(Identifier id) { -- this(id, id.withPath(path -> "textures/" + path + ".png")); -+ this(id, id.withPath(path -> "textures/" + path + ".png")); // Paper - diff on change - io.papermc.paper.registry.data.client.ClientAssetImpl#pathFromIdentifier + public ResourceTexture(final Identifier texture) { +- this(texture, texture.withPath(path -> "textures/" + path + ".png")); ++ this(texture, texture.withPath(path -> "textures/" + path + ".png")); // Paper - diff on change - io.papermc.paper.registry.data.client.ClientAssetImpl#pathFromIdentifier } } diff --git a/paper-server/patches/sources/net/minecraft/core/Direction.java.patch b/paper-server/patches/sources/net/minecraft/core/Direction.java.patch index 4b822775682c..4d521d485ece 100644 --- a/paper-server/patches/sources/net/minecraft/core/Direction.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/Direction.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/core/Direction.java +++ b/net/minecraft/core/Direction.java -@@ -65,6 +_,12 @@ - .sorted(Comparator.comparingInt(direction -> direction.data2d)) +@@ -63,6 +_,12 @@ + .sorted(Comparator.comparingInt(d -> d.data2d)) .toArray(Direction[]::new); + // Paper start - Perf: Inline shift direction fields @@ -13,7 +13,7 @@ private Direction( final int data3d, final int oppositeIndex, -@@ -83,6 +_,11 @@ +@@ -81,6 +_,11 @@ this.normal = normal; this.normalVec3 = Vec3.atLowerCornerOf(normal); this.normalVec3f = new Vector3f(normal.getX(), normal.getY(), normal.getZ()); @@ -24,8 +24,8 @@ + // Paper end - Perf: Inline shift direction fields } - public static Direction[] orderedByNearest(Entity entity) { -@@ -255,15 +_,15 @@ + public static Direction[] orderedByNearest(final Entity entity) { +@@ -253,15 +_,15 @@ } public int getStepX() { diff --git a/paper-server/patches/sources/net/minecraft/core/Holder.java.patch b/paper-server/patches/sources/net/minecraft/core/Holder.java.patch index 34c31f6e8258..9e9ae320bc73 100644 --- a/paper-server/patches/sources/net/minecraft/core/Holder.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/Holder.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/core/Holder.java +++ b/net/minecraft/core/Holder.java -@@ -227,7 +_,7 @@ +@@ -248,7 +_,7 @@ } - void bindTags(Collection> tags) { + void bindTags(final Collection> 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 } - @Override + public void bindComponents(final DataComponentMap components) { diff --git a/paper-server/patches/sources/net/minecraft/core/HolderLookup.java.patch b/paper-server/patches/sources/net/minecraft/core/HolderLookup.java.patch index 17edc3610ce4..fb7582690da5 100644 --- a/paper-server/patches/sources/net/minecraft/core/HolderLookup.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/HolderLookup.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/core/HolderLookup.java +++ b/net/minecraft/core/HolderLookup.java -@@ -68,6 +_,9 @@ +@@ -69,6 +_,9 @@ } public interface RegistryLookup extends HolderLookup, HolderOwner { @@ -10,21 +10,21 @@ ResourceKey> key(); Lifecycle registryLifecycle(); -@@ -80,6 +_,13 @@ +@@ -83,6 +_,13 @@ + Objects.requireNonNull(RegistryLookup.this); + } - default HolderLookup.RegistryLookup filterElements(final Predicate predicate) { - return new HolderLookup.RegistryLookup.Delegate() { + // Paper start - add getValueForCopying + @Override + public Optional getValueForCopying(final ResourceKey resourceKey) { -+ return this.parent().getValueForCopying(resourceKey).filter(predicate); ++ return this.parent().getValueForCopying(resourceKey).filter(filter); + } + // Paper end - add getValueForCopying + @Override public HolderLookup.RegistryLookup parent() { return RegistryLookup.this; -@@ -99,6 +_,13 @@ +@@ -102,6 +_,13 @@ public interface Delegate extends HolderLookup.RegistryLookup { HolderLookup.RegistryLookup parent(); diff --git a/paper-server/patches/sources/net/minecraft/core/HolderSet.java.patch b/paper-server/patches/sources/net/minecraft/core/HolderSet.java.patch index 353c12b7d869..46c4d12eef7f 100644 --- a/paper-server/patches/sources/net/minecraft/core/HolderSet.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/HolderSet.java.patch @@ -6,19 +6,19 @@ private @Nullable List> contents; + private @Nullable Set> typedKeys; // Paper - cache typed key set for constant contains calls - Named(HolderOwner owner, TagKey key) { + Named(final HolderOwner owner, final TagKey key) { this.owner = owner; @@ -172,6 +_,7 @@ - void bind(List> contents) { + void bind(final List> contents) { this.contents = List.copyOf(contents); + this.typedKeys = null; // Paper - reset if tag is re-bound } public TagKey key() { @@ -216,5 +_,15 @@ - public boolean canSerializeIn(HolderOwner owner) { - return this.owner.canSerializeIn(owner); + public boolean canSerializeIn(final HolderOwner context) { + return this.owner.canSerializeIn(context); } + + // Paper start - cache typed key set for constant contains calls diff --git a/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch b/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch index 1c658944e836..3ea652f70b8c 100644 --- a/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch @@ -1,22 +1,23 @@ --- a/net/minecraft/core/MappedRegistry.java +++ b/net/minecraft/core/MappedRegistry.java -@@ -32,16 +_,24 @@ +@@ -33,17 +_,25 @@ public class MappedRegistry implements WritableRegistry { private final ResourceKey> key; private final ObjectList> byId = new ObjectArrayList<>(256); -- private final Reference2IntMap toId = Util.make(new Reference2IntOpenHashMap<>(), map -> map.defaultReturnValue(-1)); +- private final Reference2IntMap toId = Util.make(new Reference2IntOpenHashMap<>(), t -> t.defaultReturnValue(-1)); - private final Map> byLocation = new HashMap<>(); - private final Map, Holder.Reference> byKey = new HashMap<>(); - private final Map> byValue = new IdentityHashMap<>(); - private final Map, RegistrationInfo> registrationInfos = new IdentityHashMap<>(); -+ private final Reference2IntMap toId = Util.make(new Reference2IntOpenHashMap<>(2048), map -> map.defaultReturnValue(-1)); // Paper - Perf: Use bigger expected size to reduce collisions ++ private final Reference2IntMap toId = Util.make(new Reference2IntOpenHashMap<>(2048), t -> t.defaultReturnValue(-1)); // Paper - Perf: Use bigger expected size to reduce collisions + private final Map> byLocation = new HashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions + private final Map, Holder.Reference> byKey = new HashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions + private final Map> byValue = new IdentityHashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions + private final Map, RegistrationInfo> registrationInfos = new IdentityHashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions private Lifecycle registryLifecycle; private final Map, HolderSet.Named> frozenTags = new IdentityHashMap<>(); - MappedRegistry.TagSet allTags = MappedRegistry.TagSet.unbound(); + private MappedRegistry.TagSet allTags = MappedRegistry.TagSet.unbound(); + private @Nullable DataComponentLookup componentLookup; private boolean frozen; private @Nullable Map> unregisteredIntrusiveHolders; + // Paper start - support pre-filling in registry mod API @@ -30,23 +31,23 @@ @Override public Stream> listTags() { -@@ -112,6 +_,7 @@ - this.toId.put(value, size); +@@ -114,6 +_,7 @@ + this.toId.put(value, newId); this.registrationInfos.put(key, registrationInfo); this.registryLifecycle = this.registryLifecycle.add(registrationInfo.lifecycle()); + this.temporaryUnfrozenMap.put(key.identifier(), value); // Paper - support pre-filling in registry mod API - return reference; + return holder; } } -@@ -268,6 +_,7 @@ +@@ -275,6 +_,7 @@ return this; } else { this.frozen = true; + this.temporaryUnfrozenMap.clear(); // Paper - support pre-filling in registry mod API - this.byValue.forEach((object, reference) -> reference.bindValue((T)object)); - List list = this.byKey + this.byValue.forEach((value, holder) -> holder.bindValue((T)value)); + List unboundEntries = this.byKey .entrySet() -@@ -502,4 +_,13 @@ +@@ -522,4 +_,13 @@ Stream> getTags(); } diff --git a/paper-server/patches/sources/net/minecraft/core/RegistrySetBuilder.java.patch b/paper-server/patches/sources/net/minecraft/core/RegistrySetBuilder.java.patch index 150d368bed7f..3f34cbfa1e2b 100644 --- a/paper-server/patches/sources/net/minecraft/core/RegistrySetBuilder.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/RegistrySetBuilder.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/core/RegistrySetBuilder.java +++ b/net/minecraft/core/RegistrySetBuilder.java -@@ -41,6 +_,13 @@ - final Map, Holder.Reference> elements +@@ -42,6 +_,13 @@ + final Map, Holder.Reference> entries ) { return new RegistrySetBuilder.EmptyTagRegistryLookup(owner) { + // Paper start - add getValueForCopying method @@ -13,9 +13,9 @@ + @Override public ResourceKey> key() { - return registryKey; -@@ -121,6 +_,13 @@ - public Optional> lookup(ResourceKey> registryKey) { + return key; +@@ -128,6 +_,13 @@ + public Optional> lookup(final ResourceKey> registryKey) { return getEntry(registryKey).map(Entry::opsInfo); } + diff --git a/paper-server/patches/sources/net/minecraft/core/Rotations.java.patch b/paper-server/patches/sources/net/minecraft/core/Rotations.java.patch index 39a00218deeb..1aeb50910287 100644 --- a/paper-server/patches/sources/net/minecraft/core/Rotations.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/Rotations.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/core/Rotations.java +++ b/net/minecraft/core/Rotations.java @@ -26,11 +_,22 @@ - buffer.writeFloat(value.z); + output.writeFloat(value.z); } }; + // Paper start - add internal method for skipping validation for plugins using userdev diff --git a/paper-server/patches/sources/net/minecraft/core/Vec3i.java.patch b/paper-server/patches/sources/net/minecraft/core/Vec3i.java.patch index 9b131d3e85a4..b77a828a7aae 100644 --- a/paper-server/patches/sources/net/minecraft/core/Vec3i.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/Vec3i.java.patch @@ -1,25 +1,25 @@ --- a/net/minecraft/core/Vec3i.java +++ b/net/minecraft/core/Vec3i.java -@@ -23,9 +_,9 @@ +@@ -22,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); - private int x; - private int y; - private int z; -+ protected int x; // Paper - Perf: Manually inline methods in BlockPosition; protected -+ protected int y; // Paper - Perf: Manually inline methods in BlockPosition; protected -+ protected int z; // Paper - Perf: Manually inline methods in BlockPosition; protected ++ protected int x; // Paper - Perf: Manually inline methods in BlockPos; protected ++ protected int y; // Paper - Perf: Manually inline methods in BlockPos; protected ++ protected int z; // Paper - Perf: Manually inline methods in BlockPos; protected - public static Codec offsetCodec(int maxOffset) { + public static Codec offsetCodec(final int maxOffsetPerAxis) { return CODEC.validate( -@@ -42,12 +_,12 @@ +@@ -41,12 +_,12 @@ } @Override -- public boolean equals(Object other) { -+ public final boolean equals(Object other) { // Paper - Perf: Final for inline - return this == other || other instanceof Vec3i vec3i && this.getX() == vec3i.getX() && this.getY() == vec3i.getY() && this.getZ() == vec3i.getZ(); +- public boolean equals(final Object o) { ++ public final boolean equals(final Object o) { // Paper - Perf: Final for inline + return this == o || o instanceof Vec3i vec3i && this.getX() == vec3i.getX() && this.getY() == vec3i.getY() && this.getZ() == vec3i.getZ(); } @Override @@ -28,7 +28,7 @@ return (this.getY() + this.getZ() * 31) * 31 + this.getX(); } -@@ -60,15 +_,15 @@ +@@ -59,15 +_,15 @@ } } @@ -47,7 +47,7 @@ return this.z; } -@@ -250,4 +_,11 @@ +@@ -247,4 +_,11 @@ public String toShortString() { return this.getX() + ", " + this.getY() + ", " + this.getZ(); } diff --git a/paper-server/patches/sources/net/minecraft/core/cauldron/CauldronInteraction.java.patch b/paper-server/patches/sources/net/minecraft/core/cauldron/CauldronInteraction.java.patch index a741db3c36ac..70d5f3e67fab 100644 --- a/paper-server/patches/sources/net/minecraft/core/cauldron/CauldronInteraction.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/cauldron/CauldronInteraction.java.patch @@ -1,302 +1,14 @@ --- a/net/minecraft/core/cauldron/CauldronInteraction.java +++ b/net/minecraft/core/cauldron/CauldronInteraction.java -@@ -42,26 +_,31 @@ +@@ -15,9 +_,9 @@ - static CauldronInteraction.InteractionMap newInteractionMap(String name) { - Object2ObjectOpenHashMap map = new Object2ObjectOpenHashMap<>(); -- map.defaultReturnValue((state, level, pos, player, hand, stack) -> InteractionResult.TRY_WITH_EMPTY_HAND); -+ map.defaultReturnValue((state, level, pos, player, hand, stack, hitDirection) -> InteractionResult.TRY_WITH_EMPTY_HAND); // Paper - add hitDirection - CauldronInteraction.InteractionMap interactionMap = new CauldronInteraction.InteractionMap(name, map); - INTERACTIONS.put(name, interactionMap); - return interactionMap; - } + @FunctionalInterface + public interface CauldronInteraction { +- CauldronInteraction DEFAULT = (var0, var1, var2, var3, var4, var5) -> InteractionResult.TRY_WITH_EMPTY_HAND; ++ CauldronInteraction DEFAULT = (var0, var1, var2, var3, var4, var5, var6) -> InteractionResult.TRY_WITH_EMPTY_HAND; // Paper - add hitDirection -- InteractionResult interact(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack stack); -+ InteractionResult interact(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection); // Paper - add hitDirection +- InteractionResult interact(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack itemInHand); ++ InteractionResult interact(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack itemInHand, net.minecraft.core.Direction hitDirection); // Paper - add hitDirection - static void bootStrap() { - Map map = EMPTY.map(); - addDefaultInteractions(map); -- map.put(Items.POTION, (state, level, pos, player, hand, stack) -> { -+ map.put(Items.POTION, (state, level, pos, player, hand, stack, hitDirection) -> { // Paper - add hitDirection - PotionContents potionContents = stack.get(DataComponents.POTION_CONTENTS); - if (potionContents != null && potionContents.is(Potions.WATER)) { - if (!level.isClientSide()) { -+ // CraftBukkit start -+ if (!LayeredCauldronBlock.changeLevel(level, pos, Blocks.WATER_CAULDRON.defaultBlockState(), player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent -+ return InteractionResult.SUCCESS; -+ } -+ // CraftBukkit end - Item item = stack.getItem(); - player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, new ItemStack(Items.GLASS_BOTTLE))); - player.awardStat(Stats.USE_CAULDRON); - player.awardStat(Stats.ITEM_USED.get(item)); -- level.setBlockAndUpdate(pos, Blocks.WATER_CAULDRON.defaultBlockState()); -+ // level.setBlockAndUpdate(pos, Blocks.WATER_CAULDRON.defaultBlockState()); // CraftBukkit - level.playSound(null, pos, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F); - level.gameEvent(null, GameEvent.FLUID_PLACE, pos); - } -@@ -75,7 +_,7 @@ - addDefaultInteractions(map1); - map1.put( - Items.BUCKET, -- (state, level, pos, player, hand, stack) -> fillBucket( -+ (state, level, pos, player, hand, stack, hitDirection) -> fillBucket( // Paper - add hitDirection - state, - level, - pos, -@@ -84,33 +_,43 @@ - stack, - new ItemStack(Items.WATER_BUCKET), - blockState -> blockState.getValue(LayeredCauldronBlock.LEVEL) == 3, -- SoundEvents.BUCKET_FILL -+ SoundEvents.BUCKET_FILL, hitDirection // Paper - add hitDirection - ) - ); -- map1.put(Items.GLASS_BOTTLE, (state, level, pos, player, hand, stack) -> { -+ map1.put(Items.GLASS_BOTTLE, (state, level, pos, player, hand, stack, hitDirection) -> { // Paper - add hitDirection - if (!level.isClientSide()) { -+ // CraftBukkit start -+ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BOTTLE_FILL)) { -+ return InteractionResult.SUCCESS; -+ } -+ // CraftBukkit end - Item item = stack.getItem(); - player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, PotionContents.createItemStack(Items.POTION, Potions.WATER))); - player.awardStat(Stats.USE_CAULDRON); - player.awardStat(Stats.ITEM_USED.get(item)); -- LayeredCauldronBlock.lowerFillLevel(state, level, pos); -+ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit - level.playSound(null, pos, SoundEvents.BOTTLE_FILL, SoundSource.BLOCKS, 1.0F, 1.0F); - level.gameEvent(null, GameEvent.FLUID_PICKUP, pos); - } - - return InteractionResult.SUCCESS; - }); -- map1.put(Items.POTION, (state, level, pos, player, hand, stack) -> { -+ map1.put(Items.POTION, (state, level, pos, player, hand, stack, hitDirection) -> { // Paper - add hitDirection - if (state.getValue(LayeredCauldronBlock.LEVEL) == 3) { - return InteractionResult.TRY_WITH_EMPTY_HAND; - } else { - PotionContents potionContents = stack.get(DataComponents.POTION_CONTENTS); - if (potionContents != null && potionContents.is(Potions.WATER)) { - if (!level.isClientSide()) { -+ // CraftBukkit start -+ if (!LayeredCauldronBlock.changeLevel(level, pos, state.cycle(LayeredCauldronBlock.LEVEL), player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent -+ return InteractionResult.SUCCESS; -+ } -+ // CraftBukkit end - player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, new ItemStack(Items.GLASS_BOTTLE))); - player.awardStat(Stats.USE_CAULDRON); - player.awardStat(Stats.ITEM_USED.get(stack.getItem())); -- level.setBlockAndUpdate(pos, state.cycle(LayeredCauldronBlock.LEVEL)); -+ // level.setBlockAndUpdate(pos, state.cycle(LayeredCauldronBlock.LEVEL)); // CraftBukkit - level.playSound(null, pos, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F); - level.gameEvent(null, GameEvent.FLUID_PLACE, pos); - } -@@ -162,15 +_,15 @@ - Map map2 = LAVA.map(); - map2.put( - Items.BUCKET, -- (state, level, pos, player, hand, stack) -> fillBucket( -- state, level, pos, player, hand, stack, new ItemStack(Items.LAVA_BUCKET), blockState -> true, SoundEvents.BUCKET_FILL_LAVA -+ (state, level, pos, player, hand, stack, hitDirection) -> fillBucket( // Paper - add hitDirection -+ state, level, pos, player, hand, stack, new ItemStack(Items.LAVA_BUCKET), blockState -> true, SoundEvents.BUCKET_FILL_LAVA, hitDirection // Paper - add hitDirection - ) - ); - addDefaultInteractions(map2); - Map map3 = POWDER_SNOW.map(); - map3.put( - Items.BUCKET, -- (state, level, pos, player, hand, stack) -> fillBucket( -+ (state, level, pos, player, hand, stack, hitDirection) -> fillBucket( // Paper - add hitDirection - state, - level, - pos, -@@ -179,7 +_,7 @@ - stack, - new ItemStack(Items.POWDER_SNOW_BUCKET), - blockState -> blockState.getValue(LayeredCauldronBlock.LEVEL) == 3, -- SoundEvents.BUCKET_FILL_POWDER_SNOW -+ SoundEvents.BUCKET_FILL_POWDER_SNOW, hitDirection // Paper - add hitDirection - ) - ); - addDefaultInteractions(map3); -@@ -202,15 +_,35 @@ - Predicate statePredicate, - SoundEvent fillSound - ) { -+ // Paper start - add hitDirection -+ return fillBucket(state, level, pos, player, hand, emptyStack, filledStack, statePredicate, fillSound, null); // Paper - add hitDirection -+ } -+ -+ static InteractionResult fillBucket(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack emptyStack, ItemStack filledStack, Predicate statePredicate, SoundEvent fillSound, @javax.annotation.Nullable net.minecraft.core.Direction hitDirection) { -+ // Paper end - add hitDirection - if (!statePredicate.test(state)) { - return InteractionResult.TRY_WITH_EMPTY_HAND; - } else { - if (!level.isClientSide()) { -+ // Paper start - fire PlayerBucketFillEvent -+ if (hitDirection != null) { -+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(level, player, pos, pos, hitDirection, emptyStack, filledStack.getItem(), hand); -+ if (event.isCancelled()) { -+ return InteractionResult.PASS; -+ } -+ filledStack = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY; -+ } -+ // Paper end - fire PlayerBucketFillEvent -+ // CraftBukkit start -+ if (!LayeredCauldronBlock.changeLevel(level, pos, Blocks.CAULDRON.defaultBlockState(), player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL, false)) { // Paper - Call CauldronLevelChangeEvent -+ return InteractionResult.SUCCESS; -+ } -+ // CraftBukkit end - Item item = emptyStack.getItem(); - player.setItemInHand(hand, ItemUtils.createFilledResult(emptyStack, player, filledStack)); - player.awardStat(Stats.USE_CAULDRON); - player.awardStat(Stats.ITEM_USED.get(item)); -- level.setBlockAndUpdate(pos, Blocks.CAULDRON.defaultBlockState()); -+ // level.setBlockAndUpdate(pos, Blocks.CAULDRON.defaultBlockState()); // CraftBukkit - level.playSound(null, pos, fillSound, SoundSource.BLOCKS, 1.0F, 1.0F); - level.gameEvent(null, GameEvent.FLUID_PICKUP, pos); - } -@@ -222,12 +_,33 @@ - static InteractionResult emptyBucket( - Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack filledStack, BlockState state, SoundEvent emptySound - ) { -+ // Paper start - add hitDirection -+ return emptyBucket(level, pos, player, hand, filledStack, state, emptySound, null); -+ } -+ -+ static InteractionResult emptyBucket(Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack filledStack, BlockState state, SoundEvent emptySound, @javax.annotation.Nullable net.minecraft.core.Direction hitDirection) { -+ // Paper end - add hitDirection - if (!level.isClientSide()) { -+ // Paper start - fire PlayerBucketEmptyEvent -+ ItemStack output = new ItemStack(Items.BUCKET); -+ if (hitDirection != null) { -+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketEmptyEvent(level, player, pos, pos, hitDirection, filledStack, hand); -+ if (event.isCancelled()) { -+ return InteractionResult.PASS; -+ } -+ output = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY; -+ } -+ // Paper end - fire PlayerBucketEmptyEvent -+ // CraftBukkit start -+ if (!LayeredCauldronBlock.changeLevel(level, pos, state, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BUCKET_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent -+ return InteractionResult.SUCCESS; -+ } -+ // CraftBukkit end - Item item = filledStack.getItem(); -- player.setItemInHand(hand, ItemUtils.createFilledResult(filledStack, player, new ItemStack(Items.BUCKET))); -+ player.setItemInHand(hand, ItemUtils.createFilledResult(filledStack, player, output)); // Paper - player.awardStat(Stats.FILL_CAULDRON); - player.awardStat(Stats.ITEM_USED.get(item)); -- level.setBlockAndUpdate(pos, state); -+ // level.setBlockAndUpdate(pos, state); // CraftBukkit - level.playSound(null, pos, emptySound, SoundSource.BLOCKS, 1.0F, 1.0F); - level.gameEvent(null, GameEvent.FLUID_PLACE, pos); - } -@@ -236,23 +_,23 @@ - } - - private static InteractionResult fillWaterInteraction( -- BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack filledStack -+ BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack filledStack, final net.minecraft.core.Direction hitDirection // Paper - add hitDirection - ) { - return emptyBucket( -- level, pos, player, hand, filledStack, Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY -+ level, pos, player, hand, filledStack, Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY, hitDirection // Paper - add hitDirection - ); - } - - private static InteractionResult fillLavaInteraction( -- BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack filledStack -+ BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack filledStack, final net.minecraft.core.Direction hitDirection // Paper - add hitDirection - ) { - return (InteractionResult)(isUnderWater(level, pos) - ? InteractionResult.CONSUME -- : emptyBucket(level, pos, player, hand, filledStack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA)); -+ : emptyBucket(level, pos, player, hand, filledStack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA, hitDirection)); // Paper - add hitDirection - } - - private static InteractionResult fillPowderSnowInteraction( -- BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack filledStack -+ BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack filledStack, final net.minecraft.core.Direction hitDirection // Paper - add hitDirection - ) { - return (InteractionResult)(isUnderWater(level, pos) - ? InteractionResult.CONSUME -@@ -263,53 +_,68 @@ - hand, - filledStack, - Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), -- SoundEvents.BUCKET_EMPTY_POWDER_SNOW -+ SoundEvents.BUCKET_EMPTY_POWDER_SNOW, hitDirection // Paper - add hitDirection - )); - } - -- private static InteractionResult shulkerBoxInteraction(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) { -+ private static InteractionResult shulkerBoxInteraction(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection - Block block = Block.byItem(stack.getItem()); - if (!(block instanceof ShulkerBoxBlock)) { - return InteractionResult.TRY_WITH_EMPTY_HAND; - } else { - if (!level.isClientSide()) { -+ // CraftBukkit start -+ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.SHULKER_WASH)) { -+ return InteractionResult.SUCCESS; -+ } -+ // CraftBukkit end - ItemStack itemStack = stack.transmuteCopy(Blocks.SHULKER_BOX, 1); - player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, itemStack, false)); - player.awardStat(Stats.CLEAN_SHULKER_BOX); -- LayeredCauldronBlock.lowerFillLevel(state, level, pos); -+ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit - } - - return InteractionResult.SUCCESS; - } - } - -- private static InteractionResult bannerInteraction(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) { -+ private static InteractionResult bannerInteraction(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection - BannerPatternLayers bannerPatternLayers = stack.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY); - if (bannerPatternLayers.layers().isEmpty()) { - return InteractionResult.TRY_WITH_EMPTY_HAND; - } else { - if (!level.isClientSide()) { -+ // CraftBukkit start -+ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BANNER_WASH)) { -+ return InteractionResult.SUCCESS; -+ } -+ // CraftBukkit end - ItemStack itemStack = stack.copyWithCount(1); - itemStack.set(DataComponents.BANNER_PATTERNS, bannerPatternLayers.removeLast()); - player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, itemStack, false)); - player.awardStat(Stats.CLEAN_BANNER); -- LayeredCauldronBlock.lowerFillLevel(state, level, pos); -+ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit - } - - return InteractionResult.SUCCESS; - } - } - -- private static InteractionResult dyedItemIteration(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) { -+ private static InteractionResult dyedItemIteration(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection - if (!stack.is(ItemTags.DYEABLE)) { - return InteractionResult.TRY_WITH_EMPTY_HAND; - } else if (!stack.has(DataComponents.DYED_COLOR)) { - return InteractionResult.TRY_WITH_EMPTY_HAND; - } else { - if (!level.isClientSide()) { -+ // CraftBukkit start -+ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.ARMOR_WASH)) { -+ return InteractionResult.SUCCESS; -+ } -+ // CraftBukkit end - stack.remove(DataComponents.DYED_COLOR); - player.awardStat(Stats.CLEAN_ARMOR); -- LayeredCauldronBlock.lowerFillLevel(state, level, pos); -+ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit - } - - return InteractionResult.SUCCESS; + public static class Dispatcher { + private final Map, CauldronInteraction> tags = new HashMap<>(); diff --git a/paper-server/patches/sources/net/minecraft/core/cauldron/CauldronInteractions.java.patch b/paper-server/patches/sources/net/minecraft/core/cauldron/CauldronInteractions.java.patch new file mode 100644 index 000000000000..831d64efa7bb --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/cauldron/CauldronInteractions.java.patch @@ -0,0 +1,312 @@ +--- a/net/minecraft/core/cauldron/CauldronInteractions.java ++++ b/net/minecraft/core/cauldron/CauldronInteractions.java +@@ -46,15 +_,20 @@ + + public static void bootStrap() { + addDefaultInteractions(EMPTY); +- EMPTY.put(Items.POTION, (var0, level, pos, player, hand, itemInHand) -> { ++ EMPTY.put(Items.POTION, (var0, level, pos, player, hand, itemInHand, hitDirection) -> { // Paper - add hitDirection + PotionContents potion = itemInHand.get(DataComponents.POTION_CONTENTS); + if (potion != null && potion.is(Potions.WATER)) { + if (!level.isClientSide()) { ++ // CraftBukkit start ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, Blocks.WATER_CAULDRON.defaultBlockState(), player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY)) { // Paper - Call CauldronLevelChangeEvent ++ return InteractionResult.SUCCESS; ++ } ++ // CraftBukkit end + Item usedItem = itemInHand.getItem(); + player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, new ItemStack(Items.GLASS_BOTTLE))); + player.awardStat(Stats.USE_CAULDRON); + player.awardStat(Stats.ITEM_USED.get(usedItem)); +- level.setBlockAndUpdate(pos, Blocks.WATER_CAULDRON.defaultBlockState()); ++ // level.setBlockAndUpdate(pos, Blocks.WATER_CAULDRON.defaultBlockState()); // CraftBukkit + level.playSound(null, pos, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F); + level.gameEvent(null, GameEvent.FLUID_PLACE, pos); + } +@@ -67,7 +_,7 @@ + addDefaultInteractions(WATER); + WATER.put( + Items.BUCKET, +- (state, level, pos, player, hand, itemInHand) -> fillBucket( ++ (state, level, pos, player, hand, itemInHand, hitDirection) -> fillBucket( // Paper - add hitDirection + state, + level, + pos, +@@ -76,33 +_,43 @@ + itemInHand, + new ItemStack(Items.WATER_BUCKET), + s -> s.getValue(LayeredCauldronBlock.LEVEL) == 3, +- SoundEvents.BUCKET_FILL ++ SoundEvents.BUCKET_FILL, hitDirection // Paper - add hitDirection + ) + ); +- WATER.put(Items.GLASS_BOTTLE, (state, level, pos, player, hand, itemInHand) -> { ++ WATER.put(Items.GLASS_BOTTLE, (state, level, pos, player, hand, itemInHand, hitDirection) -> { // Paper - add hitDirection + if (!level.isClientSide()) { ++ // CraftBukkit start ++ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BOTTLE_FILL)) { ++ return InteractionResult.SUCCESS; ++ } ++ // CraftBukkit end + Item usedItem = itemInHand.getItem(); + player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, PotionContents.createItemStack(Items.POTION, Potions.WATER))); + player.awardStat(Stats.USE_CAULDRON); + player.awardStat(Stats.ITEM_USED.get(usedItem)); +- LayeredCauldronBlock.lowerFillLevel(state, level, pos); ++ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit + level.playSound(null, pos, SoundEvents.BOTTLE_FILL, SoundSource.BLOCKS, 1.0F, 1.0F); + level.gameEvent(null, GameEvent.FLUID_PICKUP, pos); + } + + return InteractionResult.SUCCESS; + }); +- WATER.put(Items.POTION, (state, level, pos, player, hand, itemInHand) -> { ++ WATER.put(Items.POTION, (state, level, pos, player, hand, itemInHand, hitDirection) -> { // Paper - add hitDirection + if (state.getValue(LayeredCauldronBlock.LEVEL) == 3) { + return InteractionResult.TRY_WITH_EMPTY_HAND; + } else { + PotionContents potion = itemInHand.get(DataComponents.POTION_CONTENTS); + if (potion != null && potion.is(Potions.WATER)) { + if (!level.isClientSide()) { ++ // CraftBukkit start ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, state.cycle(LayeredCauldronBlock.LEVEL), player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY)) { // Paper - Call CauldronLevelChangeEvent ++ return InteractionResult.SUCCESS; ++ } ++ // CraftBukkit end + player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, new ItemStack(Items.GLASS_BOTTLE))); + player.awardStat(Stats.USE_CAULDRON); + player.awardStat(Stats.ITEM_USED.get(itemInHand.getItem())); +- level.setBlockAndUpdate(pos, state.cycle(LayeredCauldronBlock.LEVEL)); ++ // level.setBlockAndUpdate(pos, state.cycle(LayeredCauldronBlock.LEVEL)); // CraftBukkit + level.playSound(null, pos, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F); + level.gameEvent(null, GameEvent.FLUID_PLACE, pos); + } +@@ -148,14 +_,14 @@ + WATER.put(Items.YELLOW_SHULKER_BOX, CauldronInteractions::shulkerBoxInteraction); + LAVA.put( + Items.BUCKET, +- (state, level, pos, player, hand, itemInHand) -> fillBucket( +- state, level, pos, player, hand, itemInHand, new ItemStack(Items.LAVA_BUCKET), var0x -> true, SoundEvents.BUCKET_FILL_LAVA ++ (state, level, pos, player, hand, itemInHand, hitDirection) -> fillBucket( // Paper - add hitDirection ++ state, level, pos, player, hand, itemInHand, new ItemStack(Items.LAVA_BUCKET), var0x -> true, SoundEvents.BUCKET_FILL_LAVA, hitDirection // Paper - add hitDirection + ) + ); + addDefaultInteractions(LAVA); + POWDER_SNOW.put( + Items.BUCKET, +- (state, level, pos, player, hand, itemInHand) -> fillBucket( ++ (state, level, pos, player, hand, itemInHand, hitDirection) -> fillBucket( // Paper - add hitDirection + state, + level, + pos, +@@ -164,7 +_,7 @@ + itemInHand, + new ItemStack(Items.POWDER_SNOW_BUCKET), + s -> s.getValue(LayeredCauldronBlock.LEVEL) == 3, +- SoundEvents.BUCKET_FILL_POWDER_SNOW ++ SoundEvents.BUCKET_FILL_POWDER_SNOW, hitDirection // Paper - add hitDirection + ) + ); + addDefaultInteractions(POWDER_SNOW); +@@ -187,15 +_,46 @@ + final Predicate canFill, + final SoundEvent soundEvent + ) { ++ // Paper start - add hitDirection ++ return fillBucket(state, level, pos, player, hand, itemInHand, newItem, canFill, soundEvent, null); ++ } ++ ++ static InteractionResult fillBucket( ++ final BlockState state, ++ final Level level, ++ final BlockPos pos, ++ final Player player, ++ final InteractionHand hand, ++ final ItemStack itemInHand, ++ ItemStack newItem, ++ final Predicate canFill, ++ final SoundEvent soundEvent, ++ final net.minecraft.core.@org.jspecify.annotations.Nullable Direction hitDirection ++ ) { ++ // Paper end - add hitDirection + if (!canFill.test(state)) { + return InteractionResult.TRY_WITH_EMPTY_HAND; + } else { + if (!level.isClientSide()) { ++ // Paper start - fire PlayerBucketFillEvent ++ if (hitDirection != null) { ++ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(level, player, pos, pos, hitDirection, itemInHand, newItem.getItem(), hand); ++ if (event.isCancelled()) { ++ return InteractionResult.PASS; ++ } ++ newItem = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY; ++ } ++ // Paper end - fire PlayerBucketFillEvent ++ // CraftBukkit start ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, Blocks.CAULDRON.defaultBlockState(), player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL)) { // Paper - Call CauldronLevelChangeEvent ++ return InteractionResult.SUCCESS; ++ } ++ // CraftBukkit end + Item itemUsed = itemInHand.getItem(); + player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, newItem)); + player.awardStat(Stats.USE_CAULDRON); + player.awardStat(Stats.ITEM_USED.get(itemUsed)); +- level.setBlockAndUpdate(pos, Blocks.CAULDRON.defaultBlockState()); ++ // level.setBlockAndUpdate(pos, Blocks.CAULDRON.defaultBlockState()); // CraftBukkit + level.playSound(null, pos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F); + level.gameEvent(null, GameEvent.FLUID_PICKUP, pos); + } +@@ -213,12 +_,42 @@ + final BlockState newState, + final SoundEvent soundEvent + ) { ++ // Paper start - add hitDirection ++ return emptyBucket(level, pos, player, hand, itemInHand, newState, soundEvent, null); ++ } ++ ++ static InteractionResult emptyBucket( ++ final Level level, ++ final BlockPos pos, ++ final Player player, ++ final InteractionHand hand, ++ final ItemStack itemInHand, ++ final BlockState newState, ++ final SoundEvent soundEvent, ++ final net.minecraft.core.@org.jspecify.annotations.Nullable Direction hitDirection ++ ) { ++ // Paper end - add hitDirection + if (!level.isClientSide()) { ++ // Paper start - fire PlayerBucketEmptyEvent ++ ItemStack output = new ItemStack(Items.BUCKET); ++ if (hitDirection != null) { ++ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketEmptyEvent(level, player, pos, pos, hitDirection, itemInHand, hand); ++ if (event.isCancelled()) { ++ return InteractionResult.PASS; ++ } ++ output = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY; ++ } ++ // Paper end - fire PlayerBucketEmptyEvent ++ // CraftBukkit start ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, newState, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BUCKET_EMPTY)) { // Paper - Call CauldronLevelChangeEvent ++ return InteractionResult.SUCCESS; ++ } ++ // CraftBukkit end + Item itemUsed = itemInHand.getItem(); +- player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, new ItemStack(Items.BUCKET))); ++ player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, output)); // Paper + player.awardStat(Stats.FILL_CAULDRON); + player.awardStat(Stats.ITEM_USED.get(itemUsed)); +- level.setBlockAndUpdate(pos, newState); ++ // level.setBlockAndUpdate(pos, newState); // CraftBukkit + level.playSound(null, pos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F); + level.gameEvent(null, GameEvent.FLUID_PLACE, pos); + } +@@ -227,23 +_,23 @@ + } + + private static InteractionResult fillWaterInteraction( +- final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand ++ final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand, final net.minecraft.core.Direction hitDirection // Paper - add hitDirection + ) { + return emptyBucket( +- level, pos, player, hand, itemInHand, Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY ++ level, pos, player, hand, itemInHand, Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY, hitDirection // Paper - add hitDirection + ); + } + + private static InteractionResult fillLavaInteraction( +- final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand ++ final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand, final net.minecraft.core.Direction hitDirection // Paper - add hitDirection + ) { + return (InteractionResult)(isUnderWater(level, pos) + ? InteractionResult.CONSUME +- : emptyBucket(level, pos, player, hand, itemInHand, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA)); ++ : emptyBucket(level, pos, player, hand, itemInHand, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA, hitDirection)); // Paper - add hitDirection + } + + private static InteractionResult fillPowderSnowInteraction( +- final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand ++ final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand, final net.minecraft.core.Direction hitDirection // Paper - add hitDirection + ) { + return (InteractionResult)(isUnderWater(level, pos) + ? InteractionResult.CONSUME +@@ -254,22 +_,27 @@ + 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 + )); + } + + private static InteractionResult shulkerBoxInteraction( +- final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand ++ final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand, final net.minecraft.core.Direction hitDirection // Paper - add hitDirection + ) { + Block block = Block.byItem(itemInHand.getItem()); + if (!(block instanceof ShulkerBoxBlock)) { + return InteractionResult.TRY_WITH_EMPTY_HAND; + } else { + if (!level.isClientSide()) { ++ // CraftBukkit start ++ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.SHULKER_WASH)) { ++ return InteractionResult.SUCCESS; ++ } ++ // CraftBukkit end + ItemStack cleanedShulkerBox = itemInHand.transmuteCopy(Blocks.SHULKER_BOX, 1); + player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, cleanedShulkerBox, false)); + player.awardStat(Stats.CLEAN_SHULKER_BOX); +- LayeredCauldronBlock.lowerFillLevel(state, level, pos); ++ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit + } + + return InteractionResult.SUCCESS; +@@ -277,18 +_,23 @@ + } + + private static InteractionResult bannerInteraction( +- final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand ++ final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand, final net.minecraft.core.Direction hitDirection // Paper - add hitDirection + ) { + BannerPatternLayers patterns = itemInHand.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY); + if (patterns.layers().isEmpty()) { + return InteractionResult.TRY_WITH_EMPTY_HAND; + } else { + if (!level.isClientSide()) { ++ // CraftBukkit start ++ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.BANNER_WASH)) { ++ return InteractionResult.SUCCESS; ++ } ++ // CraftBukkit end + ItemStack cleanedBanner = itemInHand.copyWithCount(1); + cleanedBanner.set(DataComponents.BANNER_PATTERNS, patterns.removeLast()); + player.setItemInHand(hand, ItemUtils.createFilledResult(itemInHand, player, cleanedBanner, false)); + player.awardStat(Stats.CLEAN_BANNER); +- LayeredCauldronBlock.lowerFillLevel(state, level, pos); ++ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit + } + + return InteractionResult.SUCCESS; +@@ -296,15 +_,20 @@ + } + + private static InteractionResult dyedItemIteration( +- final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand ++ final BlockState state, final Level level, final BlockPos pos, final Player player, final InteractionHand hand, final ItemStack itemInHand, final net.minecraft.core.Direction hitDirection // Paper - add hitDirection + ) { + if (!itemInHand.has(DataComponents.DYED_COLOR)) { + return InteractionResult.TRY_WITH_EMPTY_HAND; + } else { + if (!level.isClientSide()) { ++ // CraftBukkit start ++ if (!LayeredCauldronBlock.lowerFillLevel(state, level, pos, player, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.ARMOR_WASH)) { ++ return InteractionResult.SUCCESS; ++ } ++ // CraftBukkit end + itemInHand.remove(DataComponents.DYED_COLOR); + player.awardStat(Stats.CLEAN_ARMOR); +- LayeredCauldronBlock.lowerFillLevel(state, level, pos); ++ // LayeredCauldronBlock.lowerFillLevel(state, level, pos); // CraftBukkit + } + + return InteractionResult.SUCCESS; diff --git a/paper-server/patches/sources/net/minecraft/core/component/DataComponentPatch.java.patch b/paper-server/patches/sources/net/minecraft/core/component/DataComponentPatch.java.patch index 52040ac041fe..1de1c463cb95 100644 --- a/paper-server/patches/sources/net/minecraft/core/component/DataComponentPatch.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/component/DataComponentPatch.java.patch @@ -1,52 +1,52 @@ --- a/net/minecraft/core/component/DataComponentPatch.java +++ b/net/minecraft/core/component/DataComponentPatch.java @@ -106,6 +_,11 @@ - buffer.writeVarInt(0); - buffer.writeVarInt(0); + output.writeVarInt(0); + output.writeVarInt(0); } else { + // Paper start - data sanitization for items -+ final io.papermc.paper.util.sanitizer.ItemObfuscationSession itemObfuscationSession = value.map.isEmpty() ++ final io.papermc.paper.util.sanitizer.ItemObfuscationSession itemObfuscationSession = patch.map.isEmpty() + ? null // Avoid thread local lookup of current session if it won't be needed anyway. + : io.papermc.paper.util.sanitizer.ItemObfuscationSession.currentSession(); + // Paper end - data sanitization for items - int i = 0; - int i1 = 0; + int positiveCount = 0; + int negativeCount = 0; @@ -113,7 +_,7 @@ - value.map + patch.map )) { if (entry.getValue().isPresent()) { -- i++; -+ if (!io.papermc.paper.util.sanitizer.ItemComponentSanitizer.shouldDrop(itemObfuscationSession, entry.getKey())) i++; // Paper - data sanitization for items +- positiveCount++; ++ if (!io.papermc.paper.util.sanitizer.ItemComponentSanitizer.shouldDrop(itemObfuscationSession, entry.getKey())) positiveCount++; // Paper - data sanitization for items } else { - i1++; + negativeCount++; } @@ -126,6 +_,7 @@ - value.map + patch.map )) { - Optional optional = entryx.getValue(); -+ optional = io.papermc.paper.util.sanitizer.ItemComponentSanitizer.override(itemObfuscationSession, entryx.getKey(), entryx.getValue()); // Paper - data sanitization for items - if (optional.isPresent()) { - DataComponentType dataComponentType = entryx.getKey(); - DataComponentType.STREAM_CODEC.encode(buffer, dataComponentType); + Optional value = entryx.getValue(); ++ value = io.papermc.paper.util.sanitizer.ItemComponentSanitizer.override(itemObfuscationSession, entryx.getKey(), entryx.getValue()); // Paper - data sanitization for items + if (value.isPresent()) { + DataComponentType type = entryx.getKey(); + DataComponentType.STREAM_CODEC.encode(output, type); @@ -145,7 +_,13 @@ } - private void encodeComponent(RegistryFriendlyByteBuf buffer, DataComponentType component, Object value) { -- codecGetter.apply(component).encode(buffer, (T)value); + private void encodeComponent(final RegistryFriendlyByteBuf output, final DataComponentType type, final Object value) { +- codecGetter.apply(type).encode(output, (T)value); + // Paper start - codec errors of random anonymous classes are useless + try { -+ codecGetter.apply(component).encode(buffer, (T)value); ++ codecGetter.apply(type).encode(output, (T)value); + } catch (final Exception e) { -+ throw new RuntimeException("Error encoding component " + component, e); ++ throw new RuntimeException("Error encoding component " + type, e); + } + // Paper end - codec errors of random anonymous classes are useless } }; } -@@ -248,6 +_,42 @@ +@@ -255,6 +_,42 @@ - Builder() { + private Builder() { } + + // CraftBukkit start @@ -85,5 +85,5 @@ + } + // CraftBukkit end - public DataComponentPatch.Builder set(DataComponentType component, T value) { - this.map.put(component, Optional.of(value)); + public DataComponentPatch.Builder set(final DataComponentType type, final T value) { + this.map.put(type, Optional.of(value)); diff --git a/paper-server/patches/sources/net/minecraft/core/component/DataComponents.java.patch b/paper-server/patches/sources/net/minecraft/core/component/DataComponents.java.patch index 738c56efba60..35439836aeff 100644 --- a/paper-server/patches/sources/net/minecraft/core/component/DataComponents.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/component/DataComponents.java.patch @@ -1,24 +1,24 @@ --- a/net/minecraft/core/component/DataComponents.java +++ b/net/minecraft/core/component/DataComponents.java -@@ -236,10 +_,10 @@ - "map_post_processing", builder -> builder.networkSynchronized(MapPostProcessing.STREAM_CODEC) +@@ -231,10 +_,10 @@ + "map_post_processing", b -> b.networkSynchronized(MapPostProcessing.STREAM_CODEC) ); public static final DataComponentType CHARGED_PROJECTILES = register( -- "charged_projectiles", builder -> builder.persistent(ChargedProjectiles.CODEC).networkSynchronized(ChargedProjectiles.STREAM_CODEC).cacheEncoding() -+ "charged_projectiles", builder -> builder.persistent(ChargedProjectiles.CODEC).networkSynchronized(io.papermc.paper.util.sanitizer.OversizedItemComponentSanitizer.CHARGED_PROJECTILES).cacheEncoding() // Paper - sanitize charged projectiles +- "charged_projectiles", b -> b.persistent(ChargedProjectiles.CODEC).networkSynchronized(ChargedProjectiles.STREAM_CODEC).cacheEncoding() ++ "charged_projectiles", b -> b.persistent(ChargedProjectiles.CODEC).networkSynchronized(io.papermc.paper.util.sanitizer.OversizedItemComponentSanitizer.CHARGED_PROJECTILES).cacheEncoding() // Paper - sanitize charged projectiles ); public static final DataComponentType BUNDLE_CONTENTS = register( -- "bundle_contents", builder -> builder.persistent(BundleContents.CODEC).networkSynchronized(BundleContents.STREAM_CODEC).cacheEncoding() -+ "bundle_contents", builder -> builder.persistent(BundleContents.CODEC).networkSynchronized(io.papermc.paper.util.sanitizer.OversizedItemComponentSanitizer.BUNDLE_CONTENTS).cacheEncoding() // Paper - sanitize bundle contents +- "bundle_contents", b -> b.persistent(BundleContents.CODEC).networkSynchronized(BundleContents.STREAM_CODEC).cacheEncoding() ++ "bundle_contents", b -> b.persistent(BundleContents.CODEC).networkSynchronized(io.papermc.paper.util.sanitizer.OversizedItemComponentSanitizer.BUNDLE_CONTENTS).cacheEncoding() // Paper - sanitize bundle contents ); public static final DataComponentType POTION_CONTENTS = register( - "potion_contents", builder -> builder.persistent(PotionContents.CODEC).networkSynchronized(PotionContents.STREAM_CODEC).cacheEncoding() -@@ -322,7 +_,7 @@ - "pot_decorations", builder -> builder.persistent(PotDecorations.CODEC).networkSynchronized(PotDecorations.STREAM_CODEC).cacheEncoding() + "potion_contents", b -> b.persistent(PotionContents.CODEC).networkSynchronized(PotionContents.STREAM_CODEC).cacheEncoding() +@@ -314,7 +_,7 @@ + "pot_decorations", b -> b.persistent(PotDecorations.CODEC).networkSynchronized(PotDecorations.STREAM_CODEC).cacheEncoding() ); public static final DataComponentType CONTAINER = register( -- "container", builder -> builder.persistent(ItemContainerContents.CODEC).networkSynchronized(ItemContainerContents.STREAM_CODEC).cacheEncoding() -+ "container", builder -> builder.persistent(ItemContainerContents.CODEC).networkSynchronized(io.papermc.paper.util.sanitizer.OversizedItemComponentSanitizer.CONTAINER).cacheEncoding() // Paper - sanitize container contents +- "container", b -> b.persistent(ItemContainerContents.CODEC).networkSynchronized(ItemContainerContents.STREAM_CODEC).cacheEncoding() ++ "container", b -> b.persistent(ItemContainerContents.CODEC).networkSynchronized(io.papermc.paper.util.sanitizer.OversizedItemComponentSanitizer.CONTAINER).cacheEncoding() // Paper - sanitize container contents ); public static final DataComponentType BLOCK_STATE = register( - "block_state", builder -> builder.persistent(BlockItemStateProperties.CODEC).networkSynchronized(BlockItemStateProperties.STREAM_CODEC).cacheEncoding() + "block_state", b -> b.persistent(BlockItemStateProperties.CODEC).networkSynchronized(BlockItemStateProperties.STREAM_CODEC).cacheEncoding() diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch index cdf7f08ddbe2..5fe9b0cd7828 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java +++ b/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java @@ -41,13 +_,36 @@ - d4 = 0.0; + yOffset = 0.0; } + // CraftBukkit start -+ ItemStack singleItemStack = item.copyWithCount(1); -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos()); ++ ItemStack singleItemStack = dispensed.copyWithCount(1); ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, source.pos()); + org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack); + -+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d1, d2 + d4, d3)); -+ serverLevel.getCraftServer().getPluginManager().callEvent(event); ++ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(spawnX, spawnY + yOffset, spawnZ)); ++ level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { -+ return item; ++ return dispensed; + } + + boolean shrink = true; @@ -21,22 +21,22 @@ + shrink = false; + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); + if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { -+ dispenseBehavior.dispense(blockSource, eventStack); -+ return item; ++ dispenseBehavior.dispense(source, eventStack); ++ return dispensed; + } + } + // CraftBukkit end - AbstractBoat abstractBoat = this.type.create(serverLevel, EntitySpawnReason.DISPENSER); - if (abstractBoat != null) { -- abstractBoat.setInitialPos(d1, d2 + d4, d3); -+ abstractBoat.setInitialPos(event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); // CraftBukkit - EntityType.createDefaultStackConfig(serverLevel, item, null).accept(abstractBoat); - abstractBoat.setYRot(direction.toYRot()); -- serverLevel.addFreshEntity(abstractBoat); -- item.shrink(1); -+ if (serverLevel.addFreshEntity(abstractBoat) && shrink) item.shrink(1); // Paper - if entity add was successful and supposed to shrink + AbstractBoat boat = this.type.create(level, EntitySpawnReason.DISPENSER); + if (boat != null) { +- boat.setInitialPos(spawnX, spawnY + yOffset, spawnZ); ++ boat.setInitialPos(event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); // CraftBukkit + EntityType.createDefaultStackConfig(level, dispensed, null).accept(boat); + boat.setYRot(direction.toYRot()); +- level.addFreshEntity(boat); +- dispensed.shrink(1); ++ if (level.addFreshEntity(boat) && shrink) dispensed.shrink(1); // Paper - if entity add was successful and supposed to shrink } - return item; + return dispensed; diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java.patch index 9007c067cefe..70a6553a3a17 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java.patch @@ -1,60 +1,60 @@ --- a/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java +++ b/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java -@@ -10,24 +_,37 @@ +@@ -11,24 +_,37 @@ public class DefaultDispenseItemBehavior implements DispenseItemBehavior { private static final int DEFAULT_ACCURACY = 6; + private Direction direction; // Paper - cache facing direction @Override - public final ItemStack dispense(BlockSource blockSource, ItemStack item) { -+ this.direction = blockSource.state().getValue(DispenserBlock.FACING); // Paper - cache facing direction - ItemStack itemStack = this.execute(blockSource, item); - this.playSound(blockSource); -- this.playAnimation(blockSource, blockSource.state().getValue(DispenserBlock.FACING)); -+ this.playAnimation(blockSource, this.direction); // Paper - cache facing direction - return itemStack; + public final ItemStack dispense(final BlockSource source, final ItemStack dispensed) { ++ this.direction = source.state().getValue(DispenserBlock.FACING); // Paper - cache facing direction + ItemStack result = this.execute(source, dispensed); + this.playSound(source); +- this.playAnimation(source, source.state().getValue(DispenserBlock.FACING)); ++ this.playAnimation(source, this.direction); // Paper - cache facing direction + return result; } - protected ItemStack execute(BlockSource blockSource, ItemStack item) { -- Direction direction = blockSource.state().getValue(DispenserBlock.FACING); + protected ItemStack execute(final BlockSource source, final ItemStack dispensed) { +- Direction direction = source.state().getValue(DispenserBlock.FACING); + // Paper - cache facing direction - Position dispensePosition = DispenserBlock.getDispensePosition(blockSource); - ItemStack itemStack = item.split(1); -- spawnItem(blockSource.level(), itemStack, 6, direction, dispensePosition); + Position position = DispenserBlock.getDispensePosition(source); + ItemStack itemStack = dispensed.split(1); +- spawnItem(source.level(), itemStack, 6, direction, position); + // CraftBukkit start -+ if (!DefaultDispenseItemBehavior.spawnItem(blockSource.level(), itemStack, 6, this.direction, dispensePosition, blockSource)) { -+ item.grow(1); ++ if (!DefaultDispenseItemBehavior.spawnItem(source.level(), itemStack, 6, this.direction, position, source)) { ++ dispensed.grow(1); + } + // CraftBukkit end - return item; + return dispensed; } - public static void spawnItem(Level level, ItemStack stack, int speed, Direction facing, Position position) { + public static void spawnItem(final Level level, final ItemStack itemStack, final int accuracy, final Direction direction, final Position position) { + // CraftBukkit start -+ ItemEntity itemEntity = prepareItem(level, stack, speed, facing, position); ++ ItemEntity itemEntity = prepareItem(level, itemStack, accuracy, direction, position); + level.addFreshEntity(itemEntity); + } + -+ private static ItemEntity prepareItem(Level level, ItemStack stack, int speed, Direction facing, Position position) { ++ private static ItemEntity prepareItem(final Level level, final ItemStack itemStack, final int accuracy, final Direction direction, final Position position) { + // CraftBukkit end - double d = position.x(); - double d1 = position.y(); - double d2 = position.z(); -@@ -44,7 +_,43 @@ - level.random.triangle(0.2, 0.0172275 * speed), - level.random.triangle(facing.getStepZ() * d3, 0.0172275 * speed) + double spawnX = position.x(); + double spawnY = position.y(); + double spawnZ = position.z(); +@@ -46,7 +_,43 @@ + random.triangle(0.2, 0.0172275 * accuracy), + random.triangle(direction.getStepZ() * pow, 0.0172275 * accuracy) ); + return itemEntity; // CraftBukkit + } + + // CraftBukkit start - void -> boolean return -+ public static boolean spawnItem(Level level, ItemStack stack, int speed, Direction facing, Position position, BlockSource blockSource) { -+ if (stack.isEmpty()) return true; -+ ItemEntity itemEntity = DefaultDispenseItemBehavior.prepareItem(level, stack, speed, facing, position); ++ public static boolean spawnItem(Level level, ItemStack itemStack, int accuracy, Direction direction, Position position, BlockSource source) { ++ if (itemStack.isEmpty()) return true; ++ ItemEntity itemEntity = DefaultDispenseItemBehavior.prepareItem(level, itemStack, accuracy, direction, position); + -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack); ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, source.pos()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); + + org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(itemEntity.getDeltaMovement())); + level.getCraftServer().getPluginManager().callEvent(event); @@ -66,12 +66,12 @@ + itemEntity.setItem(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem())); + itemEntity.setDeltaMovement(org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getVelocity())); + -+ if (blockSource.state().is(net.minecraft.world.level.block.Blocks.DISPENSER) && !event.getItem().getType().equals(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(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); + if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior.getClass() != DefaultDispenseItemBehavior.class) { -+ dispenseBehavior.dispense(blockSource, eventStack); ++ dispenseBehavior.dispense(source, eventStack); + } else { + level.addFreshEntity(itemEntity); + } @@ -84,4 +84,4 @@ + // CraftBukkit end } - protected void playSound(BlockSource blockSource) { + protected void playSound(final BlockSource source) { diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch index ba660f85657e..de3f5d8968e0 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch @@ -1,20 +1,19 @@ --- a/net/minecraft/core/dispenser/DispenseItemBehavior.java +++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java -@@ -88,10 +_,38 @@ - if (type == null) { - return item; - } else { +@@ -87,12 +_,38 @@ + Direction direction = source.state().getValue(DispenserBlock.FACING); + BlockPos pos = source.pos().relative(direction); + ServerLevel serverLevel = source.level(); + // CraftBukkit start -+ ServerLevel serverLevel = blockSource.level(); -+ ItemStack singleItemStack = item.copyWithCount(1); -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos()); ++ ItemStack singleItemStack = dispensed.copyWithCount(1); ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, source.pos()); + org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack); + + org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + serverLevel.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { -+ return item; ++ return dispensed; + } + + boolean shrink = true; @@ -22,93 +21,42 @@ + shrink = false; + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); + if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { -+ dispenseBehavior.dispense(blockSource, eventStack); -+ return item; -+ } -+ // Paper start - track changed items in the dispense event -+ singleItemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); // unwrap is safe because the stack won't be modified -+ type = ((SpawnEggItem) singleItemStack.getItem()).getType(singleItemStack); -+ // Paper end - track changed item from dispense event -+ } - try { - type.spawn( - blockSource.level(), -- item, -+ singleItemStack, // Paper - track changed item in dispense event - null, - blockSource.pos().relative(direction), - EntitySpawnReason.DISPENSER, -@@ -103,7 +_,8 @@ - return ItemStack.EMPTY; - } - -- item.shrink(1); -+ if (shrink) item.shrink(1); -+ // CraftBukkit end - blockSource.level().gameEvent(null, GameEvent.ENTITY_PLACE, blockSource.pos()); - return item; - } -@@ -122,12 +_,38 @@ - Direction direction = blockSource.state().getValue(DispenserBlock.FACING); - BlockPos blockPos = blockSource.pos().relative(direction); - ServerLevel serverLevel = blockSource.level(); -+ // CraftBukkit start -+ ItemStack singleItemStack = item.copyWithCount(1); -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack); -+ -+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); -+ serverLevel.getCraftServer().getPluginManager().callEvent(event); -+ -+ if (event.isCancelled()) { -+ return item; -+ } -+ -+ boolean shrink = true; -+ if (!event.getItem().equals(craftItem)) { -+ shrink = false; -+ // Chain to handler for new item -+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); -+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { -+ dispenseBehavior.dispense(blockSource, eventStack); -+ return item; ++ dispenseBehavior.dispense(source, eventStack); ++ return dispensed; + } + } + // CraftBukkit end + + final ItemStack newStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); // Paper - use event itemstack (unwrap is fine here because the stack won't be modified) - Consumer consumer = EntityType.appendDefaultStackConfig( -- armorStand1 -> armorStand1.setYRot(direction.toYRot()), serverLevel, item, null -+ armorStand1 -> armorStand1.setYRot(direction.toYRot()), serverLevel, newStack, null // Paper - track changed items in the dispense event + Consumer postSpawnConfig = EntityType.appendDefaultStackConfig( +- armorStandx -> armorStandx.setYRot(direction.toYRot()), serverLevel, dispensed, null ++ armorStandx -> armorStandx.setYRot(direction.toYRot()), serverLevel, newStack, null // Paper - track changed items in the dispense event ); - ArmorStand armorStand = EntityType.ARMOR_STAND.spawn(serverLevel, consumer, blockPos, EntitySpawnReason.DISPENSER, false, false); + ArmorStand armorStand = EntityType.ARMOR_STAND.spawn(serverLevel, postSpawnConfig, pos, EntitySpawnReason.DISPENSER, false, false); if (armorStand != null) { -- item.shrink(1); -+ if (shrink) item.shrink(1); // Paper +- dispensed.shrink(1); ++ if (shrink) dispensed.shrink(1); // Paper } - return item; -@@ -149,8 +_,35 @@ - )) { + return dispensed; +@@ -110,8 +_,33 @@ + .getEntitiesOfClass(AbstractChestedHorse.class, new AABB(pos), entity -> entity.isAlive() && !entity.hasChest())) { if (abstractChestedHorse.isTamed()) { - SlotAccess slot = abstractChestedHorse.getSlot(499); -- if (slot != null && slot.set(item)) { -- item.shrink(1); + SlotAccess slot = abstractChestedHorse.getSlot(AbstractHorse.CHEST_SLOT_OFFSET); +- if (slot != null && slot.set(dispensed)) { +- dispensed.shrink(1); + // CraftBukkit start -+ if (slot != null/* && slot.set(item)*/) { -+ ItemStack singleCopy = item.copyWithCount(1); -+ ServerLevel world = blockSource.level(); -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, blockSource.pos()); ++ if (slot != null/* && slot.set(dispensed)*/) { ++ ItemStack singleCopy = dispensed.copyWithCount(1); ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(source.level(), source.pos()); + org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleCopy); + org.bukkit.event.block.BlockDispenseArmorEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), abstractChestedHorse.getBukkitLivingEntity()); -+ world.getCraftServer().getPluginManager().callEvent(event); + -+ if (event.isCancelled()) { ++ if (!event.callEvent()) { + this.setSuccess(false); -+ return item; ++ return dispensed; + } + + boolean shrink = true; @@ -116,140 +64,138 @@ + shrink = false; + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); + if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { -+ dispenseBehavior.dispense(blockSource, eventStack); -+ return item; ++ dispenseBehavior.dispense(source, eventStack); ++ return dispensed; + } + } + slot.set(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem())); + // CraftBukkit end + -+ if (shrink) item.shrink(1); // Paper - actually handle here ++ if (shrink) dispensed.shrink(1); // Paper - actually handle here this.setSuccess(true); - return item; + return dispensed; } -@@ -189,8 +_,45 @@ - DispensibleContainerItem dispensibleContainerItem = (DispensibleContainerItem)item.getItem(); - BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); - Level level = blockSource.level(); +@@ -150,8 +_,45 @@ + DispensibleContainerItem bucket = (DispensibleContainerItem)dispensed.getItem(); + BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING)); + Level level = source.level(); + // CraftBukkit start -+ BlockState state = level.getBlockState(blockPos); -+ ItemStack dispensedItem = item; // Paper - track changed item from the dispense event ++ BlockState state = level.getBlockState(target); ++ ItemStack dispensedItem = dispensed; // Paper - track changed item from the dispense event + // Paper start - correctly check if the bucket place will succeed + /* Taken from SolidBucketItem#emptyContents */ -+ boolean willEmptyContentsSolidBucketItem = dispensibleContainerItem instanceof net.minecraft.world.item.SolidBucketItem && level.isInWorldBounds(blockPos) && state.isAir(); ++ boolean willEmptyContentsSolidBucketItem = bucket instanceof net.minecraft.world.item.SolidBucketItem && level.isInWorldBounds(target) && state.isAir(); + /* Taken from BucketItem#emptyContents */ -+ boolean willEmptyBucketItem = dispensibleContainerItem instanceof final net.minecraft.world.item.BucketItem bucketItem && bucketItem.content instanceof net.minecraft.world.level.material.FlowingFluid && (state.isAir() || state.canBeReplaced(bucketItem.content) || (state.getBlock() instanceof net.minecraft.world.level.block.LiquidBlockContainer liquidBlockContainer && liquidBlockContainer.canPlaceLiquid(null, level, blockPos, state, bucketItem.content))); ++ boolean willEmptyBucketItem = bucket instanceof final net.minecraft.world.item.BucketItem bucketItem && bucketItem.content instanceof net.minecraft.world.level.material.FlowingFluid && (state.isAir() || state.canBeReplaced(bucketItem.content) || (state.getBlock() instanceof net.minecraft.world.level.block.LiquidBlockContainer liquidBlockContainer && liquidBlockContainer.canPlaceLiquid(null, level, target, state, bucketItem.content))); + 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, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, source.pos()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(dispensed.copyWithCount(1)); // Paper - single item in event + -+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(blockPos)); ++ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(target)); + level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { -+ return item; ++ return dispensed; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); + if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { -+ dispenseBehavior.dispense(blockSource, eventStack); -+ return item; ++ dispenseBehavior.dispense(source, eventStack); ++ return dispensed; + } + } + + // Paper start - track changed item from dispense event + dispensedItem = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); // unwrap is safe here as the stack isn't mutated -+ dispensibleContainerItem = (DispensibleContainerItem) dispensedItem.getItem(); ++ bucket = (DispensibleContainerItem)dispensedItem.getItem(); + // Paper end - track changed item from dispense event + } + // CraftBukkit end + - if (dispensibleContainerItem.emptyContents(null, level, blockPos, null)) { -- dispensibleContainerItem.checkExtraContent(null, level, item, blockPos); -+ dispensibleContainerItem.checkExtraContent(null, level, dispensedItem, blockPos); // Paper - track changed item from dispense event - return this.consumeWithRemainder(blockSource, item, new ItemStack(Items.BUCKET)); + if (bucket.emptyContents(null, level, target, null)) { +- bucket.checkExtraContent(null, level, dispensed, target); ++ bucket.checkExtraContent(null, level, dispensedItem, target); // Paper - track changed item from dispense event + return this.consumeWithRemainder(source, dispensed, new ItemStack(Items.BUCKET)); } else { - return this.defaultDispenseItemBehavior.dispense(blockSource, item); -@@ -213,12 +_,19 @@ - BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); - BlockState blockState = levelAccessor.getBlockState(blockPos); - if (blockState.getBlock() instanceof BucketPickup bucketPickup) { -- ItemStack itemStack = bucketPickup.pickupBlock(null, levelAccessor, blockPos, blockState); -+ ItemStack itemStack = bucketPickup.pickupBlock(null, org.bukkit.craftbukkit.util.DummyGeneratorAccess.INSTANCE, blockPos, blockState); // CraftBukkit - if (itemStack.isEmpty()) { - return super.execute(blockSource, item); + return this.defaultDispenseItemBehavior.dispense(source, dispensed); +@@ -174,12 +_,19 @@ + BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING)); + BlockState blockState = level.getBlockState(target); + if (blockState.getBlock() instanceof BucketPickup bucket) { +- ItemStack pickup = bucket.pickupBlock(null, level, target, blockState); ++ ItemStack pickup = bucket.pickupBlock(null, org.bukkit.craftbukkit.util.DummyLevelAccessor.INSTANCE, target, blockState); // CraftBukkit + if (pickup.isEmpty()) { + return super.execute(source, dispensed); } else { - levelAccessor.gameEvent(null, GameEvent.FLUID_PICKUP, blockPos); - Item item1 = itemStack.getItem(); + level.gameEvent(null, GameEvent.FLUID_PICKUP, target); + Item targetType = pickup.getItem(); + // Paper start - Call BlockDispenseEvent -+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this); ++ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, target, dispensed, this); + if (result != null) { + return result; + } + // Paper end - Call BlockDispenseEvent -+ itemStack = bucketPickup.pickupBlock(null, levelAccessor, blockPos, blockState); // CraftBukkit - from above - return this.consumeWithRemainder(blockSource, item, new ItemStack(item1)); ++ pickup = bucket.pickupBlock(null, level, target, blockState); // CraftBukkit - from above + return this.consumeWithRemainder(source, dispensed, new ItemStack(targetType)); } } else { -@@ -233,15 +_,26 @@ +@@ -194,15 +_,26 @@ this.setSuccess(true); - Direction direction = blockSource.state().getValue(DispenserBlock.FACING); - BlockPos blockPos = blockSource.pos().relative(direction); + Direction facing = source.state().getValue(DispenserBlock.FACING); + BlockPos targetPos = source.pos().relative(facing); + // Paper start - Call BlockDispenseEvent -+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this); ++ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, targetPos, dispensed, this); + if (result != null) { + this.setSuccess(false); + return result; + } + // Paper end - Call BlockDispenseEvent - BlockState blockState = serverLevel.getBlockState(blockPos); - if (BaseFireBlock.canBePlacedAt(serverLevel, blockPos, direction)) { -- serverLevel.setBlockAndUpdate(blockPos, BaseFireBlock.getState(serverLevel, blockPos)); -- serverLevel.gameEvent(null, GameEvent.BLOCK_PLACE, blockPos); + BlockState target = level.getBlockState(targetPos); + if (BaseFireBlock.canBePlacedAt(level, targetPos, facing)) { + // CraftBukkit start - Ignition by dispensing flint and steel -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(serverLevel, blockPos, blockSource.pos()).isCancelled()) { -+ serverLevel.setBlockAndUpdate(blockPos, BaseFireBlock.getState(serverLevel, blockPos)); -+ serverLevel.gameEvent(null, GameEvent.BLOCK_PLACE, blockPos); ++ 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(blockState) || CandleBlock.canLight(blockState) || CandleCakeBlock.canLight(blockState)) { - serverLevel.setBlockAndUpdate(blockPos, blockState.setValue(BlockStateProperties.LIT, true)); - serverLevel.gameEvent(null, GameEvent.BLOCK_CHANGE, blockPos); - } else if (blockState.getBlock() instanceof TntBlock) { -- if (TntBlock.prime(serverLevel, blockPos)) { -+ if (TntBlock.prime(serverLevel, blockPos, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(serverLevel, blockPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.DISPENSER, null, blockSource.pos()))) { // CraftBukkit - TNTPrimeEvent - serverLevel.removeBlock(blockPos, false); + } 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); + } else if (target.getBlock() instanceof TntBlock) { +- if (TntBlock.prime(level, targetPos)) { ++ if (TntBlock.prime(level, targetPos, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, targetPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.DISPENSER, null, source.pos()))) { // CraftBukkit - TNTPrimeEvent + level.removeBlock(targetPos, false); } else { this.setSuccess(false); -@@ -263,11 +_,46 @@ +@@ -224,11 +_,46 @@ this.setSuccess(true); - Level level = blockSource.level(); - BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); + Level level = source.level(); + BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING)); + // Paper start - Call BlockDispenseEvent -+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this); ++ 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.captureTreeGeneration = true; // CraftBukkit - if (!BoneMealItem.growCrop(item, level, blockPos) && !BoneMealItem.growWaterPlant(item, level, blockPos, null)) { + if (!BoneMealItem.growCrop(dispensed, level, target) && !BoneMealItem.growWaterPlant(dispensed, level, target, null)) { this.setSuccess(false); } else if (!level.isClientSide()) { - level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_PLANT_GROWTH, blockPos, 15); + level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_PLANT_GROWTH, target, 15); } + // CraftBukkit start + level.captureTreeGeneration = false; + if (!level.capturedBlockStates.isEmpty()) { + org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeType; + net.minecraft.world.level.block.SaplingBlock.treeType = null; -+ org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(blockPos, level); ++ org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(target, level); + List states = new java.util.ArrayList<>(level.capturedBlockStates.values()); + level.capturedBlockStates.clear(); + org.bukkit.event.world.StructureGrowEvent structureEvent = null; @@ -266,29 +212,29 @@ + for (org.bukkit.block.BlockState state : states) { + org.bukkit.craftbukkit.block.CraftBlockState craftBlockState = (org.bukkit.craftbukkit.block.CraftBlockState) state; + craftBlockState.place(craftBlockState.getFlags()); -+ blockSource.level().checkCapturedTreeStateForObserverNotify(blockPos, craftBlockState); // Paper - notify observers even if grow failed ++ source.level().checkCapturedTreeStateForObserverNotify(target, craftBlockState); // Paper - notify observers even if grow failed + } + } + } + // CraftBukkit end - return item; + return dispensed; } -@@ -281,11 +_,36 @@ - return item; +@@ -242,11 +_,36 @@ + return dispensed; } else { - BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); -- PrimedTnt primedTnt = new PrimedTnt(serverLevel, blockPos.getX() + 0.5, blockPos.getY(), blockPos.getZ() + 0.5, null); + BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING)); +- PrimedTnt tnt = new PrimedTnt(level, target.getX() + 0.5, target.getY(), target.getZ() + 0.5, null); + // CraftBukkit start -+ ItemStack singleItemStack = item.copyWithCount(1); // Paper - shrink at end and single item in event -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos()); ++ ItemStack singleItemStack = dispensed.copyWithCount(1); // Paper - shrink at end and single item in event ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, source.pos()); + org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack); + -+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockPos.getX() + 0.5D, (double) blockPos.getY(), (double) blockPos.getZ() + 0.5D)); -+ serverLevel.getCraftServer().getPluginManager().callEvent(event); ++ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) target.getX() + 0.5D, (double) target.getY(), (double) target.getZ() + 0.5D)); ++ level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { -+ return item; ++ return dispensed; + } + + boolean shrink = true; @@ -296,150 +242,150 @@ + shrink = false; + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); + if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { -+ dispenseBehavior.dispense(blockSource, eventStack); -+ return item; ++ dispenseBehavior.dispense(source, eventStack); ++ return dispensed; + } + } + -+ PrimedTnt primedTnt = new PrimedTnt(serverLevel, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), null); ++ PrimedTnt tnt = new PrimedTnt(level, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), null); + // CraftBukkit end - serverLevel.addFreshEntity(primedTnt); - serverLevel.playSound(null, primedTnt.getX(), primedTnt.getY(), primedTnt.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F); -- serverLevel.gameEvent(null, GameEvent.ENTITY_PLACE, blockPos); -- item.shrink(1); -+ serverLevel.gameEvent(null, GameEvent.ENTITY_PLACE, org.bukkit.craftbukkit.util.CraftVector.toBlockPos(event.getVelocity())); // Paper - update game event position -+ if (shrink) item.shrink(1); // Paper + level.addFreshEntity(tnt); + level.playSound(null, tnt.getX(), tnt.getY(), tnt.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F); +- level.gameEvent(null, GameEvent.ENTITY_PLACE, target); +- dispensed.shrink(1); ++ level.gameEvent(null, GameEvent.ENTITY_PLACE, org.bukkit.craftbukkit.util.CraftVector.toBlockPos(event.getVelocity())); // Paper - update game event position ++ if (shrink) dispensed.shrink(1); // Paper this.setSuccess(true); - return item; + return dispensed; } -@@ -299,6 +_,13 @@ - Level level = blockSource.level(); - Direction direction = blockSource.state().getValue(DispenserBlock.FACING); - BlockPos blockPos = blockSource.pos().relative(direction); +@@ -260,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(blockSource, blockPos, item, this); ++ 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(blockPos) && WitherSkullBlock.canSpawnMob(level, blockPos, item)) { + if (level.isEmptyBlock(target) && WitherSkullBlock.canSpawnMob(level, target, dispensed)) { level.setBlock( - blockPos, -@@ -314,7 +_,7 @@ - item.shrink(1); + target, +@@ -275,7 +_,7 @@ + dispensed.shrink(1); this.setSuccess(true); } else { -- this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item)); -+ this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item, this)); // Paper - fix possible StackOverflowError +- this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(source, dispensed)); ++ this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(source, dispensed, this)); // Paper - fix possible StackOverflowError } - return item; -@@ -327,6 +_,13 @@ - Level level = blockSource.level(); - BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); - CarvedPumpkinBlock carvedPumpkinBlock = (CarvedPumpkinBlock)Blocks.CARVED_PUMPKIN; + return dispensed; +@@ -288,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(blockSource, blockPos, item, this); ++ 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(blockPos) && carvedPumpkinBlock.canSpawnGolem(level, blockPos)) { + if (level.isEmptyBlock(target) && pumpkinBlock.canSpawnGolem(level, target)) { if (!level.isClientSide()) { - level.setBlock(blockPos, carvedPumpkinBlock.defaultBlockState(), Block.UPDATE_ALL); -@@ -336,7 +_,7 @@ - item.shrink(1); + level.setBlock(target, pumpkinBlock.defaultBlockState(), Block.UPDATE_ALL); +@@ -297,7 +_,7 @@ + dispensed.shrink(1); this.setSuccess(true); } else { -- this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item)); -+ this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item, this)); // Paper - fix possible StackOverflowError +- this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(source, dispensed)); ++ this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(source, dispensed, this)); // Paper - fix possible StackOverflowError } - return item; -@@ -362,6 +_,12 @@ - ServerLevel serverLevel = blockSource.level(); - BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); - BlockState blockState = serverLevel.getBlockState(blockPos); + return dispensed; +@@ -335,6 +_,12 @@ + ServerLevel level = source.level(); + BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING)); + BlockState state = level.getBlockState(target); + // Paper start - Call BlockDispenseEvent -+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this); ++ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, target, dispensed, this); + if (result != null) { + return result; + } + // Paper end - Call BlockDispenseEvent - if (blockState.is( - BlockTags.BEEHIVES, - blockStateBase -> blockStateBase.hasProperty(BeehiveBlock.HONEY_LEVEL) && blockStateBase.getBlock() instanceof BeehiveBlock -@@ -390,6 +_,13 @@ + if (state.is(BlockTags.BEEHIVES, s -> s.hasProperty(BeehiveBlock.HONEY_LEVEL) && s.getBlock() instanceof BeehiveBlock) + && state.getValue(BeehiveBlock.HONEY_LEVEL) >= 5) { + ((BeehiveBlock)state.getBlock()) +@@ -360,6 +_,13 @@ this.setSuccess(true); if (blockState.is(Blocks.RESPAWN_ANCHOR)) { if (blockState.getValue(RespawnAnchorBlock.CHARGE) != 4) { + // Paper start - Call BlockDispenseEvent -+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this); ++ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, pos, dispensed, this); + if (result != null) { + this.setSuccess(false); + return result; + } + // Paper end - Call BlockDispenseEvent - RespawnAnchorBlock.charge(null, level, blockPos, blockState); - item.shrink(1); + RespawnAnchorBlock.charge(null, level, pos, blockState); + dispensed.shrink(1); } else { -@@ -413,6 +_,28 @@ +@@ -383,6 +_,28 @@ this.setSuccess(false); - return item; + return dispensed; } else { + // CraftBukkit start -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item); // Paper - ignore stack size on damageable items ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, source.pos()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(dispensed); // Paper - ignore stack size on damageable items + -+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), entitiesOfClass.get(0).getBukkitLivingEntity()); -+ serverLevel.getCraftServer().getPluginManager().callEvent(event); ++ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), armadillos.get(0).getBukkitLivingEntity()); ++ level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + this.setSuccess(false); -+ return item; ++ return dispensed; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); + if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { -+ dispenseBehavior.dispense(blockSource, eventStack); -+ return item; ++ dispenseBehavior.dispense(source, eventStack); ++ return dispensed; + } + } + // CraftBukkit end - for (Armadillo armadillo : entitiesOfClass) { - if (armadillo.brushOffScute(null, item)) { - item.hurtAndBreak(16, serverLevel, null, item1 -> {}); -@@ -433,6 +_,13 @@ - BlockState blockState = level.getBlockState(blockPos); - Optional waxed = HoneycombItem.getWaxed(blockState); - if (waxed.isPresent()) { + for (Armadillo armadillo : armadillos) { + if (armadillo.brushOffScute(null, dispensed)) { + dispensed.hurtAndBreak(16, level, null, item -> {}); +@@ -403,6 +_,13 @@ + BlockState blockState = level.getBlockState(pos); + Optional maybeWaxed = HoneycombItem.getWaxed(blockState); + if (maybeWaxed.isPresent()) { + // Paper start - Call BlockDispenseEvent -+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this); ++ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, pos, dispensed, this); + if (result != null) { + this.setSuccess(false); + return result; + } + // Paper end - Call BlockDispenseEvent - level.setBlockAndUpdate(blockPos, waxed.get()); - level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_WAX_ON, blockPos, 0); - item.shrink(1); -@@ -460,6 +_,12 @@ - if (!serverLevel.getBlockState(blockPos1).is(BlockTags.CONVERTABLE_TO_MUD)) { - return this.defaultDispenseItemBehavior.dispense(blockSource, item); + level.setBlockAndUpdate(pos, maybeWaxed.get()); + level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_WAX_ON, pos, 0); + dispensed.shrink(1); +@@ -430,6 +_,12 @@ + if (!level.getBlockState(target).is(BlockTags.CONVERTABLE_TO_MUD)) { + return this.defaultDispenseItemBehavior.dispense(source, dispensed); } else { + // Paper start - Call BlockDispenseEvent -+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos1, item, this); ++ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, target, dispensed, this); + if (result != null) { + return result; + } + // Paper end - Call BlockDispenseEvent - if (!serverLevel.isClientSide()) { - for (int i = 0; i < 5; i++) { - serverLevel.sendParticles( + if (!level.isClientSide()) { + RandomSource random = level.getRandom(); + diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch index bf7562248686..fd59898eb292 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch @@ -3,35 +3,35 @@ @@ -14,10 +_,17 @@ @Override - protected ItemStack execute(BlockSource blockSource, ItemStack item) { -- return dispenseEquipment(blockSource, item) ? item : super.execute(blockSource, item); -+ return dispenseEquipment(blockSource, item, this) ? item : super.execute(blockSource, item); // Paper - fix possible StackOverflowError + protected ItemStack execute(final BlockSource source, final ItemStack dispensed) { +- return dispenseEquipment(source, dispensed) ? dispensed : super.execute(source, dispensed); ++ return dispenseEquipment(source, dispensed, this) ? dispensed : super.execute(source, dispensed); // Paper - fix possible StackOverflowError } + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - public static boolean dispenseEquipment(BlockSource blockSource, ItemStack item) { + public static boolean dispenseEquipment(final BlockSource source, final ItemStack dispensed) { + // Paper start -+ return dispenseEquipment(blockSource, item, null); ++ return dispenseEquipment(source, dispensed, null); + } + -+ public static boolean dispenseEquipment(BlockSource blockSource, ItemStack item, @javax.annotation.Nullable DispenseItemBehavior currentBehavior) { ++ public static boolean dispenseEquipment(final BlockSource source, final ItemStack dispensed, final @org.jspecify.annotations.Nullable DispenseItemBehavior currentBehavior) { + // Paper end - BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); - List entitiesOfClass = blockSource.level() - .getEntitiesOfClass(LivingEntity.class, new AABB(blockPos), entity -> entity.canEquipWithDispenser(item)); -@@ -26,13 +_,39 @@ + BlockPos pos = source.pos().relative(source.state().getValue(DispenserBlock.FACING)); + List entities = source.level().getEntitiesOfClass(LivingEntity.class, new AABB(pos), entity -> entity.canEquipWithDispenser(dispensed)); + if (entities.isEmpty()) { +@@ -25,13 +_,39 @@ } else { - LivingEntity livingEntity = entitiesOfClass.getFirst(); - EquipmentSlot equipmentSlotForItem = livingEntity.getEquipmentSlotForItem(item); -- ItemStack itemStack = item.split(1); -- livingEntity.setItemSlot(equipmentSlotForItem, itemStack); -+ ItemStack itemStack = item.copyWithCount(1); // Paper - shrink below and single item in event + LivingEntity target = entities.getFirst(); + EquipmentSlot slot = target.getEquipmentSlotForItem(dispensed); +- ItemStack equip = dispensed.split(1); +- target.setItemSlot(slot, equip); ++ ItemStack equip = dispensed.copyWithCount(1); // Paper - shrink below and single item in event + // CraftBukkit start -+ net.minecraft.world.level.Level world = blockSource.level(); -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); ++ net.minecraft.world.level.Level world = source.level(); ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, source.pos()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(equip); + -+ org.bukkit.event.block.BlockDispenseArmorEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) livingEntity.getBukkitEntity()); ++ org.bukkit.event.block.BlockDispenseArmorEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) target.getBukkitEntity()); + world.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { @@ -43,21 +43,21 @@ + shrink = false; + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseItemBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); ++ DispenseItemBehavior dispenseItemBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); + if (dispenseItemBehavior != DispenseItemBehavior.NOOP && (currentBehavior == null || dispenseItemBehavior != currentBehavior)) { -+ dispenseItemBehavior.dispense(blockSource, eventStack); ++ dispenseItemBehavior.dispense(source, eventStack); + return true; + } + } + -+ livingEntity.setItemSlot(equipmentSlotForItem, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem())); ++ target.setItemSlot(slot, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem())); + // CraftBukkit end - if (livingEntity instanceof Mob mob) { - mob.setGuaranteedDrop(equipmentSlotForItem); - mob.setPersistenceRequired(); + if (target instanceof Mob targetMob) { + targetMob.setGuaranteedDrop(slot); + targetMob.setPersistenceRequired(); } -+ if (shrink) item.shrink(1); // Paper - shrink here ++ if (shrink) dispensed.shrink(1); // Paper - shrink here return true; } } diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java.patch index 0acafa1f5ab3..f3a2e22b82ba 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java.patch @@ -1,21 +1,19 @@ --- a/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java +++ b/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java -@@ -58,12 +_,35 @@ +@@ -58,12 +_,37 @@ } - Vec3 vec31 = new Vec3(d, d1 + d3, d2); -- AbstractMinecart abstractMinecart = AbstractMinecart.createMinecart( -- serverLevel, vec31.x, vec31.y, vec31.z, this.entityType, EntitySpawnReason.DISPENSER, item, null -- ); -+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink below and single item in event -+ org.bukkit.block.Block block2 = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1); + Vec3 spawnPos = new Vec3(spawnX, spawnY + yOffset, spawnZ); ++ // CraftBukkit start ++ ItemStack singleItemStack = dispensed.copyWithCount(1); // Paper - shrink below and single item in event ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, source.pos()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack); + -+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block2, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(vec31)); -+ serverLevel.getCraftServer().getPluginManager().callEvent(event); ++ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(spawnPos)); ++ level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { -+ return item; ++ return dispensed; + } + + boolean shrink = true; @@ -23,21 +21,23 @@ + shrink = false; + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseItemBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); -+ if (dispenseItemBehavior != DispenseItemBehavior.NOOP && dispenseItemBehavior != this) { -+ dispenseItemBehavior.dispense(blockSource, eventStack); -+ return item; ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); ++ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { ++ dispenseBehavior.dispense(source, eventStack); ++ return dispensed; + } + } + -+ itemstack1 = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ AbstractMinecart abstractMinecart = AbstractMinecart.createMinecart(serverLevel, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.entityType, EntitySpawnReason.DISPENSER, itemstack1, null); -+ - if (abstractMinecart != null) { -- serverLevel.addFreshEntity(abstractMinecart); -- item.shrink(1); -+ if (serverLevel.addFreshEntity(abstractMinecart) && shrink) item.shrink(1); // Paper - if entity add was successful and supposed to shrink ++ singleItemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); + AbstractMinecart minecart = AbstractMinecart.createMinecart( +- level, spawnPos.x, spawnPos.y, spawnPos.z, this.entityType, EntitySpawnReason.DISPENSER, dispensed, null ++ level, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.entityType, EntitySpawnReason.DISPENSER, singleItemStack, null + ); + if (minecart != null) { +- level.addFreshEntity(minecart); +- dispensed.shrink(1); ++ if (level.addFreshEntity(minecart) && shrink) dispensed.shrink(1); // Paper - if entity add was successful and supposed to shrink + // CraftBukkit end } - return item; + return dispensed; diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch index 3fa36c36dfd4..772e836788a4 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch @@ -1,29 +1,29 @@ --- a/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java +++ b/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java @@ -27,17 +_,36 @@ - ServerLevel serverLevel = blockSource.level(); - Direction direction = blockSource.state().getValue(DispenserBlock.FACING); - Position dispensePosition = this.dispenseConfig.positionFunction().getDispensePosition(blockSource, direction); + ServerLevel level = source.level(); + Direction direction = source.state().getValue(DispenserBlock.FACING); + Position position = this.dispenseConfig.positionFunction().getDispensePosition(source, direction); - Projectile.spawnProjectileUsingShoot( -- this.projectileItem.asProjectile(serverLevel, dispensePosition, item, direction), -- serverLevel, -- item, +- this.projectileItem.asProjectile(level, position, dispensed, direction), +- level, +- dispensed, - direction.getStepX(), - direction.getStepY(), - direction.getStepZ(), - this.dispenseConfig.power(), - this.dispenseConfig.uncertainty() - ); -- item.shrink(1); -+ ItemStack singleItemStack = item.copyWithCount(1); // Paper - shrink below and single item in event -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos()); +- dispensed.shrink(1); ++ ItemStack singleItemStack = dispensed.copyWithCount(1); // Paper - shrink below and single item in event ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, source.pos()); + org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack); + + org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(direction.getStepX(), direction.getStepY(), direction.getStepZ())); -+ serverLevel.getCraftServer().getPluginManager().callEvent(event); ++ level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { -+ return item; ++ return dispensed; + } + + boolean shrink = true; @@ -31,20 +31,20 @@ + shrink = false; + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); + if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { -+ dispenseBehavior.dispense(blockSource, eventStack); -+ return item; ++ dispenseBehavior.dispense(source, eventStack); ++ return dispensed; + } + } + + // SPIGOT-7923: Avoid create projectiles with empty item + if (!singleItemStack.isEmpty()) { -+ Projectile projectile = Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(serverLevel, dispensePosition, org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()), direction), serverLevel, singleItemStack, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // Paper - track changed items in the dispense event; unwrap is safe here because all uses of the stack make their own copies -+ projectile.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource(blockSource.blockEntity()); ++ Projectile projectile = Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(level, position, org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()), direction), level, singleItemStack, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // Paper - track changed items in the dispense event; unwrap is safe here because all uses of the stack make their own copies ++ projectile.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource(source.blockEntity()); + } -+ if (shrink) item.shrink(1); ++ if (shrink) dispensed.shrink(1); + // CraftBukkit end - return item; + return dispensed; } diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch index 4d6ce2c36aca..e33af392147a 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch @@ -2,58 +2,58 @@ +++ b/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java @@ -20,9 +_,30 @@ @Override - protected ItemStack execute(BlockSource blockSource, ItemStack item) { - ServerLevel serverLevel = blockSource.level(); + protected ItemStack execute(final BlockSource source, final ItemStack dispensed) { + ServerLevel level = source.level(); + // CraftBukkit start -+ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item); // Paper - ignore stack size on damageable items ++ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, source.pos()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(dispensed); // Paper - ignore stack size on damageable items + org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); -+ serverLevel.getCraftServer().getPluginManager().callEvent(event); ++ level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + this.setSuccess(false); -+ return item; ++ return dispensed; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); + if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { -+ dispenseBehavior.dispense(blockSource, eventStack); -+ return item; ++ dispenseBehavior.dispense(source, eventStack); ++ return dispensed; + } + } + // CraftBukkit end - if (!serverLevel.isClientSide()) { - BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); -- this.setSuccess(tryShearBeehive(serverLevel, item, blockPos) || tryShearEntity(serverLevel, blockPos, item)); -+ this.setSuccess(tryShearBeehive(serverLevel, item, blockPos) || tryShearEntity(serverLevel, blockPos, item, bukkitBlock, craftItem)); // CraftBukkit + if (!level.isClientSide()) { + BlockPos pos = source.pos().relative(source.state().getValue(DispenserBlock.FACING)); +- this.setSuccess(tryShearBeehive(level, dispensed, pos) || tryShearEntity(level, pos, dispensed)); ++ this.setSuccess(tryShearBeehive(level, dispensed, pos) || tryShearEntity(level, pos, dispensed, bukkitBlock, craftItem)); // CraftBukkit if (this.isSuccess()) { - item.hurtAndBreak(1, serverLevel, null, item1 -> {}); + dispensed.hurtAndBreak(1, level, null, item -> {}); } -@@ -50,14 +_,22 @@ +@@ -47,14 +_,22 @@ return false; } -- private static boolean tryShearEntity(ServerLevel level, BlockPos pos, ItemStack stack) { -+ private static boolean tryShearEntity(ServerLevel level, BlockPos pos, ItemStack stack, org.bukkit.block.Block bukkitBlock, org.bukkit.craftbukkit.inventory.CraftItemStack craftItem) { // CraftBukkit - add args +- private static boolean tryShearEntity(final ServerLevel level, final BlockPos pos, final ItemStack tool) { ++ private static boolean tryShearEntity(final ServerLevel level, final BlockPos pos, final ItemStack tool, org.bukkit.block.Block bukkitBlock, org.bukkit.craftbukkit.inventory.CraftItemStack craftItem) { // CraftBukkit - add args for (Entity entity : level.getEntitiesOfClass(Entity.class, new AABB(pos), EntitySelector.NO_SPECTATORS)) { if (entity.shearOffAllLeashConnections(null)) { return true; } if (entity instanceof Shearable shearable && shearable.readyForShearing()) { -- shearable.shear(level, SoundSource.BLOCKS, stack); +- shearable.shear(level, SoundSource.BLOCKS, tool); + // CraftBukkit start + // Paper start - Add drops to shear events -+ org.bukkit.event.block.BlockShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockShearEntityEvent(entity, bukkitBlock, craftItem, shearable.generateDefaultDrops(level, stack)); ++ org.bukkit.event.block.BlockShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockShearEntityEvent(entity, bukkitBlock, craftItem, shearable.generateDefaultDrops(level, tool)); + if (event.isCancelled()) { + // Paper end - Add drops to shear events + continue; + } + // CraftBukkit end -+ shearable.shear(level, SoundSource.BLOCKS, stack, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops())); // Paper - Add drops to shear events ++ shearable.shear(level, SoundSource.BLOCKS, tool, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops())); // Paper - Add drops to shear events level.gameEvent(null, GameEvent.SHEAR, pos); return true; } diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java.patch index 06c88a18571c..acc9e4c90c27 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java.patch @@ -1,40 +1,40 @@ --- a/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java +++ b/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java @@ -22,10 +_,36 @@ - BlockPos blockPos = blockSource.pos().relative(direction); - Direction direction1 = blockSource.level().isEmptyBlock(blockPos.below()) ? direction : Direction.UP; + BlockPos relativePos = source.pos().relative(facing); + Direction clickedFace = source.level().isEmptyBlock(relativePos.below()) ? facing : Direction.UP; + // CraftBukkit start -+ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(blockSource.level(), blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); ++ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(source.level(), source.pos()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(dispensed.copyWithCount(1)); + -+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(blockPos)); -+ blockSource.level().getCraftServer().getPluginManager().callEvent(event); ++ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(relativePos)); ++ source.level().getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { -+ return item; ++ return dispensed; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); + if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { -+ dispenseBehavior.dispense(blockSource, eventStack); -+ return item; ++ dispenseBehavior.dispense(source, eventStack); ++ return dispensed; + } + } + // CraftBukkit end try { + // Paper start - track changed items in the dispense event this.setSuccess( -- ((BlockItem)item1).place(new DirectionalPlaceContext(blockSource.level(), blockPos, direction, item, direction1)).consumesAction() -+ ((BlockItem) item1).place(new DirectionalPlaceContext(blockSource.level(), blockPos, direction, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), direction1)).consumesAction() +- ((BlockItem)item).place(new DirectionalPlaceContext(source.level(), relativePos, facing, dispensed, clickedFace)).consumesAction() ++ ((BlockItem)item).place(new DirectionalPlaceContext(source.level(), relativePos, facing, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), clickedFace)).consumesAction() ); + if (this.isSuccess()) { -+ item.shrink(1); // vanilla shrink is in the place function above, manually handle it here ++ dispensed.shrink(1); // vanilla shrink is in the place function above, manually handle it here + } + // Paper end - track changed items in the dispense event } catch (Exception var8) { - LOGGER.error("Error trying to place shulker box at {}", blockPos, var8); + LOGGER.error("Error trying to place shulker box at {}", relativePos, var8); } diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/SpawnEggItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/SpawnEggItemBehavior.java.patch new file mode 100644 index 000000000000..026114b948d0 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/SpawnEggItemBehavior.java.patch @@ -0,0 +1,42 @@ +--- a/net/minecraft/core/dispenser/SpawnEggItemBehavior.java ++++ b/net/minecraft/core/dispenser/SpawnEggItemBehavior.java +@@ -18,14 +_,37 @@ + if (type == null) { + return dispensed; + } else { ++ // Paper start - block dispense event ++ ItemStack singleDispensed = dispensed.copyWithCount(1); ++ final org.bukkit.craftbukkit.inventory.CraftItemStack eventItemCopy = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleDispensed); ++ final org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(source.level(), source.pos()), ++ eventItemCopy, ++ new org.bukkit.util.Vector(0, 0, 0) ++ ); ++ if (!event.callEvent()) return dispensed; ++ ++ final boolean shrink = event.getItem().equals(eventItemCopy); ++ if (!shrink) { ++ final ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); ++ final DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack); ++ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { ++ dispenseBehavior.dispense(source, eventStack); ++ return dispensed; ++ } ++ ++ type = SpawnEggItem.getType(eventStack); ++ singleDispensed = eventStack; ++ } ++ // Paper end - block dispense event + try { +- type.spawn(source.level(), dispensed, null, source.pos().relative(direction), EntitySpawnReason.DISPENSER, direction != Direction.UP, false); ++ type.spawn(source.level(), singleDispensed, null, source.pos().relative(direction), EntitySpawnReason.DISPENSER, direction != Direction.UP, false); // Paper - block dispense event - update used item stack + } catch (Exception var6) { + LOGGER.error("Error while dispensing spawn egg from dispenser at {}", source.pos(), var6); + return ItemStack.EMPTY; + } + +- dispensed.shrink(1); ++ if (shrink) dispensed.shrink(1); // Paper - block dispense event - only shrink if above logic requires it. + source.level().gameEvent(null, GameEvent.ENTITY_PLACE, source.pos()); + return dispensed; + } diff --git a/paper-server/patches/sources/net/minecraft/core/particles/SpellParticleOption.java.patch b/paper-server/patches/sources/net/minecraft/core/particles/SpellParticleOption.java.patch index 03e72654547c..db4e1c2f2e8c 100644 --- a/paper-server/patches/sources/net/minecraft/core/particles/SpellParticleOption.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/particles/SpellParticleOption.java.patch @@ -8,4 +8,4 @@ + public final int color; // Paper - public private final float power; - public static MapCodec codec(ParticleType type) { + public static MapCodec codec(final ParticleType type) { diff --git a/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch b/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch index 83c57bdfd6a3..a1540c1aa6c5 100644 --- a/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/core/registries/BuiltInRegistries.java +++ b/net/minecraft/core/registries/BuiltInRegistries.java -@@ -356,6 +_,11 @@ +@@ -361,6 +_,11 @@ public static final Registry> SLOT_SOURCE_TYPE = registerSimple(Registries.SLOT_SOURCE_TYPE, SlotSources::bootstrap); public static final Registry> TEST_FUNCTION = registerSimple(Registries.TEST_FUNCTION, BuiltinTestFunctions::bootstrap); public static final Registry> REGISTRY = WRITABLE_REGISTRY; @@ -10,17 +10,17 @@ + ); + // Paper end - add built-in registry conversions - private static Registry registerSimple(ResourceKey> registryKey, BuiltInRegistries.RegistryBootstrap bootstrap) { - return internalRegister(registryKey, new MappedRegistry<>(registryKey, Lifecycle.stable(), false), bootstrap); -@@ -383,6 +_,7 @@ - ResourceKey> registryKey, R registry, BuiltInRegistries.RegistryBootstrap bootstrap + private static Registry registerSimple(final ResourceKey> name, final BuiltInRegistries.RegistryBootstrap loader) { + return internalRegister(name, new MappedRegistry<>(name, Lifecycle.stable(), false), loader); +@@ -388,6 +_,7 @@ + final ResourceKey> name, final R registry, final BuiltInRegistries.RegistryBootstrap loader ) { - Bootstrap.checkBootstrapCalled(() -> "registry " + registryKey.identifier()); + Bootstrap.checkBootstrapCalled(() -> "registry " + name.identifier()); + io.papermc.paper.registry.PaperRegistryAccess.instance().registerRegistry(registry); // Paper - initialize API registry - Identifier identifier = registryKey.identifier(); - LOADERS.put(identifier, () -> bootstrap.run(registry)); - WRITABLE_REGISTRY.register((ResourceKey)registryKey, registry, RegistrationInfo.BUILT_IN); -@@ -390,16 +_,34 @@ + Identifier key = name.identifier(); + LOADERS.put(key, () -> loader.run(registry)); + WRITABLE_REGISTRY.register((ResourceKey)name, registry, RegistrationInfo.BUILT_IN); +@@ -395,16 +_,34 @@ } public static void bootStrap() { @@ -47,15 +47,15 @@ + throw new RuntimeException(ex); + } + // Paper end - class-load org.bukkit.Registry - LOADERS.forEach((identifier, supplier) -> { - if (supplier.get() == null) { - LOGGER.error("Unable to bootstrap registry '{}'", identifier); + LOADERS.forEach((key, value) -> { + if (value.get() == null) { + LOGGER.error("Unable to bootstrap registry '{}'", key); } -+ io.papermc.paper.registry.PaperRegistryAccess.instance().lockReferenceHolders(ResourceKey.createRegistryKey(identifier)); // Paper - lock reference holder creation ++ io.papermc.paper.registry.PaperRegistryAccess.instance().lockReferenceHolders(ResourceKey.createRegistryKey(key)); // Paper - lock reference holder creation }); } -@@ -408,6 +_,7 @@ +@@ -413,6 +_,7 @@ for (Registry registry : REGISTRY) { bindBootstrappedTagsToEmpty(registry); diff --git a/paper-server/patches/sources/net/minecraft/data/loot/packs/VanillaChestLoot.java.patch b/paper-server/patches/sources/net/minecraft/data/loot/packs/VanillaChestLoot.java.patch index 289351cfa27d..511748b2a25e 100644 --- a/paper-server/patches/sources/net/minecraft/data/loot/packs/VanillaChestLoot.java.patch +++ b/paper-server/patches/sources/net/minecraft/data/loot/packs/VanillaChestLoot.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/data/loot/packs/VanillaChestLoot.java +++ b/net/minecraft/data/loot/packs/VanillaChestLoot.java -@@ -1046,7 +_,6 @@ +@@ -1047,7 +_,6 @@ .add( LootItem.lootTableItem(Items.COMPASS) .apply(SetItemCountFunction.setCount(ConstantValue.exactly(1.0F))) diff --git a/paper-server/patches/sources/net/minecraft/gametest/framework/GameTestInfo.java.patch b/paper-server/patches/sources/net/minecraft/gametest/framework/GameTestInfo.java.patch index b41f4ca1b28a..c8961dd01ea1 100644 --- a/paper-server/patches/sources/net/minecraft/gametest/framework/GameTestInfo.java.patch +++ b/paper-server/patches/sources/net/minecraft/gametest/framework/GameTestInfo.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/gametest/framework/GameTestInfo.java +++ b/net/minecraft/gametest/framework/GameTestInfo.java -@@ -244,7 +_,7 @@ - AABB structureBounds = this.getStructureBounds(); - List entitiesOfClass = this.getLevel() - .getEntitiesOfClass(Entity.class, structureBounds.inflate(1.0), entity -> !(entity instanceof Player)); -- entitiesOfClass.forEach(entity -> entity.remove(Entity.RemovalReason.DISCARDED)); -+ entitiesOfClass.forEach(entity -> entity.remove(Entity.RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD)); // Paper +@@ -246,7 +_,7 @@ + this.finish(); + AABB bounds = this.getStructureBounds(); + List entities = this.getLevel().getEntitiesOfClass(Entity.class, bounds.inflate(1.0), mob -> !(mob instanceof Player)); +- entities.forEach(e -> e.remove(Entity.RemovalReason.DISCARDED)); ++ entities.forEach(e -> e.remove(Entity.RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD)); // Paper } } diff --git a/paper-server/patches/sources/net/minecraft/gametest/framework/GameTestMainUtil.java.patch b/paper-server/patches/sources/net/minecraft/gametest/framework/GameTestMainUtil.java.patch deleted file mode 100644 index c7d9012b3e1e..000000000000 --- a/paper-server/patches/sources/net/minecraft/gametest/framework/GameTestMainUtil.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/gametest/framework/GameTestMainUtil.java -+++ b/net/minecraft/gametest/framework/GameTestMainUtil.java -@@ -75,7 +_,7 @@ - copyPacks(string, string1); - } - -- LevelStorageSource.LevelStorageAccess levelStorageAccess = LevelStorageSource.createDefault(Paths.get(string)).createAccess("gametestworld"); -+ LevelStorageSource.LevelStorageAccess levelStorageAccess = LevelStorageSource.createDefault(Paths.get(string)).createAccess("gametestworld", net.minecraft.world.level.dimension.LevelStem.OVERWORLD); // Paper - PackRepository packRepository = ServerPacksSource.createPackRepository(levelStorageAccess); - MinecraftServer.spin( - thread -> GameTestServer.create(thread, levelStorageAccess, packRepository, optionalFromOption(optionSet, tests), optionSet.has(verify)) diff --git a/paper-server/patches/sources/net/minecraft/gametest/framework/GameTestServer.java.patch b/paper-server/patches/sources/net/minecraft/gametest/framework/GameTestServer.java.patch index 1ab2a5ca3a50..3cd39b15ac0a 100644 --- a/paper-server/patches/sources/net/minecraft/gametest/framework/GameTestServer.java.patch +++ b/paper-server/patches/sources/net/minecraft/gametest/framework/GameTestServer.java.patch @@ -1,35 +1,38 @@ --- a/net/minecraft/gametest/framework/GameTestServer.java +++ b/net/minecraft/gametest/framework/GameTestServer.java -@@ -146,6 +_,8 @@ - boolean verify +@@ -155,6 +_,8 @@ + final int repeatCount ) { super( + null, // Paper + null, // Paper serverThread, - storageSource, + levelStorageSource, packRepository, -@@ -161,9 +_,17 @@ +@@ -173,13 +_,21 @@ @Override - public boolean initServer() { -- this.setPlayerList(new PlayerList(this, this.registries(), this.playerDataStorage, new EmptyNotificationService()) {}); + protected boolean initServer() { + // Paper start -+ this.setPlayerList(new PlayerList(this, this.registries(), this.playerDataStorage, new EmptyNotificationService()) { + this.setPlayerList(new PlayerList(this, this.registries(), this.playerDataStorage, new EmptyNotificationService()) { + { + Objects.requireNonNull(GameTestServer.this); + } ++ + @Override + public void loadAndSaveFiles() { + throw new UnsupportedOperationException("Should not be called in a GameTestServer"); + } -+ }); + }); + this.initPostWorld(); Gizmos.withCollector(GizmoCollector.NOOP); - this.loadLevel(); + this.loadLevel("blah"); + // Paper end - ServerLevel serverLevel = this.overworld(); - this.testBatches = this.evaluateTestsToRun(serverLevel); + ServerLevel level = this.overworld(); + this.testBatches = this.evaluateTestsToRun(level); LOGGER.info("Started game test server"); -@@ -348,6 +_,13 @@ +@@ -385,6 +_,13 @@ return false; } @@ -41,9 +44,9 @@ + // Paper end + @Override - public boolean isSingleplayerOwner(NameAndId nameAndId) { + public boolean isSingleplayerOwner(final NameAndId nameAndId) { return false; -@@ -395,5 +_,16 @@ +@@ -432,5 +_,16 @@ @Override public void save() { } diff --git a/paper-server/patches/sources/net/minecraft/gametest/framework/StructureUtils.java.patch b/paper-server/patches/sources/net/minecraft/gametest/framework/StructureUtils.java.patch index b367e2c5afda..60705bfeefa0 100644 --- a/paper-server/patches/sources/net/minecraft/gametest/framework/StructureUtils.java.patch +++ b/paper-server/patches/sources/net/minecraft/gametest/framework/StructureUtils.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/gametest/framework/StructureUtils.java +++ b/net/minecraft/gametest/framework/StructureUtils.java @@ -85,7 +_,7 @@ - level.clearBlockEvents(boundingBox); - AABB aabb = AABB.of(boundingBox); - List entitiesOfClass = level.getEntitiesOfClass(Entity.class, aabb, entity -> !(entity instanceof Player)); -- entitiesOfClass.forEach(Entity::discard); -+ entitiesOfClass.forEach(entity -> entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD)); // Paper + level.clearBlockEvents(structureBoundingBox); + AABB bounds = AABB.of(structureBoundingBox); + List livingEntities = level.getEntitiesOfClass(Entity.class, bounds, mob -> !(mob instanceof Player)); +- livingEntities.forEach(Entity::discard); ++ livingEntities.forEach(entity -> entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD)); // Paper } - public static BlockPos getTransformedFarCorner(BlockPos pos, Vec3i offset, Rotation rotation) { + public static BlockPos getTransformedFarCorner(final BlockPos structurePosition, final Vec3i size, final Rotation rotation) { diff --git a/paper-server/patches/sources/net/minecraft/gametest/framework/TestEnvironmentDefinition.java.patch b/paper-server/patches/sources/net/minecraft/gametest/framework/TestEnvironmentDefinition.java.patch index f705c709f77c..54b94a7b7ced 100644 --- a/paper-server/patches/sources/net/minecraft/gametest/framework/TestEnvironmentDefinition.java.patch +++ b/paper-server/patches/sources/net/minecraft/gametest/framework/TestEnvironmentDefinition.java.patch @@ -1,20 +1,29 @@ --- a/net/minecraft/gametest/framework/TestEnvironmentDefinition.java +++ b/net/minecraft/gametest/framework/TestEnvironmentDefinition.java -@@ -124,7 +_,7 @@ - public void setup(ServerLevel level) { +@@ -184,7 +_,7 @@ + GameRuleMap originalState = GameRuleMap.of(); GameRules gameRules = level.getGameRules(); - MinecraftServer server = level.getServer(); -- gameRules.setAll(this.gameRulesMap, server); + this.gameRulesMap.keySet().forEach(rule -> setFromActive(originalState, (GameRule)rule, gameRules)); +- gameRules.setAll(this.gameRulesMap, level.getServer()); + gameRules.setAll(this.gameRulesMap, level); // Paper - per-world game rules + return originalState; } - @Override -@@ -133,7 +_,7 @@ - } +@@ -194,7 +_,7 @@ - private void resetRule(ServerLevel level, GameRule rule) { -- level.getGameRules().set(rule, rule.defaultValue(), level.getServer()); -+ level.getGameRules().set(rule, rule.defaultValue(), level); // Paper - per-world game rules + @Override + public void teardown(final ServerLevel level, final GameRuleMap saveData) { +- level.getGameRules().setAll(saveData, level.getServer()); ++ level.getGameRules().setAll(saveData, level); // Paper - per-world game rules } @Override +@@ -286,7 +_,7 @@ + } + + void apply(final ServerLevel level) { +- level.getServer().setWeatherParameters(this.clearTime, this.rainTime, this.raining, this.thundering); ++ level.getServer().setWeatherParameters(level, this.clearTime, this.rainTime, this.raining, this.thundering); // Paper - per-level WeatherData + } + + @Override diff --git a/paper-server/patches/sources/net/minecraft/nbt/ByteArrayTag.java.patch b/paper-server/patches/sources/net/minecraft/nbt/ByteArrayTag.java.patch index 2d195ba0b8fb..736b116f87bd 100644 --- a/paper-server/patches/sources/net/minecraft/nbt/ByteArrayTag.java.patch +++ b/paper-server/patches/sources/net/minecraft/nbt/ByteArrayTag.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/nbt/ByteArrayTag.java +++ b/net/minecraft/nbt/ByteArrayTag.java @@ -23,6 +_,7 @@ - private static byte[] readAccounted(DataInput input, NbtAccounter accounter) throws IOException { + private static byte[] readAccounted(final DataInput input, final NbtAccounter accounter) throws IOException { accounter.accountBytes(24L); - int _int = input.readInt(); -+ com.google.common.base.Preconditions.checkArgument(_int < 1 << 24); // Spigot - accounter.accountBytes(1L, _int); - byte[] bytes = new byte[_int]; - input.readFully(bytes); + int length = input.readInt(); ++ com.google.common.base.Preconditions.checkArgument(length < 1 << 24); // Spigot + accounter.accountBytes(1L, length); + byte[] data = new byte[length]; + input.readFully(data); diff --git a/paper-server/patches/sources/net/minecraft/nbt/CompoundTag.java.patch b/paper-server/patches/sources/net/minecraft/nbt/CompoundTag.java.patch index c07e64d19e6c..651d45dd8b23 100644 --- a/paper-server/patches/sources/net/minecraft/nbt/CompoundTag.java.patch +++ b/paper-server/patches/sources/net/minecraft/nbt/CompoundTag.java.patch @@ -2,13 +2,13 @@ +++ b/net/minecraft/nbt/CompoundTag.java @@ -54,7 +_,7 @@ - private static CompoundTag loadCompound(DataInput input, NbtAccounter nbtAccounter) throws IOException { - nbtAccounter.accountBytes(48L); -- Map map = Maps.newHashMap(); -+ it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap map = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f); // Paper - Reduce memory footprint of CompoundTag + private static CompoundTag loadCompound(final DataInput input, final NbtAccounter accounter) throws IOException { + accounter.accountBytes(48L); +- Map values = Maps.newHashMap(); ++ it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap values = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f); // Paper - Reduce memory footprint of CompoundTag - byte b; - while ((b = input.readByte()) != 0) { + byte tagType; + while ((tagType = input.readByte()) != 0) { @@ -171,7 +_,7 @@ } @@ -22,9 +22,9 @@ @Override public CompoundTag copy() { -- HashMap map = new HashMap<>(); -- this.tags.forEach((key, value) -> map.put(key, value.copy())); -- return new CompoundTag(map); +- HashMap newTags = new HashMap<>(); +- this.tags.forEach((key, tag) -> newTags.put(key, tag.copy())); +- return new CompoundTag(newTags); + // Paper start - Reduce memory footprint of CompoundTag + it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap ret = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(this.tags.size(), 0.8f); + java.util.Iterator> iterator = (this.tags instanceof it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap) ? ((it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap)this.tags).object2ObjectEntrySet().fastIterator() : this.tags.entrySet().iterator(); @@ -39,30 +39,30 @@ @Override @@ -523,22 +_,47 @@ - this.merge((CompoundTag)mapCodec.encoder().encodeStart(ops, data).getOrThrow()); + this.merge((CompoundTag)codec.encoder().encodeStart(ops, value).getOrThrow()); } -- public Optional read(String key, Codec codec) { -+ public Optional read(String key, Codec codec) { // Paper - option to read via codec without logging errors - diff on change - return this.read(key, codec, NbtOps.INSTANCE); +- public Optional read(final String name, final Codec codec) { ++ public Optional read(final String name, final Codec codec) { // Paper - option to read via codec without logging errors - diff on change + return this.read(name, codec, NbtOps.INSTANCE); } -- public Optional read(String key, Codec codec, DynamicOps ops) { -+ public Optional read(String key, Codec codec, DynamicOps ops) { // Paper - option to read via codec without logging errors - diff on change - Tag tag = this.get(key); +- public Optional read(final String name, final Codec codec, final DynamicOps ops) { ++ public Optional read(final String name, final Codec codec, final DynamicOps ops) { // Paper - option to read via codec without logging errors - diff on change + Tag tag = this.get(name); return tag == null ? Optional.empty() - : codec.parse(ops, tag).resultOrPartial(string -> LOGGER.error("Failed to read field ({}={}): {}", key, tag, string)); + : codec.parse(ops, tag).resultOrPartial(error -> LOGGER.error("Failed to read field ({}={}): {}", name, tag, error)); } -- public Optional read(MapCodec mapCodec) { -+ public Optional read(MapCodec mapCodec) { // Paper - option to read via codec without logging errors - diff on change - return this.read(mapCodec, NbtOps.INSTANCE); +- public Optional read(final MapCodec codec) { ++ public Optional read(final MapCodec codec) { // Paper - option to read via codec without logging errors - diff on change + return this.read(codec, NbtOps.INSTANCE); } -- public Optional read(MapCodec mapCodec, DynamicOps ops) { -+ public Optional read(MapCodec mapCodec, DynamicOps ops) { // Paper - option to read via codec without logging errors - diff on change - return mapCodec.decode(ops, ops.getMap(this).getOrThrow()).resultOrPartial(string -> LOGGER.error("Failed to read value ({}): {}", this, string)); +- public Optional read(final MapCodec codec, final DynamicOps ops) { ++ public Optional read(final MapCodec codec, final DynamicOps ops) { // Paper - option to read via codec without logging errors - diff on change + return codec.decode(ops, ops.getMap(this).getOrThrow()).resultOrPartial(error -> LOGGER.error("Failed to read value ({}): {}", this, error)); } + + // Paper start - option to read via codec without logging errors @@ -70,23 +70,23 @@ + // Copying was chosen over overloading the read methods as a boolean parameter to mark a method as quiet + // is not intuitive and would require even more overloads. + // Not a lot of diff in these methods is expected -+ public Optional readQuiet(String key, Codec codec) { -+ return this.readQuiet(key, codec, NbtOps.INSTANCE); ++ public Optional readQuiet(String name, Codec codec) { ++ return this.readQuiet(name, codec, NbtOps.INSTANCE); + } + -+ public Optional readQuiet(String key, Codec codec, DynamicOps ops) { -+ Tag tag = this.get(key); ++ public Optional readQuiet(String name, Codec codec, DynamicOps ops) { ++ Tag tag = this.get(name); + return tag == null + ? Optional.empty() + : codec.parse(ops, tag).resultOrPartial(); + } + -+ public Optional readQuiet(MapCodec mapCodec) { -+ return this.readQuiet(mapCodec, NbtOps.INSTANCE); ++ public Optional readQuiet(MapCodec codec) { ++ return this.readQuiet(codec, NbtOps.INSTANCE); + } + -+ public Optional readQuiet(MapCodec mapCodec, DynamicOps ops) { -+ return mapCodec.decode(ops, ops.getMap(this).getOrThrow()).resultOrPartial(); ++ public Optional readQuiet(MapCodec codec, DynamicOps ops) { ++ return codec.decode(ops, ops.getMap(this).getOrThrow()).resultOrPartial(); + } + // Paper end - option to read via codec without logging errors } diff --git a/paper-server/patches/sources/net/minecraft/nbt/IntArrayTag.java.patch b/paper-server/patches/sources/net/minecraft/nbt/IntArrayTag.java.patch index de1fb28f75f5..cb478c6240fd 100644 --- a/paper-server/patches/sources/net/minecraft/nbt/IntArrayTag.java.patch +++ b/paper-server/patches/sources/net/minecraft/nbt/IntArrayTag.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/nbt/IntArrayTag.java +++ b/net/minecraft/nbt/IntArrayTag.java @@ -23,6 +_,7 @@ - private static int[] readAccounted(DataInput input, NbtAccounter accounter) throws IOException { + private static int[] readAccounted(final DataInput input, final NbtAccounter accounter) throws IOException { accounter.accountBytes(24L); - int _int = input.readInt(); -+ com.google.common.base.Preconditions.checkArgument(_int < 1 << 24); // Spigot - accounter.accountBytes(4L, _int); - int[] ints = new int[_int]; + int length = input.readInt(); ++ com.google.common.base.Preconditions.checkArgument(length < 1 << 24); // Spigot + accounter.accountBytes(4L, length); + int[] data = new int[length]; diff --git a/paper-server/patches/sources/net/minecraft/nbt/NbtIo.java.patch b/paper-server/patches/sources/net/minecraft/nbt/NbtIo.java.patch index 5cd314e969fc..e94e7511736f 100644 --- a/paper-server/patches/sources/net/minecraft/nbt/NbtIo.java.patch +++ b/paper-server/patches/sources/net/minecraft/nbt/NbtIo.java.patch @@ -1,14 +1,16 @@ --- a/net/minecraft/nbt/NbtIo.java +++ b/net/minecraft/nbt/NbtIo.java -@@ -118,6 +_,11 @@ +@@ -117,7 +_,12 @@ + return read(input, NbtAccounter.unlimitedHeap()); } - public static CompoundTag read(DataInput input, NbtAccounter accounter) throws IOException { -+ // Spigot start +- public static CompoundTag read(final DataInput input, final NbtAccounter accounter) throws IOException { ++ // Spigot start ++ public static CompoundTag read(DataInput input, final NbtAccounter accounter) throws IOException { + if (input instanceof io.netty.buffer.ByteBufInputStream byteBufInputStream) { + input = new DataInputStream(new org.spigotmc.LimitStream(byteBufInputStream, accounter)); + } + // Spigot end - Tag unnamedTag = readUnnamedTag(input, accounter); - if (unnamedTag instanceof CompoundTag) { - return (CompoundTag)unnamedTag; + Tag tag = readUnnamedTag(input, accounter); + if (tag instanceof CompoundTag) { + return (CompoundTag)tag; diff --git a/paper-server/patches/sources/net/minecraft/nbt/SnbtGrammar.java.patch b/paper-server/patches/sources/net/minecraft/nbt/SnbtGrammar.java.patch index f32f5b684f24..83043f4aafb1 100644 --- a/paper-server/patches/sources/net/minecraft/nbt/SnbtGrammar.java.patch +++ b/paper-server/patches/sources/net/minecraft/nbt/SnbtGrammar.java.patch @@ -1,21 +1,23 @@ --- a/net/minecraft/nbt/SnbtGrammar.java +++ b/net/minecraft/nbt/SnbtGrammar.java -@@ -587,7 +_,7 @@ - Atom>> atom30 = Atom.of("map_entries"); - dictionary.put(atom30, Term.repeatedWithTrailingSeparator(namedRule3, atom30, StringReaderTerms.character(',')), scope -> scope.getOrThrow(atom30)); - Atom atom31 = Atom.of("map_literal"); -- dictionary.put(atom31, Term.sequence(StringReaderTerms.character('{'), dictionary.named(atom30), StringReaderTerms.character('}')), scope -> { -+ dictionary.put(atom31, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), dictionary.named(atom30), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { // Paper - track depth - List> list = scope.getOrThrow(atom30); - if (list.isEmpty()) { - return object2; -@@ -622,7 +_,9 @@ - atom35, +@@ -619,7 +_,7 @@ + mapEntries, Term.repeatedWithTrailingSeparator(mapEntryRule, mapEntries, StringReaderTerms.character(',')), scope -> scope.getOrThrow(mapEntries) + ); + Atom mapLiteral = Atom.of("map_literal"); +- rules.put(mapLiteral, Term.sequence(StringReaderTerms.character('{'), rules.named(mapEntries), StringReaderTerms.character('}')), scope -> { ++ rules.put(mapLiteral, Term.sequence(StringReaderTerms.character('{'), Scope.increaseDepth(), rules.named(mapEntries), Scope.decreaseDepth(), StringReaderTerms.character('}')), scope -> { // Paper - track depth + List> entries = scope.getOrThrow(mapEntries); + if (entries.isEmpty()) { + return emptyMapValue; +@@ -660,9 +_,11 @@ + listLiteral, Term.sequence( StringReaderTerms.character('['), + Scope.increaseDepth(), // Paper - track depth - Term.alternative(Term.sequence(dictionary.named(atom33), StringReaderTerms.character(';'), dictionary.named(atom34)), dictionary.named(atom32)), + Term.alternative( + Term.sequence(rules.named(arrayPrefix), StringReaderTerms.character(';'), rules.named(intArrayEntries)), rules.named(listEntries) + ), + Scope.decreaseDepth(), // Paper - track depth StringReaderTerms.character(']') ), - parseState -> { + state -> { diff --git a/paper-server/patches/sources/net/minecraft/network/Connection.java.patch b/paper-server/patches/sources/net/minecraft/network/Connection.java.patch index 9e277b37beeb..5dbd3bcf9813 100644 --- a/paper-server/patches/sources/net/minecraft/network/Connection.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/Connection.java.patch @@ -15,12 +15,12 @@ @@ -81,6 +_,43 @@ private boolean handlingFault; private volatile @Nullable DisconnectionDetails delayedDisconnect; - @Nullable BandwidthDebugMonitor bandwidthDebugMonitor; + private @Nullable BandwidthDebugMonitor bandwidthDebugMonitor; + public String hostname = ""; // CraftBukkit - add field + // Paper start - NetworkClient implementation + public int protocolVersion; + public java.net.InetSocketAddress virtualHost; -+ private static boolean enableExplicitFlush = Boolean.getBoolean("paper.explicit-flush"); // Paper - Disable explicit network manager flushing ++ private static boolean enableExplicitFlush = Boolean.getBoolean("paper.explicit-flush"); // Paper - Disable explicit connection flushing + // Paper end + // Paper start - add utility methods + public final net.minecraft.server.level.ServerPlayer getPlayer() { @@ -54,7 +54,7 @@ + public net.minecraft.server.level.@Nullable ServerPlayer savedPlayerForLegacyEvents; // Paper - playerloginevent & PlayerSpawnLocationEvent + public org.bukkit.event.player.PlayerResourcePackStatusEvent.@Nullable Status resourcePackStatus; // Paper - public Connection(PacketFlow receiving) { + public Connection(final PacketFlow receiving) { this.receiving = receiving; @@ -91,6 +_,7 @@ super.channelActive(ctx); @@ -64,56 +64,58 @@ if (this.delayedDisconnect != null) { this.disconnect(this.delayedDisconnect); } -@@ -103,14 +_,31 @@ +@@ -102,15 +_,32 @@ + } @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable exception) { -+ // Paper start - Handle large packets disconnecting client -+ if (exception instanceof io.netty.handler.codec.EncoderException && exception.getCause() instanceof PacketEncoder.PacketTooLargeException packetTooLargeException) { +- public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) { ++ // Paper start - Handle large packets disconnecting client ++ public void exceptionCaught(final ChannelHandlerContext ctx, Throwable cause) { ++ if (cause instanceof io.netty.handler.codec.EncoderException && cause.getCause() instanceof PacketEncoder.PacketTooLargeException packetTooLargeException) { + final Packet packet = packetTooLargeException.getPacket(); + if (packet.packetTooLarge(this)) { + ProtocolSwapHandler.handleOutboundTerminalPacket(ctx, packet); + return; + } else if (packet.isSkippable()) { -+ Connection.LOGGER.debug("Skipping packet due to errors", exception.getCause()); ++ Connection.LOGGER.debug("Skipping packet due to errors", cause.getCause()); + ProtocolSwapHandler.handleOutboundTerminalPacket(ctx, packet); + return; + } else { -+ exception = exception.getCause(); ++ cause = cause.getCause(); + } + } + // Paper end - Handle large packets disconnecting client - if (exception instanceof SkipPacketException) { - LOGGER.debug("Skipping packet due to errors", exception.getCause()); + if (cause instanceof SkipPacketException) { + LOGGER.debug("Skipping packet due to errors", cause.getCause()); } else { - boolean flag = !this.handlingFault; + boolean isFirstFault = !this.handlingFault; this.handlingFault = true; if (this.channel.isOpen()) { + net.minecraft.server.level.ServerPlayer player = this.getPlayer(); // Paper - Add API for quit reason - if (exception instanceof TimeoutException) { - LOGGER.debug("Timeout", exception); + if (cause instanceof TimeoutException) { + LOGGER.debug("Timeout", cause); + if (player != null) player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.TIMED_OUT; // Paper - Add API for quit reason this.disconnect(Component.translatable("disconnect.timeout")); } else { - Component component = Component.translatable("disconnect.genericReason", "Internal Exception: " + exception); + Component reason = Component.translatable("disconnect.genericReason", "Internal Exception: " + cause); @@ -122,9 +_,11 @@ - disconnectionDetails = new DisconnectionDetails(component); + details = new DisconnectionDetails(reason); } + if (player != null) player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.ERRONEOUS_STATE; // Paper - Add API for quit reason - if (flag) { - LOGGER.debug("Failed to sent packet", exception); + if (isFirstFault) { + LOGGER.debug("Failed to sent packet", cause); - if (this.getSending() == PacketFlow.CLIENTBOUND) { + boolean doesDisconnectExist = this.packetListener.protocol() != ConnectionProtocol.STATUS && this.packetListener.protocol() != ConnectionProtocol.HANDSHAKING; // Paper + if (this.getSending() == PacketFlow.CLIENTBOUND && doesDisconnectExist) { // Paper Packet packet = (Packet)(this.sendLoginDisconnect - ? new ClientboundLoginDisconnectPacket(component) - : new ClientboundDisconnectPacket(component)); + ? new ClientboundLoginDisconnectPacket(reason) + : new ClientboundDisconnectPacket(reason)); @@ -141,6 +_,7 @@ } } } -+ if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) io.papermc.paper.util.TraceUtil.printStackTrace(exception); // Spigot // Paper ++ if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) io.papermc.paper.util.TraceUtil.printStackTrace(cause); // Spigot // Paper } @Override @@ -152,7 +154,7 @@ + case DROP: + return; + case KICK: -+ String deobfedPacketName = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(check.getName()); ++ String deobfedPacketName = check.getName(); + + String playerName; + if (this.packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl impl) { @@ -178,7 +180,7 @@ } catch (RejectedExecutionException var6) { this.disconnect(Component.translatable("multiplayer.disconnect.server_shutdown")); } catch (ClassCastException var7) { -@@ -346,10 +_,30 @@ +@@ -344,10 +_,30 @@ } } @@ -193,7 +195,7 @@ + Connection.joinAttemptsThisTick = 0; + } + // Paper end - Buffer joins to world - if (this.packetListener instanceof TickablePacketListener tickablePacketListener) { + if (this.packetListener instanceof TickablePacketListener tickable) { + // Paper start - Buffer joins to world + if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener) + || loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING @@ -201,7 +203,7 @@ + // Paper start - detailed watchdog information + net.minecraft.network.PacketProcessor.packetProcessing.push(this.packetListener); + try { - tickablePacketListener.tick(); + tickable.tick(); + } finally { + net.minecraft.network.PacketProcessor.packetProcessing.pop(); + } // Paper end - detailed watchdog information @@ -209,31 +211,31 @@ } if (!this.isConnected() && !this.disconnectionHandled) { -@@ -357,7 +_,7 @@ +@@ -355,7 +_,7 @@ } if (this.channel != null) { - this.channel.flush(); -+ if (enableExplicitFlush) this.channel.eventLoop().execute(() -> this.channel.flush()); // Paper - Disable explicit network manager flushing; we don't need to explicit flush here, but allow opt in incase issues are found to a better version ++ if (enableExplicitFlush) this.channel.eventLoop().execute(() -> this.channel.flush()); // Paper - Disable explicit connection flushing; we don't need to explicit flush here, but allow opt in incase issues are found to a better version } if (this.tickCount++ % 20 == 0) { -@@ -393,12 +_,13 @@ +@@ -391,12 +_,13 @@ } - public void disconnect(DisconnectionDetails disconnectionDetails) { + public void disconnect(final DisconnectionDetails details) { + this.preparing = false; // Spigot if (this.channel == null) { - this.delayedDisconnect = disconnectionDetails; + this.delayedDisconnect = details; } if (this.isConnected()) { - this.channel.close().awaitUninterruptibly(); + this.channel.close(); // We can't wait as this may be called from an event loop. - this.disconnectionDetails = disconnectionDetails; + this.disconnectionDetails = details; } } -@@ -533,6 +_,13 @@ +@@ -543,6 +_,14 @@ } } @@ -244,28 +246,29 @@ + } + } + // Paper end - add proper async disconnect - public void setupCompression(int threshold, boolean validateDecompressed) { ++ + public void setupCompression(final int threshold, final boolean validateDecompressed) { if (threshold >= 0) { - if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder compressionDecoder) { -@@ -546,6 +_,7 @@ + if (this.channel.pipeline().get(HandlerNames.DECOMPRESS) instanceof CompressionDecoder compressionDecoder) { +@@ -556,6 +_,7 @@ } else { - this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(threshold)); + this.channel.pipeline().addAfter(HandlerNames.PREPENDER, HandlerNames.COMPRESS, new CompressionEncoder(threshold)); } + this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners } else { - if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) { - this.channel.pipeline().remove("decompress"); -@@ -554,6 +_,7 @@ - if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) { - this.channel.pipeline().remove("compress"); + if (this.channel.pipeline().get(HandlerNames.DECOMPRESS) instanceof CompressionDecoder) { + this.channel.pipeline().remove(HandlerNames.DECOMPRESS); +@@ -564,6 +_,7 @@ + if (this.channel.pipeline().get(HandlerNames.COMPRESS) instanceof CompressionEncoder) { + this.channel.pipeline().remove(HandlerNames.COMPRESS); } + this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_DISABLED); // Paper - Add Channel initialization listeners } } -@@ -571,6 +_,26 @@ +@@ -581,6 +_,26 @@ ); - packetListener1.onDisconnect(disconnectionDetails); + disconnectListener.onDisconnect(details); } + this.pendingActions.clear(); // Free up packet queue. + // Paper start - Add PlayerConnectionCloseEvent diff --git a/paper-server/patches/sources/net/minecraft/network/DisconnectionDetails.java.patch b/paper-server/patches/sources/net/minecraft/network/DisconnectionDetails.java.patch index 434d042dcdfd..468ba760fe0b 100644 --- a/paper-server/patches/sources/net/minecraft/network/DisconnectionDetails.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/DisconnectionDetails.java.patch @@ -12,6 +12,6 @@ + } +// Paper end - Configuration API: add support for enhanced disconnection causes + - public DisconnectionDetails(Component reason) { + public DisconnectionDetails(final Component reason) { this(reason, Optional.empty(), Optional.empty()); } diff --git a/paper-server/patches/sources/net/minecraft/network/FriendlyByteBuf.java.patch b/paper-server/patches/sources/net/minecraft/network/FriendlyByteBuf.java.patch index c4e2ef021142..d477f5aab15b 100644 --- a/paper-server/patches/sources/net/minecraft/network/FriendlyByteBuf.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/FriendlyByteBuf.java.patch @@ -16,7 +16,7 @@ + public byte codecDepth; + // Paper end - Track codec depth - public FriendlyByteBuf(ByteBuf source) { + public FriendlyByteBuf(final ByteBuf source) { + this.adventure$locale = PacketEncoder.ADVENTURE_LOCALE.get(); // Paper - track player's locale for server-side translations this.source = source; } @@ -24,22 +24,22 @@ @@ -106,8 +_,13 @@ } - public void writeJsonWithCodec(Codec codec, T value) { + public void writeJsonWithCodec(final Codec codec, final T value) { + // Paper start - Adventure; add max length parameter + this.writeJsonWithCodec(codec, value, MAX_STRING_LENGTH); + } + public void writeJsonWithCodec(Codec codec, T value, int maxLength) { + // Paper end - Adventure; add max length parameter - DataResult dataResult = codec.encodeStart(JsonOps.INSTANCE, value); -- this.writeUtf(GSON.toJson(dataResult.getOrThrow(exception -> new EncoderException("Failed to encode: " + exception + " " + value)))); -+ this.writeUtf(GSON.toJson(dataResult.getOrThrow(exception -> new EncoderException("Failed to encode: " + exception + " " + value))), maxLength); // Paper - Adventure; add max length parameter + DataResult result = codec.encodeStart(JsonOps.INSTANCE, value); +- this.writeUtf(GSON.toJson(result.getOrThrow(error -> new EncoderException("Failed to encode: " + error + " " + value)))); ++ this.writeUtf(GSON.toJson(result.getOrThrow(error -> new EncoderException("Failed to encode: " + error + " " + value))), maxLength); // Paper - Adventure; add max length parameter } - public static IntFunction limitValue(IntFunction function, int limit) { -@@ -552,7 +_,7 @@ + public static IntFunction limitValue(final IntFunction original, final int limit) { +@@ -530,7 +_,7 @@ try { - NbtIo.writeAnyTag(tag, new ByteBufOutputStream(buffer)); + NbtIo.writeAnyTag(tag, new ByteBufOutputStream(output)); - } catch (IOException var3) { + } catch (Exception var3) { // CraftBukkit - IOException -> Exception throw new EncoderException(var3); diff --git a/paper-server/patches/sources/net/minecraft/network/PacketEncoder.java.patch b/paper-server/patches/sources/net/minecraft/network/PacketEncoder.java.patch index 7318c99f0cd6..ad66db91b916 100644 --- a/paper-server/patches/sources/net/minecraft/network/PacketEncoder.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/PacketEncoder.java.patch @@ -6,25 +6,25 @@ + static final ThreadLocal ADVENTURE_LOCALE = ThreadLocal.withInitial(() -> null); // Paper - adventure; set player's locale @Override - protected void encode(ChannelHandlerContext channelHandlerContext, Packet packet, ByteBuf byteBuf) throws Exception { - PacketType> packetType = packet.type(); + protected void encode(final ChannelHandlerContext ctx, final Packet packet, final ByteBuf output) throws Exception { + PacketType> packetId = packet.type(); try { -+ ADVENTURE_LOCALE.set(channelHandlerContext.channel().attr(io.papermc.paper.adventure.PaperAdventure.LOCALE_ATTRIBUTE).get()); // Paper - adventure; set player's locale - this.protocolInfo.codec().encode(byteBuf, packet); - int i = byteBuf.readableBytes(); ++ ADVENTURE_LOCALE.set(ctx.channel().attr(io.papermc.paper.adventure.PaperAdventure.LOCALE_ATTRIBUTE).get()); // Paper - adventure; set player's locale + this.protocolInfo.codec().encode(output, packet); + int writtenBytes = output.readableBytes(); if (LOGGER.isDebugEnabled()) { -@@ -39,7 +_,33 @@ +@@ -44,7 +_,33 @@ throw var9; } finally { + // Paper start - Handle large packets disconnecting client -+ int packetLength = byteBuf.readableBytes(); ++ int packetLength = output.readableBytes(); + if (packetLength > MAX_PACKET_SIZE || (packetLength > MAX_FINAL_PACKET_SIZE && packet.hasLargePacketFallback())) { + throw new PacketTooLargeException(packet, packetLength); + } + // Paper end - Handle large packets disconnecting client - ProtocolSwapHandler.handleOutboundTerminalPacket(channelHandlerContext, packet); + ProtocolSwapHandler.handleOutboundTerminalPacket(ctx, packet); } } + diff --git a/paper-server/patches/sources/net/minecraft/network/PacketProcessor.java.patch b/paper-server/patches/sources/net/minecraft/network/PacketProcessor.java.patch index 79eaf236b146..9d468f157102 100644 --- a/paper-server/patches/sources/net/minecraft/network/PacketProcessor.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/PacketProcessor.java.patch @@ -24,7 +24,7 @@ return Thread.currentThread() == this.runningThread; } - public void scheduleIfPossible(T listener, Packet packet) { + public void scheduleIfPossible(final T listener, final Packet packet) { if (this.closed) { - throw new RejectedExecutionException("Server already shutting down"); + throw new io.papermc.paper.util.ServerStopRejectedExecutionException("Server already shutting down"); // Paper - do not prematurely disconnect players on stop @@ -67,7 +67,7 @@ + } + // Paper end - detailed watchdog information + - record ListenerAndPacket(T listener, Packet packet) { + private record ListenerAndPacket(T listener, Packet packet) { public void handle() { + packetProcessing.push(this.listener); // Paper - detailed watchdog information + try { // Paper - detailed watchdog information diff --git a/paper-server/patches/sources/net/minecraft/network/VarInt.java.patch b/paper-server/patches/sources/net/minecraft/network/VarInt.java.patch index 8604de1e1d78..49a1b46f5a9f 100644 --- a/paper-server/patches/sources/net/minecraft/network/VarInt.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/VarInt.java.patch @@ -3,41 +3,41 @@ @@ -9,6 +_,18 @@ private static final int DATA_BITS_PER_BYTE = 7; - public static int getByteSize(int data) { -+ // Paper start - Optimize VarInts -+ return VARINT_EXACT_BYTE_LENGTHS[Integer.numberOfLeadingZeros(data)]; + public static int getByteSize(final int value) { ++ // Paper start - Optimize VarInts ++ return VARINT_EXACT_BYTE_LENGTHS[Integer.numberOfLeadingZeros(value)]; + } + private static final int[] VARINT_EXACT_BYTE_LENGTHS = new int[33]; + static { + for (int i = 0; i <= 32; ++i) { -+ VARINT_EXACT_BYTE_LENGTHS[i] = (int) Math.ceil((31d - (i - 1)) / 7d); ++ VARINT_EXACT_BYTE_LENGTHS[i] = (int) Math.ceil((31.0 - (i - 1)) / 7.0); + } + VARINT_EXACT_BYTE_LENGTHS[32] = 1; // Special case for the number 0. + } -+ public static int getByteSizeOld(int data) { -+ // Paper end - Optimize VarInts ++ public static int getByteSizeSlow(final int value) { ++ // Paper end - Optimize VarInts for (int i = 1; i < MAX_VARINT_SIZE; i++) { - if ((data & -1 << i * 7) == 0) { + if ((value & -1 << i * DATA_BITS_PER_BYTE) == 0) { return i; @@ -39,6 +_,21 @@ } - public static ByteBuf write(ByteBuf buffer, int value) { -+ // Paper start - Optimize VarInts + public static ByteBuf write(final ByteBuf output, int value) { ++ // Paper start - Optimize VarInts + // Peel the one and two byte count cases explicitly as they are the most common VarInt sizes + // that the proxy will write, to improve inlining. + if ((value & (0xFFFFFFFF << 7)) == 0) { -+ buffer.writeByte(value); ++ output.writeByte(value); + } else if ((value & (0xFFFFFFFF << 14)) == 0) { -+ int w = (value & 0x7F | 0x80) << 8 | (value >>> 7); -+ buffer.writeShort(w); ++ int s = (value & 0x7F | 0x80) << 8 | (value >>> 7); ++ output.writeShort(s); + } else { -+ writeOld(buffer, value); ++ writeSlow(output, value); + } -+ return buffer; ++ return output; + } -+ public static ByteBuf writeOld(ByteBuf buffer, int value) { -+ // Paper end - Optimize VarInts ++ public static ByteBuf writeSlow(final ByteBuf output, int value) { ++ // Paper end - Optimize VarInts while ((value & -128) != 0) { - buffer.writeByte(value & 127 | 128); - value >>>= 7; + output.writeByte(value & DATA_BITS_MASK | CONTINUATION_BIT_MASK); + value >>>= DATA_BITS_PER_BYTE; diff --git a/paper-server/patches/sources/net/minecraft/network/Varint21FrameDecoder.java.patch b/paper-server/patches/sources/net/minecraft/network/Varint21FrameDecoder.java.patch index 46628723821a..e948af5ebd9c 100644 --- a/paper-server/patches/sources/net/minecraft/network/Varint21FrameDecoder.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/Varint21FrameDecoder.java.patch @@ -3,7 +3,7 @@ @@ -40,6 +_,12 @@ @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { + protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List out) { + // Paper start - Perf: Optimize exception handling; if channel is not active just discard the packet + if (!ctx.channel().isActive()) { + in.skipBytes(in.readableBytes()); diff --git a/paper-server/patches/sources/net/minecraft/network/chat/ChatDecorator.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/ChatDecorator.java.patch index b340bb0c3041..92c2aeb9a395 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/ChatDecorator.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/ChatDecorator.java.patch @@ -4,16 +4,16 @@ @FunctionalInterface public interface ChatDecorator { -- ChatDecorator PLAIN = (player, message) -> message; +- ChatDecorator PLAIN = (player, plain) -> plain; - -- Component decorate(@Nullable ServerPlayer player, Component message); -+ ChatDecorator PLAIN = (player, message) -> java.util.concurrent.CompletableFuture.completedFuture(message); // Paper - adventure; support async chat decoration events +- Component decorate(@Nullable ServerPlayer player, Component plain); ++ ChatDecorator PLAIN = (player, plain) -> java.util.concurrent.CompletableFuture.completedFuture(plain); // Paper - adventure; support async chat decoration events + + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - adventure; support chat decoration events (callers should use the overload with CommandSourceStack) -+ java.util.concurrent.CompletableFuture decorate(@Nullable ServerPlayer player, Component message); // Paper - adventure; support async chat decoration events ++ java.util.concurrent.CompletableFuture decorate(@Nullable ServerPlayer player, Component plain); // Paper - adventure; support async chat decoration events + + // Paper start - adventure; support async chat decoration events -+ default java.util.concurrent.CompletableFuture decorate(@Nullable ServerPlayer sender, net.minecraft.commands.@Nullable CommandSourceStack commandSourceStack, Component message) { ++ default java.util.concurrent.CompletableFuture decorate(@Nullable ServerPlayer sender, net.minecraft.commands.@Nullable CommandSourceStack commandSourceStack, Component plain) { + throw new UnsupportedOperationException("Must override this implementation"); + } + // Paper end - adventure; support async chat decoration events diff --git a/paper-server/patches/sources/net/minecraft/network/chat/Component.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/Component.java.patch index 6159dbd6c797..6d624f287eec 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/Component.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/Component.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/network/chat/Component.java +++ b/net/minecraft/network/chat/Component.java -@@ -25,7 +_,19 @@ +@@ -27,7 +_,19 @@ import net.minecraft.world.level.ChunkPos; import org.jspecify.annotations.Nullable; diff --git a/paper-server/patches/sources/net/minecraft/network/chat/ComponentSerialization.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/ComponentSerialization.java.patch index 815a89c7d6b4..6e42e1ffbd8f 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/ComponentSerialization.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/ComponentSerialization.java.patch @@ -34,8 +34,8 @@ public static final StreamCodec> TRUSTED_OPTIONAL_STREAM_CODEC = TRUSTED_STREAM_CODEC.apply( ByteBufCodecs::optional ); -@@ -91,7 +_,25 @@ - return ExtraCodecs.orCompressed(mapCodec2, mapCodec1); +@@ -93,7 +_,25 @@ + return ExtraCodecs.orCompressed(contentsCodec, discriminatorCodec); } + // Paper start - adventure; create separate codec for each locale @@ -50,23 +50,23 @@ + } + // Paper end - adventure; create separate codec for each locale + - private static Codec createCodec(Codec codec) { + private static Codec createCodec(final Codec topSerializer) { + // Paper start - adventure; create separate codec for each locale -+ return createCodec(codec, null); ++ return createCodec(topSerializer, null); + } + -+ private static Codec createCodec(Codec codec, @javax.annotation.Nullable java.util.Locale locale) { ++ private static Codec createCodec(Codec topSerializer, java.util.@org.jspecify.annotations.Nullable Locale locale) { + // Paper end - adventure; create separate codec for each locale - ExtraCodecs.LateBoundIdMapper> lateBoundIdMapper = new ExtraCodecs.LateBoundIdMapper<>(); - bootstrap(lateBoundIdMapper); - MapCodec mapCodec = createLegacyComponentMatcher(lateBoundIdMapper, ComponentContents::codec, "type"); -@@ -103,6 +_,34 @@ + ExtraCodecs.LateBoundIdMapper> contentTypes = new ExtraCodecs.LateBoundIdMapper<>(); + bootstrap(contentTypes); + MapCodec compressedContentsCodec = createLegacyComponentMatcher(contentTypes, ComponentContents::codec, "type"); +@@ -105,6 +_,34 @@ ) - .apply(instance, MutableComponent::new) + .apply(i, MutableComponent::new) ); + // Paper start - adventure; create separate codec for each locale -+ final Codec origCodec = codec1; -+ codec1 = new Codec<>() { ++ final Codec origCodec = fullCodec; ++ fullCodec = new Codec<>() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return origCodec.decode(ops, input); @@ -92,6 +92,6 @@ + } + }; + // Paper end - adventure; create separate codec for each locale - return Codec.either(Codec.either(Codec.STRING, ExtraCodecs.nonEmptyList(codec.listOf())), codec1) + return Codec.either(Codec.either(Codec.STRING, ExtraCodecs.nonEmptyList(topSerializer.listOf())), fullCodec) .xmap( - either -> either.map(either1 -> either1.map(Component::literal, ComponentSerialization::createFromList), component -> (Component)component), + specialOrComponent -> specialOrComponent.map( diff --git a/paper-server/patches/sources/net/minecraft/network/chat/ComponentUtils.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/ComponentUtils.java.patch index 525b0643e6f4..22053517b89e 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/ComponentUtils.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/ComponentUtils.java.patch @@ -1,20 +1,22 @@ --- a/net/minecraft/network/chat/ComponentUtils.java +++ b/net/minecraft/network/chat/ComponentUtils.java -@@ -49,16 +_,45 @@ +@@ -47,6 +_,7 @@ } } + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - validate separators - right now this method is only used for separator evaluation. Error on build if this changes to re-evaluate. - public static Optional updateForEntity( - @Nullable CommandSourceStack source, Optional optionalComponent, @Nullable Entity entity, int recursionDepth - ) throws CommandSyntaxException { - return optionalComponent.isPresent() ? Optional.of(updateForEntity(source, optionalComponent.get(), entity, recursionDepth)) : Optional.empty(); + public static Optional resolve(final ResolutionContext context, final Optional component, final int recursionDepth) throws CommandSyntaxException { + return component.isPresent() ? Optional.of(resolve(context, component.get(), recursionDepth)) : Optional.empty(); + } +@@ -55,13 +_,40 @@ + return resolve(context, component, 0); } +- public static MutableComponent resolve(final ResolutionContext context, final Component component, final int recursionDepth) throws CommandSyntaxException { + // Paper start - validate separator -+ public static Optional updateSeparatorForEntity(@Nullable CommandSourceStack source, Optional text, @Nullable Entity sender, int depth) throws CommandSyntaxException { ++ public static Optional resolveSeparator(final ResolutionContext context, final Optional text, final int recursionDepth) throws CommandSyntaxException { + if (text.isEmpty() || !isValidSelector(text.get())) return Optional.empty(); -+ return Optional.of(updateForEntity(source, text.get(), sender, depth)); ++ return Optional.of(resolve(context, text.get(), recursionDepth)); + } + + public static boolean isValidSelector(final Component component) { @@ -33,16 +35,18 @@ + } + // Paper end - validate separator + - public static MutableComponent updateForEntity(@Nullable CommandSourceStack source, Component component, @Nullable Entity entity, int recursionDepth) throws CommandSyntaxException { - if (recursionDepth > 100) { - return component.copy(); ++ public static MutableComponent resolve(final ResolutionContext context, Component component, final int recursionDepth) throws CommandSyntaxException { // Paper - adventure; pass actual vanilla component + if (recursionDepth > context.depthLimit()) { + return switch (context.depthLimitBehavior()) { + case DISCARD_REMAINING -> CommonComponents.ELLIPSIS.copy(); + case STOP_PROCESSING_AND_COPY_REMAINING -> component.copy(); + }; } else { + // Paper start - adventure; pass actual vanilla component + if (component instanceof io.papermc.paper.adventure.AdventureComponent adventureComponent) { + component = adventureComponent.deepConverted(); + } + // Paper end - adventure; pass actual vanilla component -+ - MutableComponent mutableComponent = component.getContents().resolve(source, entity, recursionDepth + 1); + MutableComponent result = component.getContents().resolve(context, recursionDepth + 1); - for (Component component1 : component.getSiblings()) { + for (Component sibling : component.getSiblings()) { diff --git a/paper-server/patches/sources/net/minecraft/network/chat/MutableComponent.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/MutableComponent.java.patch index 677316ab3cbe..71e7db277d02 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/MutableComponent.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/MutableComponent.java.patch @@ -3,12 +3,12 @@ @@ -97,6 +_,11 @@ @Override - public boolean equals(Object other) { + public boolean equals(final Object o) { + // Paper start - make AdventureComponent equivalent -+ if (other instanceof io.papermc.paper.adventure.AdventureComponent adventureComponent) { -+ other = adventureComponent.deepConverted(); ++ if (o instanceof io.papermc.paper.adventure.AdventureComponent adventureComponent) { ++ return this.equals(adventureComponent.deepConverted()); + } + // Paper end - make AdventureComponent equivalent - return this == other - || other instanceof MutableComponent mutableComponent - && this.contents.equals(mutableComponent.contents) + return this == o + || o instanceof MutableComponent that + && this.contents.equals(that.contents) diff --git a/paper-server/patches/sources/net/minecraft/network/chat/OutgoingChatMessage.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/OutgoingChatMessage.java.patch index 929fe273908a..1c01a7e3c5f9 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/OutgoingChatMessage.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/OutgoingChatMessage.java.patch @@ -2,28 +2,28 @@ +++ b/net/minecraft/network/chat/OutgoingChatMessage.java @@ -7,6 +_,12 @@ - void sendToPlayer(ServerPlayer player, boolean filtered, ChatType.Bound boundChatType); + void sendToPlayer(ServerPlayer player, boolean filtered, ChatType.Bound chatType); + // Paper start -+ default void sendToPlayer(ServerPlayer player, boolean filtered, ChatType.Bound boundChatType, @javax.annotation.Nullable Component unsigned) { -+ this.sendToPlayer(player, filtered, boundChatType); ++ default void sendToPlayer(final ServerPlayer player, final boolean filtered, final ChatType.Bound chatType, final @org.jspecify.annotations.Nullable Component unsigned) { ++ this.sendToPlayer(player, filtered, chatType); + } + // Paper end + - static OutgoingChatMessage create(PlayerChatMessage message) { + static OutgoingChatMessage create(final PlayerChatMessage message) { return (OutgoingChatMessage)(message.isSystem() ? new OutgoingChatMessage.Disguised(message.decoratedContent()) @@ -16,7 +_,13 @@ public record Disguised(@Override Component content) implements OutgoingChatMessage { @Override - public void sendToPlayer(ServerPlayer player, boolean filtered, ChatType.Bound boundChatType) { -- player.connection.sendDisguisedChatMessage(this.content, boundChatType); + public void sendToPlayer(final ServerPlayer player, final boolean filtered, final ChatType.Bound chatType) { +- player.connection.sendDisguisedChatMessage(this.content, chatType); + // Paper start -+ this.sendToPlayer(player, filtered, boundChatType, null); ++ this.sendToPlayer(player, filtered, chatType, null); + } + @Override -+ public void sendToPlayer(ServerPlayer player, boolean filtered, ChatType.Bound boundChatType, @javax.annotation.Nullable Component unsigned) { -+ player.connection.sendDisguisedChatMessage(unsigned != null ? unsigned : this.content, boundChatType); ++ public void sendToPlayer(final ServerPlayer player, final boolean filtered, final ChatType.Bound chatType, final @org.jspecify.annotations.Nullable Component unsigned) { ++ player.connection.sendDisguisedChatMessage(unsigned != null ? unsigned : this.content, chatType); + // Paper end } } @@ -31,15 +31,15 @@ @@ -28,7 +_,14 @@ @Override - public void sendToPlayer(ServerPlayer player, boolean filtered, ChatType.Bound boundChatType) { + public void sendToPlayer(final ServerPlayer player, final boolean filtered, final ChatType.Bound chatType) { + // Paper start -+ this.sendToPlayer(player, filtered, boundChatType, null); ++ this.sendToPlayer(player, filtered, chatType, null); + } + @Override -+ public void sendToPlayer(ServerPlayer player, boolean filtered, ChatType.Bound boundChatType, @javax.annotation.Nullable Component unsigned) { ++ public void sendToPlayer(final ServerPlayer player, final boolean filtered, final ChatType.Bound chatType, final @org.jspecify.annotations.Nullable Component unsigned) { + // Paper end - PlayerChatMessage playerChatMessage = this.message.filter(filtered); -+ playerChatMessage = unsigned != null ? playerChatMessage.withUnsignedContent(unsigned) : playerChatMessage; // Paper - if (!playerChatMessage.isFullyFiltered()) { - player.connection.sendPlayerChatMessage(playerChatMessage, boundChatType); + PlayerChatMessage filteredMessage = this.message.filter(filtered); ++ filteredMessage = unsigned != null ? filteredMessage.withUnsignedContent(unsigned) : filteredMessage; // Paper + if (!filteredMessage.isFullyFiltered()) { + player.connection.sendPlayerChatMessage(filteredMessage, chatType); } diff --git a/paper-server/patches/sources/net/minecraft/network/chat/PlayerChatMessage.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/PlayerChatMessage.java.patch index f6d43730715a..55f2dc457ec1 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/PlayerChatMessage.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/PlayerChatMessage.java.patch @@ -42,21 +42,21 @@ + // Paper end - adventure; support signed messages + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group( + i -> i.group( SignedMessageLink.CODEC.fieldOf("link").forGetter(PlayerChatMessage::link), -@@ -48,7 +_,14 @@ +@@ -50,7 +_,14 @@ } - public PlayerChatMessage withUnsignedContent(Component message) { -- Component component = !message.equals(Component.literal(this.signedContent())) ? message : null; + public PlayerChatMessage withUnsignedContent(final Component content) { +- Component unsignedContent = !content.equals(Component.literal(this.signedContent())) ? content : null; + // Paper start - adventure -+ final Component component; -+ if (message instanceof io.papermc.paper.adventure.AdventureComponent advComponent) { -+ component = this.signedContent().equals(io.papermc.paper.adventure.AdventureCodecs.tryCollapseToString(advComponent.adventure$component())) ? null : message; ++ final Component unsignedContent; ++ if (content instanceof io.papermc.paper.adventure.AdventureComponent advComponent) { ++ unsignedContent = this.signedContent().equals(io.papermc.paper.adventure.AdventureCodecs.tryCollapseToString(advComponent.adventure$component())) ? null : content; + } else { -+ component = !message.equals(Component.literal(this.signedContent())) ? message : null; ++ unsignedContent = !content.equals(Component.literal(this.signedContent())) ? content : null; + } + // Paper end - adventure - return new PlayerChatMessage(this.link, this.signature, this.signedBody, component, this.filterMask); + return new PlayerChatMessage(this.link, this.signature, this.signedBody, unsignedContent, this.filterMask); } diff --git a/paper-server/patches/sources/net/minecraft/network/chat/SignedMessageChain.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/SignedMessageChain.java.patch index 72366c94cd99..bd43bcbeab6e 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/SignedMessageChain.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/SignedMessageChain.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/network/chat/SignedMessageChain.java +++ b/net/minecraft/network/chat/SignedMessageChain.java -@@ -39,14 +_,14 @@ +@@ -44,14 +_,14 @@ if (signature == null) { throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.MISSING_PROFILE_KEY); - } else if (publicKey.data().hasExpired()) { + } else if (profilePublicKey.data().hasExpired()) { - throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.EXPIRED_PROFILE_KEY); -+ throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.EXPIRED_PROFILE_KEY, org.bukkit.event.player.PlayerKickEvent.Cause.EXPIRED_PROFILE_PUBLIC_KEY); // Paper - kick event causes ++ throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.EXPIRED_PROFILE_KEY, org.bukkit.event.player.PlayerKickEvent.Cause.EXPIRED_PROFILE_PUBLIC_KEY); // Paper - kick event causes } else { - SignedMessageLink signedMessageLink = SignedMessageChain.this.nextLink; - if (signedMessageLink == null) { + SignedMessageLink link = SignedMessageChain.this.nextLink; + if (link == null) { throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.CHAIN_BROKEN); } else if (body.timeStamp().isBefore(SignedMessageChain.this.lastTimeStamp)) { this.setChainBroken(); @@ -16,12 +16,12 @@ + throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.OUT_OF_ORDER_CHAT, org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event causes } else { SignedMessageChain.this.lastTimeStamp = body.timeStamp(); - PlayerChatMessage playerChatMessage = new PlayerChatMessage(signedMessageLink, signature, body, null, FilterMask.PASS_THROUGH); -@@ -79,8 +_,15 @@ - static final Component INVALID_SIGNATURE = Component.translatable("chat.disabled.invalid_signature"); - static final Component OUT_OF_ORDER_CHAT = Component.translatable("chat.disabled.out_of_order_chat"); + PlayerChatMessage unpacked = new PlayerChatMessage(link, signature, body, null, FilterMask.PASS_THROUGH); +@@ -84,8 +_,15 @@ + private static final Component INVALID_SIGNATURE = Component.translatable("chat.disabled.invalid_signature"); + private static final Component OUT_OF_ORDER_CHAT = Component.translatable("chat.disabled.out_of_order_chat"); -- public DecodeException(Component component) { +- public DecodeException(final Component component) { + // Paper start + public final org.bukkit.event.player.PlayerKickEvent.Cause kickCause; + public DecodeException(Component component, org.bukkit.event.player.PlayerKickEvent.Cause event) { @@ -29,7 +29,7 @@ + this.kickCause = event; + } + // Paper end -+ public DecodeException(Component component) { ++ public DecodeException(final Component component) { + this(component, org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); // Paper } } diff --git a/paper-server/patches/sources/net/minecraft/network/chat/TextColor.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/TextColor.java.patch index 40288d32b2e2..aa4e4213f486 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/TextColor.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/TextColor.java.patch @@ -4,25 +4,25 @@ public static final Codec CODEC = Codec.STRING.comapFlatMap(TextColor::parseColor, TextColor::serialize); private static final Map LEGACY_FORMAT_TO_COLOR = Stream.of(ChatFormatting.values()) .filter(ChatFormatting::isColor) -- .collect(ImmutableMap.toImmutableMap(Function.identity(), formatting -> new TextColor(formatting.getColor(), formatting.getName()))); -+ .collect(ImmutableMap.toImmutableMap(Function.identity(), formatting -> new TextColor(formatting.getColor(), formatting.getName(), formatting))); // CraftBukkit +- .collect(ImmutableMap.toImmutableMap(Function.identity(), f -> new TextColor(f.getColor(), f.getName()))); ++ .collect(ImmutableMap.toImmutableMap(Function.identity(), f -> new TextColor(f.getColor(), f.getName(), f))); // CraftBukkit private static final Map NAMED_COLORS = LEGACY_FORMAT_TO_COLOR.values() .stream() - .collect(ImmutableMap.toImmutableMap(textColor -> textColor.name, Function.identity())); + .collect(ImmutableMap.toImmutableMap(e -> e.name, Function.identity())); private final int value; public final @Nullable String name; + // CraftBukkit start + @Nullable + public final ChatFormatting format; -- private TextColor(int value, String name) { -+ private TextColor(int value, String name, ChatFormatting format) { +- private TextColor(final int value, final String name) { ++ private TextColor(final int value, final String name, ChatFormatting format) { this.value = value & 16777215; this.name = name; + this.format = format; } - private TextColor(int value) { + private TextColor(final int value) { this.value = value & 16777215; this.name = null; + this.format = null; diff --git a/paper-server/patches/sources/net/minecraft/network/chat/contents/NbtContents.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/contents/NbtContents.java.patch index ca177c9cdd7f..e18f4e57cae7 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/contents/NbtContents.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/contents/NbtContents.java.patch @@ -1,20 +1,11 @@ --- a/net/minecraft/network/chat/contents/NbtContents.java +++ b/net/minecraft/network/chat/contents/NbtContents.java -@@ -118,7 +_,7 @@ +@@ -70,7 +_,7 @@ + } + }); + Component resolvedSeparator = DataFixUtils.orElse( +- ComponentUtils.resolve(context, this.separator, recursionDepth), ComponentUtils.DEFAULT_NO_STYLE_SEPARATOR ++ ComponentUtils.resolveSeparator(context, this.separator, recursionDepth), ComponentUtils.DEFAULT_NO_STYLE_SEPARATOR // Paper - validate separator + ); if (this.interpreting) { RegistryOps registryOps = source.registryAccess().createSerializationContext(NbtOps.INSTANCE); - Component component = DataFixUtils.orElse( -- ComponentUtils.updateForEntity(source, this.separator, entity, recursionDepth), ComponentUtils.DEFAULT_NO_STYLE_SEPARATOR -+ ComponentUtils.updateSeparatorForEntity(source, this.separator, entity, recursionDepth), ComponentUtils.DEFAULT_NO_STYLE_SEPARATOR // Paper - validate separator - ); - return stream.flatMap(tag -> { - try { -@@ -131,7 +_,7 @@ - }).reduce((mutableComponent, component1) -> mutableComponent.append(component).append(component1)).orElseGet(Component::empty); - } else { - Stream stream1 = stream.map(NbtContents::asString); -- return ComponentUtils.updateForEntity(source, this.separator, entity, recursionDepth) -+ return ComponentUtils.updateSeparatorForEntity(source, this.separator, entity, recursionDepth) // Paper - validate separator - .map( - mutableComponent -> stream1.map(Component::literal) - .reduce((mutableComponent1, otherMutableComponent) -> mutableComponent1.append(mutableComponent).append(otherMutableComponent)) diff --git a/paper-server/patches/sources/net/minecraft/network/chat/contents/ObjectContents.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/contents/ObjectContents.java.patch new file mode 100644 index 000000000000..dbe317a5b0e4 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/network/chat/contents/ObjectContents.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/network/chat/contents/ObjectContents.java ++++ b/net/minecraft/network/chat/contents/ObjectContents.java +@@ -32,7 +_,7 @@ + + @Override + public MutableComponent resolve(final ResolutionContext context, final int recursionDepth) throws CommandSyntaxException { +- Optional fallback = ComponentUtils.resolve(context, this.fallback, recursionDepth); ++ Optional fallback = ComponentUtils.resolveSeparator(context, this.fallback, recursionDepth); // Paper - validate separator + ObjectInfo validatedContents = context.validate(this.contents); + return validatedContents == null + ? fallback.orElseGet(() -> Component.literal(this.contents.defaultFallback())) diff --git a/paper-server/patches/sources/net/minecraft/network/chat/contents/SelectorContents.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/contents/SelectorContents.java.patch index 596e6f42a82a..d7fd00d307ee 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/contents/SelectorContents.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/contents/SelectorContents.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/network/chat/contents/SelectorContents.java +++ b/net/minecraft/network/chat/contents/SelectorContents.java -@@ -35,7 +_,7 @@ +@@ -37,7 +_,7 @@ if (source == null) { return Component.empty(); } else { -- Optional optional = ComponentUtils.updateForEntity(source, this.separator, entity, recursionDepth); -+ Optional optional = ComponentUtils.updateSeparatorForEntity(source, this.separator, entity, recursionDepth); // Paper - validate separator - return ComponentUtils.formatList(this.selector.resolved().findEntities(source), optional, Entity::getDisplayName); +- Optional resolvedSeparator = ComponentUtils.resolve(context, this.separator, recursionDepth); ++ Optional resolvedSeparator = ComponentUtils.resolveSeparator(context, this.separator, recursionDepth); // Paper - validate separator + return ComponentUtils.formatList(this.selector.compiled().findEntities(source), resolvedSeparator, Entity::getDisplayName); } } diff --git a/paper-server/patches/sources/net/minecraft/network/chat/contents/TranslatableContents.java.patch b/paper-server/patches/sources/net/minecraft/network/chat/contents/TranslatableContents.java.patch index bdbfe1fcb029..d93ab456914e 100644 --- a/paper-server/patches/sources/net/minecraft/network/chat/contents/TranslatableContents.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/chat/contents/TranslatableContents.java.patch @@ -1,23 +1,23 @@ --- a/net/minecraft/network/chat/contents/TranslatableContents.java +++ b/net/minecraft/network/chat/contents/TranslatableContents.java -@@ -178,6 +_,16 @@ +@@ -177,6 +_,16 @@ @Override - public Optional visit(FormattedText.ContentConsumer contentConsumer) { + public Optional visit(final FormattedText.ContentConsumer output) { + // Paper start - Count visited parts + try { -+ return this.visit(new TranslatableContentConsumer<>(contentConsumer)); ++ return this.visit(new TranslatableContentConsumer<>(output)); + } catch (IllegalArgumentException ignored) { -+ return contentConsumer.accept("..."); ++ return output.accept("..."); + } + } + -+ private Optional visit(TranslatableContentConsumer contentConsumer) { ++ private Optional visit(TranslatableContentConsumer output) { + // Paper end - Count visited parts this.decompose(); - for (FormattedText formattedText : this.decomposedParts) { -@@ -189,6 +_,27 @@ + for (FormattedText part : this.decomposedParts) { +@@ -188,6 +_,27 @@ return Optional.empty(); } @@ -44,4 +44,4 @@ + // Paper end - Count visited parts @Override - public MutableComponent resolve(@Nullable CommandSourceStack source, @Nullable Entity entity, int recursionDepth) throws CommandSyntaxException { + public MutableComponent resolve(final ResolutionContext context, final int recursionDepth) throws CommandSyntaxException { diff --git a/paper-server/patches/sources/net/minecraft/network/codec/ByteBufCodecs.java.patch b/paper-server/patches/sources/net/minecraft/network/codec/ByteBufCodecs.java.patch index 6d8633da13b2..277fd8af4d1b 100644 --- a/paper-server/patches/sources/net/minecraft/network/codec/ByteBufCodecs.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/codec/ByteBufCodecs.java.patch @@ -46,6 +46,6 @@ + } + // Paper end - Track codec depth + - static StreamCodec> optional(final StreamCodec codec) { + static StreamCodec> optional(final StreamCodec original) { return new StreamCodec>() { @Override diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/Packet.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/Packet.java.patch index 221a5587d372..003903558f20 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/Packet.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/Packet.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/network/protocol/Packet.java @@ -11,6 +_,19 @@ - void handle(T handler); + void handle(T listener); + // Paper start + default boolean hasLargePacketFallback() { diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java.patch index 10835a1af317..4bf443aa3d21 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java.patch @@ -4,7 +4,7 @@ private static final int MAX_PAYLOAD_SIZE = 32767; public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec( id -> DiscardedPayload.codec(id, 32767), -- Util.make(Lists.newArrayList(new CustomPacketPayload.TypeAndCodec<>(BrandPayload.TYPE, BrandPayload.STREAM_CODEC)), list -> {}) +- Util.make(Lists.newArrayList(new CustomPacketPayload.TypeAndCodec<>(BrandPayload.TYPE, BrandPayload.STREAM_CODEC)), types -> {}) + java.util.Collections.emptyList() // CraftBukkit - treat all packets the same ) .map(ServerboundCustomPayloadPacket::new, ServerboundCustomPayloadPacket::payload); diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/common/custom/DiscardedPayload.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/common/custom/DiscardedPayload.java.patch index 59f5e69f5568..29719bd22644 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/common/custom/DiscardedPayload.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/common/custom/DiscardedPayload.java.patch @@ -6,21 +6,21 @@ -public record DiscardedPayload(Identifier id) implements CustomPacketPayload { +public record DiscardedPayload(Identifier id, byte[] data) implements CustomPacketPayload { // Paper - store data - public static StreamCodec codec(Identifier id, int maxSize) { -- return CustomPacketPayload.codec((value, output) -> {}, buffer -> { -+ return CustomPacketPayload.codec((value, output) -> { + public static StreamCodec codec(final Identifier id, final int maxPayloadSize) { +- return CustomPacketPayload.codec((payload, buf) -> {}, buf -> { ++ return CustomPacketPayload.codec((payload, buf) -> { + // Paper start + // Always write data -+ output.writeBytes(value.data); -+ }, buffer -> { - int i = buffer.readableBytes(); - if (i >= 0 && i <= maxSize) { -- buffer.skipBytes(i); ++ buf.writeBytes(payload.data); ++ }, buf -> { + int length = buf.readableBytes(); + if (length >= 0 && length <= maxPayloadSize) { +- buf.skipBytes(length); - return new DiscardedPayload(id); -+ final byte[] data = new byte[i]; -+ buffer.readBytes(data); ++ final byte[] data = new byte[length]; ++ buf.readBytes(data); + return new DiscardedPayload(id, data); + // Paper end } else { - throw new IllegalArgumentException("Payload may not be larger than " + maxSize + " bytes"); + throw new IllegalArgumentException("Payload may not be larger than " + maxPayloadSize + " bytes"); } diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundBlockEntityDataPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundBlockEntityDataPacket.java.patch index 92492a6835d3..72a2a17526a3 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundBlockEntityDataPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundBlockEntityDataPacket.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/network/protocol/game/ClientboundBlockEntityDataPacket.java +++ b/net/minecraft/network/protocol/game/ClientboundBlockEntityDataPacket.java -@@ -29,7 +_,7 @@ - - public static ClientboundBlockEntityDataPacket create(BlockEntity blockEntity, BiFunction dataGetter) { +@@ -31,7 +_,7 @@ + final BlockEntity blockEntity, final BiFunction updateTagSaver + ) { RegistryAccess registryAccess = blockEntity.getLevel().registryAccess(); -- return new ClientboundBlockEntityDataPacket(blockEntity.getBlockPos(), blockEntity.getType(), dataGetter.apply(blockEntity, registryAccess)); -+ return new ClientboundBlockEntityDataPacket(blockEntity.getBlockPos(), blockEntity.getType(), blockEntity.sanitizeSentNbt(dataGetter.apply(blockEntity, registryAccess))); // Paper - Sanitize sent data +- return new ClientboundBlockEntityDataPacket(blockEntity.getBlockPos(), blockEntity.getType(), updateTagSaver.apply(blockEntity, registryAccess)); ++ return new ClientboundBlockEntityDataPacket(blockEntity.getBlockPos(), blockEntity.getType(), blockEntity.sanitizeSentNbt(updateTagSaver.apply(blockEntity, registryAccess))); // Paper - Sanitize sent data } - public static ClientboundBlockEntityDataPacket create(BlockEntity blockEntity) { + public static ClientboundBlockEntityDataPacket create(final BlockEntity blockEntity) { diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java.patch index 03d18f0dc288..85c6a94d8330 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java +++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java -@@ -50,7 +_,7 @@ - public ClientboundLevelChunkPacketData(RegistryFriendlyByteBuf buffer, int x, int z) { - this.heightmaps = HEIGHTMAPS_STREAM_CODEC.decode(buffer); - int varInt = buffer.readVarInt(); -- if (varInt > 2097152) { -+ if (varInt > 2097152) { // Paper - diff on change - if this changes, update PacketEncoder - throw new RuntimeException("Chunk Packet trying to allocate too much memory on read."); - } else { - this.buffer = new byte[varInt]; +@@ -28,7 +_,7 @@ + private static final StreamCodec> HEIGHTMAPS_STREAM_CODEC = ByteBufCodecs.map( + size -> new EnumMap<>(Heightmap.Types.class), Heightmap.Types.STREAM_CODEC, ByteBufCodecs.LONG_ARRAY + ); +- private static final int TWO_MEGABYTES = 2097152; ++ private static final int TWO_MEGABYTES = 2097152; // Paper - diff on change - if this changes, update PacketEncoder + private final Map heightmaps; + private final byte[] buffer; + private final List blockEntitiesData; @@ -154,6 +_,7 @@ - CompoundTag updateTag = blockEntity.getUpdateTag(blockEntity.getLevel().registryAccess()); - BlockPos blockPos = blockEntity.getBlockPos(); - int i = SectionPos.sectionRelative(blockPos.getX()) << 4 | SectionPos.sectionRelative(blockPos.getZ()); -+ blockEntity.sanitizeSentNbt(updateTag); // Paper - Sanitize sent data - return new ClientboundLevelChunkPacketData.BlockEntityInfo(i, blockPos.getY(), blockEntity.getType(), updateTag.isEmpty() ? null : updateTag); + CompoundTag tag = blockEntity.getUpdateTag(blockEntity.getLevel().registryAccess()); + BlockPos pos = blockEntity.getBlockPos(); + int xz = SectionPos.sectionRelative(pos.getX()) << 4 | SectionPos.sectionRelative(pos.getZ()); ++ blockEntity.sanitizeSentNbt(tag); // Paper - Sanitize sent data + return new ClientboundLevelChunkPacketData.BlockEntityInfo(xz, pos.getY(), blockEntity.getType(), tag.isEmpty() ? null : tag); } } diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java.patch index edcd2a2d5de7..e1c2d8ad9c30 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java.patch @@ -10,5 +10,5 @@ + } + // Paper end - Anti-Xray - public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight) { - ChunkPos pos = chunk.getPos(); + public ClientboundLevelChunkWithLightPacket( + final LevelChunk levelChunk, diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundPlayerInfoUpdatePacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundPlayerInfoUpdatePacket.java.patch index bed45c045c4a..20051a27a9e1 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundPlayerInfoUpdatePacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundPlayerInfoUpdatePacket.java.patch @@ -16,11 +16,11 @@ + } + // Paper end - Add Listing API for Player - public static ClientboundPlayerInfoUpdatePacket createPlayerInitializing(Collection players) { - EnumSet set = EnumSet.of( + public static ClientboundPlayerInfoUpdatePacket createPlayerInitializing(final Collection players) { + EnumSet actions = EnumSet.of( @@ -53,6 +_,46 @@ ); - return new ClientboundPlayerInfoUpdatePacket(set, players); + return new ClientboundPlayerInfoUpdatePacket(actions, players); } + // Paper start - Add Listing API for Player + public static ClientboundPlayerInfoUpdatePacket createPlayerInitializing(Collection players, ServerPlayer forPlayer) { @@ -63,33 +63,33 @@ + } + // Paper end - Add Listing API for Player - private ClientboundPlayerInfoUpdatePacket(RegistryFriendlyByteBuf buffer) { - this.actions = buffer.readEnumSet(ClientboundPlayerInfoUpdatePacket.Action.class); + private ClientboundPlayerInfoUpdatePacket(final RegistryFriendlyByteBuf input) { + this.actions = input.readEnumSet(ClientboundPlayerInfoUpdatePacket.Action.class); @@ -117,7 +_,15 @@ }), INITIALIZE_CHAT( - (entryBuilder, buffer) -> entryBuilder.chatSession = buffer.readNullable(RemoteChatSession.Data::read), -- (buffer, entry) -> buffer.writeNullable(entry.chatSession, RemoteChatSession.Data::write) + (entry, input) -> entry.chatSession = input.readNullable(RemoteChatSession.Data::read), +- (output, entry) -> output.writeNullable(entry.chatSession, RemoteChatSession.Data::write) + // Paper start - Prevent causing expired keys from impacting new joins -+ (buffer, entry) -> { ++ (output, entry) -> { + RemoteChatSession.Data chatSession = entry.chatSession; + if (chatSession != null && chatSession.profilePublicKey().hasExpired()) { + chatSession = null; + } -+ buffer.writeNullable(chatSession, RemoteChatSession.Data::write); ++ output.writeNullable(chatSession, RemoteChatSession.Data::write); + } + // Paper end - Prevent causing expired keys from impacting new joins ), - UPDATE_GAME_MODE( - (entryBuilder, buffer) -> entryBuilder.gameMode = GameType.byId(buffer.readVarInt()), -@@ -161,10 +_,15 @@ + UPDATE_GAME_MODE((entry, input) -> entry.gameMode = GameType.byId(input.readVarInt()), (output, entry) -> output.writeVarInt(entry.gameMode().getId())), + UPDATE_LISTED((entry, input) -> entry.listed = input.readBoolean(), (output, entry) -> output.writeBoolean(entry.listed())), +@@ -158,10 +_,15 @@ RemoteChatSession.@Nullable Data chatSession ) { - Entry(ServerPlayer player) { + private Entry(final ServerPlayer player) { + // Paper start - Add Listing API for Player + this(player, true); + } -+ Entry(ServerPlayer player, boolean listed) { ++ private Entry(ServerPlayer player, boolean listed) { this( + // Paper end - Add Listing API for Player player.getUUID(), @@ -99,7 +99,7 @@ player.connection.latency(), player.gameMode(), player.getTabListDisplayName(), -@@ -173,6 +_,11 @@ +@@ -170,6 +_,11 @@ Optionull.map(player.getChatSession(), RemoteChatSession::asData) ); } @@ -110,4 +110,4 @@ + // Paper end - Add Listing API for Player } - static class EntryBuilder { + private static class EntryBuilder { diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java.patch index f73670c52257..0acf8c0f9cec 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java.patch @@ -1,11 +1,13 @@ --- a/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java +++ b/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java -@@ -30,10 +_,25 @@ +@@ -30,12 +_,25 @@ - for (short s : positions) { - this.positions[i] = s; -- this.states[i] = section.getBlockState(SectionPos.sectionRelativeX(s), SectionPos.sectionRelativeY(s), SectionPos.sectionRelativeZ(s)); -+ this.states[i] = (section != null) ? section.getBlockState(SectionPos.sectionRelativeX(s), SectionPos.sectionRelativeY(s), SectionPos.sectionRelativeZ(s)) : net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(); // CraftBukkit - SPIGOT-6076, Mojang bug when empty chunk section notified + for (short packedPos : changes) { + this.positions[i] = packedPos; +- this.states[i] = section.getBlockState( +- SectionPos.sectionRelativeX(packedPos), SectionPos.sectionRelativeY(packedPos), SectionPos.sectionRelativeZ(packedPos) +- ); ++ this.states[i] = (section != null) ? section.getBlockState(SectionPos.sectionRelativeX(packedPos), SectionPos.sectionRelativeY(packedPos), SectionPos.sectionRelativeZ(packedPos)) : net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(); // CraftBukkit - SPIGOT-6076, Mojang bug when empty chunk section notified i++; } } @@ -25,5 +27,5 @@ + // Paper end - Multi Block Change API + - private ClientboundSectionBlocksUpdatePacket(FriendlyByteBuf buffer) { - this.sectionPos = SectionPos.STREAM_CODEC.decode(buffer); + private ClientboundSectionBlocksUpdatePacket(final FriendlyByteBuf input) { + this.sectionPos = SectionPos.STREAM_CODEC.decode(input); diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java.patch index f8353c254ea7..7c6aa304916c 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java.patch @@ -3,12 +3,12 @@ @@ -19,9 +_,11 @@ } - private static void pack(List> dataValues, RegistryFriendlyByteBuf buffer) { + private static void pack(final List> items, final RegistryFriendlyByteBuf output) { + try (io.papermc.paper.util.sanitizer.ItemObfuscationSession ignored = io.papermc.paper.util.sanitizer.ItemObfuscationSession.start(io.papermc.paper.configuration.GlobalConfiguration.get().anticheat.obfuscation.items.binding.level)) { // Paper - data sanitization - for (SynchedEntityData.DataValue dataValue : dataValues) { - dataValue.write(buffer); + for (SynchedEntityData.DataValue item : items) { + item.write(output); } + } // Paper - data sanitization - buffer.writeByte(255); + output.writeByte(255); } diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java.patch index 1a8efdc47835..6f77ce833e58 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java.patch @@ -3,7 +3,7 @@ @@ -19,6 +_,13 @@ private final List> slots; - public ClientboundSetEquipmentPacket(int entity, List> slots) { + public ClientboundSetEquipmentPacket(final int entity, final List> slots) { + // Paper start - data sanitization + this(entity, slots, false); + } @@ -15,16 +15,16 @@ this.slots = slots; } @@ -40,6 +_,7 @@ - buffer.writeVarInt(this.entity); + output.writeVarInt(this.entity); int size = this.slots.size(); + try (final io.papermc.paper.util.sanitizer.ItemObfuscationSession ignored = io.papermc.paper.util.sanitizer.ItemObfuscationSession.start(this.sanitize ? io.papermc.paper.configuration.GlobalConfiguration.get().anticheat.obfuscation.items.binding.level : io.papermc.paper.util.sanitizer.ItemObfuscationSession.ObfuscationLevel.NONE)) { // Paper - data sanitization for (int i = 0; i < size; i++) { - Pair pair = this.slots.get(i); - EquipmentSlot equipmentSlot = pair.getFirst(); + Pair e = this.slots.get(i); + EquipmentSlot slotType = e.getFirst(); @@ -48,6 +_,7 @@ - buffer.writeByte(flag ? ordinal | -128 : ordinal); - ItemStack.OPTIONAL_STREAM_CODEC.encode(buffer, pair.getSecond()); + output.writeByte(shouldContinue ? slotId | -128 : slotId); + ItemStack.OPTIONAL_STREAM_CODEC.encode(output, e.getSecond()); } + } // Paper - data sanitization } diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java.patch index dfa5bbbfebde..7e1cfcafd6e5 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java +++ b/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java -@@ -57,6 +_,12 @@ +@@ -61,6 +_,12 @@ ); } @@ -10,15 +10,15 @@ + } + // Paper end - Multiple Entries with Scoreboards + - private ClientboundSetPlayerTeamPacket(RegistryFriendlyByteBuf buffer) { - this.name = buffer.readUtf(); - this.method = buffer.readByte(); -@@ -197,7 +_,7 @@ - ComponentSerialization.TRUSTED_STREAM_CODEC.encode(buffer, this.displayName); - buffer.writeByte(this.options); - Team.Visibility.STREAM_CODEC.encode(buffer, this.nametagVisibility); -- Team.CollisionRule.STREAM_CODEC.encode(buffer, this.collisionRule); -+ Team.CollisionRule.STREAM_CODEC.encode(buffer, !io.papermc.paper.configuration.GlobalConfiguration.get().collisions.enablePlayerCollisions ? PlayerTeam.CollisionRule.NEVER : this.collisionRule); // Paper - Configurable player collision - buffer.writeEnum(this.color); - ComponentSerialization.TRUSTED_STREAM_CODEC.encode(buffer, this.playerPrefix); - ComponentSerialization.TRUSTED_STREAM_CODEC.encode(buffer, this.playerSuffix); + private ClientboundSetPlayerTeamPacket(final RegistryFriendlyByteBuf input) { + this.name = input.readUtf(); + this.method = input.readByte(); +@@ -201,7 +_,7 @@ + ComponentSerialization.TRUSTED_STREAM_CODEC.encode(output, this.displayName); + output.writeByte(this.options); + Team.Visibility.STREAM_CODEC.encode(output, this.nametagVisibility); +- Team.CollisionRule.STREAM_CODEC.encode(output, this.collisionRule); ++ Team.CollisionRule.STREAM_CODEC.encode(output, !io.papermc.paper.configuration.GlobalConfiguration.get().collisions.enablePlayerCollisions ? PlayerTeam.CollisionRule.NEVER : this.collisionRule); // Paper - Configurable player collision + output.writeEnum(this.color); + ComponentSerialization.TRUSTED_STREAM_CODEC.encode(output, this.playerPrefix); + ComponentSerialization.TRUSTED_STREAM_CODEC.encode(output, this.playerSuffix); diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundChatCommandPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundChatCommandPacket.java.patch index b8a62f6e22f1..2e09a7eb3614 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundChatCommandPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundChatCommandPacket.java.patch @@ -9,9 +9,9 @@ ServerboundChatCommandPacket::write, ServerboundChatCommandPacket::new ); - private ServerboundChatCommandPacket(FriendlyByteBuf buffer) { -- this(buffer.readUtf()); -+ this(buffer.readUtf(MAX_CHAT_PACKET_INPUT_SIZE)); // Paper - limit chat command inputs + private ServerboundChatCommandPacket(final FriendlyByteBuf input) { +- this(input.readUtf()); ++ this(input.readUtf(MAX_CHAT_PACKET_INPUT_SIZE)); // Paper - limit chat command inputs } - private void write(FriendlyByteBuf buffer) { + private void write(final FriendlyByteBuf output) { diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundChatCommandSignedPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundChatCommandSignedPacket.java.patch index 0378ad997adf..f033ecd4998b 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundChatCommandSignedPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundChatCommandSignedPacket.java.patch @@ -3,9 +3,9 @@ @@ -16,7 +_,7 @@ ); - private ServerboundChatCommandSignedPacket(FriendlyByteBuf buffer) { -- this(buffer.readUtf(), buffer.readInstant(), buffer.readLong(), new ArgumentSignatures(buffer), new LastSeenMessages.Update(buffer)); -+ this(buffer.readUtf(net.minecraft.network.protocol.game.ServerboundChatCommandPacket.MAX_CHAT_PACKET_INPUT_SIZE), buffer.readInstant(), buffer.readLong(), new ArgumentSignatures(buffer), new LastSeenMessages.Update(buffer)); // Paper - limit chat command inputs + private ServerboundChatCommandSignedPacket(final FriendlyByteBuf input) { +- this(input.readUtf(), input.readInstant(), input.readLong(), new ArgumentSignatures(input), new LastSeenMessages.Update(input)); ++ this(input.readUtf(net.minecraft.network.protocol.game.ServerboundChatCommandPacket.MAX_CHAT_PACKET_INPUT_SIZE), input.readInstant(), input.readLong(), new ArgumentSignatures(input), new LastSeenMessages.Update(input)); // Paper - limit chat command inputs } - private void write(FriendlyByteBuf buffer) { + private void write(final FriendlyByteBuf output) { diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java.patch index 3161af31644d..a4cebb786e6a 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java @@ -19,7 +_,7 @@ - private ServerboundCommandSuggestionPacket(FriendlyByteBuf buffer) { - this.id = buffer.readVarInt(); -- this.command = buffer.readUtf(32500); -+ this.command = buffer.readUtf(2048); // Paper + private ServerboundCommandSuggestionPacket(final FriendlyByteBuf input) { + this.id = input.readVarInt(); +- this.command = input.readUtf(32500); ++ this.command = input.readUtf(2048); // Paper } - private void write(FriendlyByteBuf buffer) { + private void write(final FriendlyByteBuf output) { diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundInteractPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundInteractPacket.java.patch deleted file mode 100644 index 8d441064cbc2..000000000000 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundInteractPacket.java.patch +++ /dev/null @@ -1,18 +0,0 @@ ---- a/net/minecraft/network/protocol/game/ServerboundInteractPacket.java -+++ b/net/minecraft/network/protocol/game/ServerboundInteractPacket.java -@@ -152,6 +_,15 @@ - buffer.writeEnum(this.hand); - } - } -+ // Paper start - PlayerUseUnknownEntityEvent -+ public int getEntityId() { -+ return this.entityId; -+ } -+ -+ public boolean isAttack() { -+ return this.action.getType() == ActionType.ATTACK; -+ } -+ // Paper end - PlayerUseUnknownEntityEvent - - static class InteractionAtLocationAction implements ServerboundInteractPacket.Action { - private final InteractionHand hand; diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundUseItemOnPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundUseItemOnPacket.java.patch index 73bc58e48b7d..bf89e2fdd266 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundUseItemOnPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundUseItemOnPacket.java.patch @@ -6,13 +6,13 @@ private final int sequence; + public long timestamp; // Spigot - public ServerboundUseItemOnPacket(InteractionHand hand, BlockHitResult blockHit, int sequence) { + public ServerboundUseItemOnPacket(final InteractionHand hand, final BlockHitResult blockHit, final int sequence) { this.hand = hand; @@ -22,6 +_,7 @@ } - private ServerboundUseItemOnPacket(FriendlyByteBuf buffer) { + private ServerboundUseItemOnPacket(final FriendlyByteBuf input) { + this.timestamp = System.currentTimeMillis(); // Spigot - this.hand = buffer.readEnum(InteractionHand.class); - this.blockHit = buffer.readBlockHitResult(); - this.sequence = buffer.readVarInt(); + this.hand = input.readEnum(InteractionHand.class); + this.blockHit = input.readBlockHitResult(); + this.sequence = input.readVarInt(); diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundUseItemPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundUseItemPacket.java.patch index b4e853fe8272..1a9005b6f99a 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundUseItemPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/ServerboundUseItemPacket.java.patch @@ -6,13 +6,13 @@ private final float xRot; + public long timestamp; // Spigot - public ServerboundUseItemPacket(InteractionHand hand, int sequence, float yRot, float xRot) { + public ServerboundUseItemPacket(final InteractionHand hand, final int sequence, final float yRot, final float xRot) { this.hand = hand; @@ -23,6 +_,7 @@ } - private ServerboundUseItemPacket(FriendlyByteBuf buffer) { + private ServerboundUseItemPacket(final FriendlyByteBuf input) { + this.timestamp = System.currentTimeMillis(); // Spigot - this.hand = buffer.readEnum(InteractionHand.class); - this.sequence = buffer.readVarInt(); - this.yRot = buffer.readFloat(); + this.hand = input.readEnum(InteractionHand.class); + this.sequence = input.readVarInt(); + this.yRot = input.readFloat(); diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/game/VecDeltaCodec.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/game/VecDeltaCodec.java.patch index fff6b8efc429..ea746bc5758b 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/game/VecDeltaCodec.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/game/VecDeltaCodec.java.patch @@ -8,4 +8,4 @@ + public Vec3 base = Vec3.ZERO; // Paper @VisibleForTesting - static long encode(double value) { + static long encode(final double input) { diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/handshake/ClientIntentionPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/handshake/ClientIntentionPacket.java.patch index 752d456ac240..200a4d0c8260 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/handshake/ClientIntentionPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/handshake/ClientIntentionPacket.java.patch @@ -3,9 +3,9 @@ @@ -20,7 +_,7 @@ } - private ClientIntentionPacket(FriendlyByteBuf buffer) { -- this(buffer.readVarInt(), buffer.readUtf(255), buffer.readUnsignedShort(), ClientIntent.byId(buffer.readVarInt())); -+ this(buffer.readVarInt(), buffer.readUtf(Short.MAX_VALUE), buffer.readUnsignedShort(), ClientIntent.byId(buffer.readVarInt())); // Spigot - increase max hostName length + private ClientIntentionPacket(final FriendlyByteBuf input) { +- this(input.readVarInt(), input.readUtf(255), input.readUnsignedShort(), ClientIntent.byId(input.readVarInt())); ++ this(input.readVarInt(), input.readUtf(Short.MAX_VALUE), input.readUnsignedShort(), ClientIntent.byId(input.readVarInt())); // Spigot - increase max hostName length } - private void write(FriendlyByteBuf buffer) { + private void write(final FriendlyByteBuf output) { diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/login/ClientboundCustomQueryPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/login/ClientboundCustomQueryPacket.java.patch index d8ce01b3e11d..fb51d23533d6 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/login/ClientboundCustomQueryPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/login/ClientboundCustomQueryPacket.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/network/protocol/login/ClientboundCustomQueryPacket.java +++ b/net/minecraft/network/protocol/login/ClientboundCustomQueryPacket.java @@ -47,4 +_,13 @@ - public void handle(ClientLoginPacketListener handler) { - handler.handleCustomQuery(this); + public void handle(final ClientLoginPacketListener listener) { + listener.handleCustomQuery(this); } + + // Paper start - MC Utils - default query payloads diff --git a/paper-server/patches/sources/net/minecraft/network/protocol/login/ServerboundCustomQueryAnswerPacket.java.patch b/paper-server/patches/sources/net/minecraft/network/protocol/login/ServerboundCustomQueryAnswerPacket.java.patch index 40b8fdbc7893..77a76de19a0e 100644 --- a/paper-server/patches/sources/net/minecraft/network/protocol/login/ServerboundCustomQueryAnswerPacket.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/protocol/login/ServerboundCustomQueryAnswerPacket.java.patch @@ -3,10 +3,10 @@ @@ -20,7 +_,17 @@ } - private static CustomQueryAnswerPayload readPayload(int transactionId, FriendlyByteBuf buffer) { -- return readUnknownPayload(buffer); + private static CustomQueryAnswerPayload readPayload(final int transactionId, final FriendlyByteBuf input) { +- return readUnknownPayload(input); + // Paper start - MC Utils - default query payloads -+ FriendlyByteBuf buf = buffer.readNullable((buf2) -> { ++ FriendlyByteBuf buf = input.readNullable((buf2) -> { + int readableBytes = buf2.readableBytes(); + if (readableBytes >= 0 && readableBytes <= MAX_PAYLOAD_SIZE) { + return new FriendlyByteBuf(buf2.readBytes(readableBytes)); @@ -14,14 +14,14 @@ + throw new IllegalArgumentException("Payload may not be larger than " + MAX_PAYLOAD_SIZE + " bytes"); + } + }); -+ return buf == null ? null : new net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket.QueryAnswerPayload(buf); ++ return buf == null ? null : new QueryAnswerPayload(buf); + // Paper end - MC Utils - default query payloads } - private static CustomQueryAnswerPayload readUnknownPayload(FriendlyByteBuf buffer) { + private static CustomQueryAnswerPayload readUnknownPayload(final FriendlyByteBuf input) { @@ -47,4 +_,19 @@ - public void handle(ServerLoginPacketListener handler) { - handler.handleCustomQueryPacket(this); + public void handle(final ServerLoginPacketListener listener) { + listener.handleCustomQueryPacket(this); } + + // Paper start - MC Utils - default query payloads diff --git a/paper-server/patches/sources/net/minecraft/network/syncher/EntityDataSerializers.java.patch b/paper-server/patches/sources/net/minecraft/network/syncher/EntityDataSerializers.java.patch index 24afdbddedb8..178e2bd1e79a 100644 --- a/paper-server/patches/sources/net/minecraft/network/syncher/EntityDataSerializers.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/syncher/EntityDataSerializers.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/network/syncher/EntityDataSerializers.java +++ b/net/minecraft/network/syncher/EntityDataSerializers.java -@@ -55,10 +_,27 @@ +@@ -59,10 +_,27 @@ public static final EntityDataSerializer> OPTIONAL_COMPONENT = EntityDataSerializer.forValueType( ComponentSerialization.TRUSTED_OPTIONAL_STREAM_CODEC ); diff --git a/paper-server/patches/sources/net/minecraft/network/syncher/SynchedEntityData.java.patch b/paper-server/patches/sources/net/minecraft/network/syncher/SynchedEntityData.java.patch index d94f74eb7226..33c19095a735 100644 --- a/paper-server/patches/sources/net/minecraft/network/syncher/SynchedEntityData.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/syncher/SynchedEntityData.java.patch @@ -5,8 +5,8 @@ } + // CraftBukkit start - add method from above -+ public void markDirty(final EntityDataAccessor entityDataAccessor) { -+ this.getItem(entityDataAccessor).setDirty(true); ++ public void markDirty(final EntityDataAccessor accessor) { ++ this.getItem(accessor).setDirty(true); + this.isDirty = true; + } + // CraftBukkit end @@ -23,14 +23,14 @@ + // We need to pack all as we cannot rely on "non default values" or "dirty" ones. + // Because these values can possibly be desynced on the client. + public List> packAll() { -+ final List> list = new ArrayList<>(this.itemsById.length); -+ for (final DataItem dataItem : this.itemsById) { -+ list.add(dataItem.value()); ++ final List> values = new ArrayList<>(this.itemsById.length); ++ for (final DataItem item : this.itemsById) { ++ values.add(item.value()); + } + -+ return list; ++ return values; + } + // Paper end public static class DataItem { - final EntityDataAccessor accessor; + private final EntityDataAccessor accessor; diff --git a/paper-server/patches/sources/net/minecraft/resources/Identifier.java.patch b/paper-server/patches/sources/net/minecraft/resources/Identifier.java.patch index 23ea05ccf452..592a39a552a9 100644 --- a/paper-server/patches/sources/net/minecraft/resources/Identifier.java.patch +++ b/paper-server/patches/sources/net/minecraft/resources/Identifier.java.patch @@ -1,39 +1,39 @@ --- a/net/minecraft/resources/Identifier.java +++ b/net/minecraft/resources/Identifier.java -@@ -20,6 +_,7 @@ - public static final char NAMESPACE_SEPARATOR = ':'; +@@ -22,6 +_,7 @@ public static final String DEFAULT_NAMESPACE = "minecraft"; public static final String REALMS_NAMESPACE = "realms"; + public static final String ALLOWED_NAMESPACE_CHARACTERS = "[a-z0-9_.-]"; + public static final String PAPER_NAMESPACE = "paper"; // Paper private final String namespace; private final String path; -@@ -28,6 +_,13 @@ +@@ -30,6 +_,13 @@ assert isValidPath(path); + // Paper start - Validate Identifier + // Check for the max network string length (capped at Short.MAX_VALUE) as well as the max bytes of a StringTag (length written as an unsigned short) -+ final String resourceLocation = namespace + ":" + path; -+ if (resourceLocation.length() > Short.MAX_VALUE || io.netty.buffer.ByteBufUtil.utf8MaxBytes(resourceLocation) > 2 * Short.MAX_VALUE + 1) { -+ throw new IdentifierException("Resource location too long: " + resourceLocation); ++ final String id = namespace + ":" + path; ++ if (id.length() > Short.MAX_VALUE || io.netty.buffer.ByteBufUtil.utf8MaxBytes(id) > 2 * Short.MAX_VALUE + 1) { ++ throw new IdentifierException("Identifier too long: " + id); + } + // Paper end - Validate Identifier this.namespace = namespace; this.path = path; } -@@ -238,7 +_,7 @@ +@@ -242,7 +_,7 @@ - private static String assertValidNamespace(String namespace, String path) { + private static String assertValidNamespace(final String namespace, final String path) { if (!isValidNamespace(namespace)) { -- throw new IdentifierException("Non [a-z0-9_.-] character in namespace of location: " + namespace + ":" + path); -+ throw new IdentifierException("Non [a-z0-9_.-] character in namespace of location: " + org.apache.commons.lang3.StringUtils.normalizeSpace(namespace) + ":" + org.apache.commons.lang3.StringUtils.normalizeSpace(path)); // Paper - Sanitize Identifier error logging +- throw new IdentifierException("Non [a-z0-9_.-] character in namespace of identifier: " + namespace + ":" + path); ++ throw new IdentifierException("Non [a-z0-9_.-] character in namespace of identifier: " + org.apache.commons.lang3.StringUtils.normalizeSpace(namespace) + ":" + org.apache.commons.lang3.StringUtils.normalizeSpace(path)); // Paper - Sanitize Identifier error logging } else { return namespace; } -@@ -263,7 +_,7 @@ +@@ -258,7 +_,7 @@ - private static String assertValidPath(String namespace, String path) { + private static String assertValidPath(final String namespace, final String path) { if (!isValidPath(path)) { - throw new IdentifierException("Non [a-z0-9/._-] character in path of location: " + namespace + ":" + path); + throw new IdentifierException("Non [a-z0-9/._-] character in path of location: " + namespace + ":" + org.apache.commons.lang3.StringUtils.normalizeSpace(path)); // Paper - Sanitize Identifier error logging diff --git a/paper-server/patches/sources/net/minecraft/resources/NetworkRegistryLoadTask.java.patch b/paper-server/patches/sources/net/minecraft/resources/NetworkRegistryLoadTask.java.patch new file mode 100644 index 000000000000..2f7996f97cc3 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/resources/NetworkRegistryLoadTask.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/resources/NetworkRegistryLoadTask.java ++++ b/net/minecraft/resources/NetworkRegistryLoadTask.java +@@ -79,7 +_,7 @@ + } + + return Util.sequence(elements).thenAcceptAsync(pendingRegistrations -> { +- this.registerElements(pendingRegistrations.stream()); ++ this.registerElements(pendingRegistrations.stream(), null); // Paper - this code is only used on the client + Map, List>> pendingTags = TagLoader.loadTagsFromNetwork(registryEntries.tags(), this.readOnlyRegistry()); + this.registerTags(pendingTags); + }, executor); diff --git a/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch b/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch index 5776d719e9a2..91fa2b5938bb 100644 --- a/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch +++ b/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/resources/RegistryDataLoader.java +++ b/net/minecraft/resources/RegistryDataLoader.java -@@ -212,11 +_,21 @@ - final Map>, RegistryOps.RegistryInfo> map = new HashMap<>(); - registryLookups.forEach(registryLookup -> map.put(registryLookup.key(), createInfoForContextRegistry((HolderLookup.RegistryLookup)registryLookup))); - loaders.forEach(loader -> map.put(loader.registry.key(), createInfoForNewRegistry(loader.registry))); +@@ -243,11 +_,21 @@ + final Map>, RegistryOps.RegistryInfo> result = new HashMap<>(); + contextRegistries.forEach(e -> result.put(e.key(), createInfoForContextRegistry((HolderLookup.RegistryLookup)e))); + newRegistriesAndLoaders.forEach(e -> result.put(e.registryKey(), e.createRegistryInfo())); + // this creates a HolderLookup.Provider to be used exclusively to obtain real instances of values to pre-populate builders + // for the Registry Modification API. This concatenates the context registry lookups and the empty registries about to be filled. -+ final HolderLookup.Provider providerForBuilders = HolderLookup.Provider.create(java.util.stream.Stream.concat(registryLookups.stream(), loaders.stream().map(Loader::registry))); // Paper - add method to get the value for pre-filling builders in the reg mod API ++ final HolderLookup.Provider providerForBuilders = HolderLookup.Provider.create(java.util.stream.Stream.concat(contextRegistries.stream(), newRegistriesAndLoaders.stream().map(t -> t.registry))); // Paper - add method to get the value for pre-filling builders in the reg mod API return new RegistryOps.RegistryInfoLookup() { @Override - public Optional> lookup(ResourceKey> registryKey) { - return Optional.ofNullable((RegistryOps.RegistryInfo)map.get(registryKey)); + public Optional> lookup(final ResourceKey> key) { + return Optional.ofNullable((RegistryOps.RegistryInfo)result.get(key)); } + + // Paper start - add method to get the value for pre-filling builders in the reg mod API @@ -22,73 +22,3 @@ }; } -@@ -280,13 +_,14 @@ - RegistryOps ops, - ResourceKey resourceKey, - Resource resource, -- RegistrationInfo registrationInfo -+ RegistrationInfo registrationInfo, -+ io.papermc.paper.registry.data.util.Conversions conversions // Paper - pass conversions - ) throws IOException { - try (Reader reader = resource.openAsReader()) { - JsonElement jsonElement = StrictJsonParser.parse(reader); - DataResult dataResult = codec.parse(ops, jsonElement); - E orThrow = dataResult.getOrThrow(); -- registry.register(resourceKey, orThrow, registrationInfo); -+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(registry, resourceKey, orThrow, registrationInfo, conversions); // Paper - register with listeners - } - } - -@@ -300,6 +_,7 @@ - FileToIdConverter fileToIdConverter = FileToIdConverter.registry(registry.key()); - RegistryOps registryOps = RegistryOps.create(JsonOps.INSTANCE, registryInfoLookup); - -+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(registryInfoLookup); // Paper - create conversions - for (Entry entry : fileToIdConverter.listMatchingResources(resourceManager).entrySet()) { - Identifier identifier = entry.getKey(); - ResourceKey resourceKey = ResourceKey.create(registry.key(), fileToIdConverter.fileToId(identifier)); -@@ -307,7 +_,7 @@ - RegistrationInfo registrationInfo = REGISTRATION_INFO_CACHE.apply(resource.knownPackInfo()); - - try { -- loadElementFromResource(registry, codec, registryOps, resourceKey, resource, registrationInfo); -+ loadElementFromResource(registry, codec, registryOps, resourceKey, resource, registrationInfo, conversions); // Paper - pass conversions - } catch (Exception var14) { - loadingErrors.put( - resourceKey, -@@ -316,7 +_,9 @@ - } - } - -- TagLoader.loadTagsForRegistry(resourceManager, registry); -+ io.papermc.paper.registry.PaperRegistryAccess.instance().lockReferenceHolders(registry.key()); // Paper - lock reference holders -+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), conversions); // Paper - run pre-freeze listeners -+ TagLoader.loadTagsForRegistry(resourceManager, registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - tag lifecycle - add cause - } - - static void loadContentsFromNetwork( -@@ -333,6 +_,7 @@ - RegistryOps registryOps1 = RegistryOps.create(JsonOps.INSTANCE, registryInfoLookup); - FileToIdConverter fileToIdConverter = FileToIdConverter.registry(registry.key()); - -+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(registryInfoLookup); // Paper - create conversions - for (RegistrySynchronization.PackedRegistryEntry packedRegistryEntry : networkedRegistryData.elements) { - ResourceKey resourceKey = ResourceKey.create(registry.key(), packedRegistryEntry.id()); - Optional optional = packedRegistryEntry.data(); -@@ -351,7 +_,7 @@ - - try { - Resource resourceOrThrow = resourceProvider.getResourceOrThrow(identifier); -- loadElementFromResource(registry, codec, registryOps1, resourceKey, resourceOrThrow, NETWORK_REGISTRATION_INFO); -+ loadElementFromResource(registry, codec, registryOps1, resourceKey, resourceOrThrow, NETWORK_REGISTRATION_INFO, conversions); // Paper - pass conversions - } catch (Exception var17) { - loadingErrors.put(resourceKey, new IllegalStateException("Failed to parse local data", var17)); - } -@@ -393,6 +_,7 @@ - - RegistryDataLoader.Loader create(Lifecycle registryLifecycle, Map, Exception> loadingErrors) { - WritableRegistry writableRegistry = new MappedRegistry<>(this.key, registryLifecycle); -+ io.papermc.paper.registry.PaperRegistryAccess.instance().registerRegistry(writableRegistry); // Paper - initialize API registry - return new RegistryDataLoader.Loader<>(this, writableRegistry, loadingErrors); - } - diff --git a/paper-server/patches/sources/net/minecraft/resources/RegistryLoadTask.java.patch b/paper-server/patches/sources/net/minecraft/resources/RegistryLoadTask.java.patch new file mode 100644 index 000000000000..3077800843e8 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/resources/RegistryLoadTask.java.patch @@ -0,0 +1,33 @@ +--- a/net/minecraft/resources/RegistryLoadTask.java ++++ b/net/minecraft/resources/RegistryLoadTask.java +@@ -29,7 +_,7 @@ + public abstract class RegistryLoadTask { + private final Object registryWriteLock = new Object(); + protected final RegistryDataLoader.RegistryData data; +- private final WritableRegistry registry; ++ final WritableRegistry registry; // Paper - package-private + protected final ConcurrentHolderGetter concurrentRegistrationGetter; + protected final Map, Exception> loadingErrors; + private volatile boolean elementsRegistered; +@@ -39,6 +_,7 @@ + this.registry = new MappedRegistry<>(data.key(), lifecycle); + this.loadingErrors = loadingErrors; + this.concurrentRegistrationGetter = new ConcurrentHolderGetter<>(this.registryWriteLock, this.registry.createRegistrationLookup()); ++ io.papermc.paper.registry.PaperRegistryAccess.instance().registerRegistry(this.registry); // Paper - initialize API registry + } + + protected ResourceKey> registryKey() { +@@ -59,11 +_,11 @@ + return new RegistryOps.RegistryInfo<>(this.registry, this.concurrentRegistrationGetter, this.registry.registryLifecycle()); + } + +- protected void registerElements(final Stream> elements) { ++ protected void registerElements(final Stream> elements, final io.papermc.paper.registry.data.util.Conversions conversions) { // Paper - pass conversions + synchronized (this.registryWriteLock) { + elements.forEach( + element -> element.value +- .ifLeft(value -> this.registry.register(element.key, (T)value, element.registrationInfo)) ++ .ifLeft(value -> io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(this.registry, element.key, (T)value, element.registrationInfo, conversions)) // Paper - register with listeners + .ifRight(error -> this.loadingErrors.put(element.key, error)) + ); + this.elementsRegistered = true; diff --git a/paper-server/patches/sources/net/minecraft/resources/RegistryOps.java.patch b/paper-server/patches/sources/net/minecraft/resources/RegistryOps.java.patch index 93b5b31807ba..031749c949c6 100644 --- a/paper-server/patches/sources/net/minecraft/resources/RegistryOps.java.patch +++ b/paper-server/patches/sources/net/minecraft/resources/RegistryOps.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/resources/RegistryOps.java +++ b/net/minecraft/resources/RegistryOps.java -@@ -117,6 +_,13 @@ +@@ -116,6 +_,13 @@ public int hashCode() { return this.lookupProvider.hashCode(); } @@ -14,7 +14,7 @@ } public record RegistryInfo(HolderOwner owner, HolderGetter getter, Lifecycle elementsLifecycle) { -@@ -127,5 +_,7 @@ +@@ -126,5 +_,7 @@ public interface RegistryInfoLookup { Optional> lookup(ResourceKey> registryKey); diff --git a/paper-server/patches/sources/net/minecraft/resources/ResourceManagerRegistryLoadTask.java.patch b/paper-server/patches/sources/net/minecraft/resources/ResourceManagerRegistryLoadTask.java.patch new file mode 100644 index 000000000000..17f57ea24dad --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/resources/ResourceManagerRegistryLoadTask.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/resources/ResourceManagerRegistryLoadTask.java ++++ b/net/minecraft/resources/ResourceManagerRegistryLoadTask.java +@@ -40,6 +_,7 @@ + @Override + public CompletableFuture load(final RegistryOps.RegistryInfoLookup context, final Executor executor) { + FileToIdConverter lister = FileToIdConverter.registry(this.registryKey()); ++ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(context); // Paper - create conversions + return CompletableFuture.>supplyAsync(() -> lister.listMatchingResources(this.resourceManager), executor) + .thenCompose( + registryResources -> { +@@ -61,11 +_,13 @@ + ) + .thenAcceptAsync( + loadedEntries -> { +- this.registerElements(loadedEntries.entrySet().stream().sorted(Entry.comparingByKey()).map(Entry::getValue)); ++ this.registerElements(loadedEntries.entrySet().stream().sorted(Entry.comparingByKey()).map(Entry::getValue), conversions); ++ io.papermc.paper.registry.PaperRegistryAccess.instance().lockReferenceHolders(this.registryKey()); // Paper - lock reference holders ++ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(this.registryKey(), conversions); // Paper - run pre-freeze listeners + TagLoader.ElementLookup> tagElementLookup = TagLoader.ElementLookup.fromGetters( + this.registryKey(), this.concurrentRegistrationGetter, this.readOnlyRegistry() + ); +- Map, List>> pendingTags = TagLoader.loadTagsForRegistry(this.resourceManager, this.registryKey(), tagElementLookup); ++ Map, List>> pendingTags = TagLoader.loadTagsForRegistry(this.resourceManager, this.registryKey(), tagElementLookup, this.registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - tag lifecycle - add registry, cause + this.registerTags(pendingTags); + }, + executor diff --git a/paper-server/patches/sources/net/minecraft/server/Bootstrap.java.patch b/paper-server/patches/sources/net/minecraft/server/Bootstrap.java.patch index 2700e1f082b5..89768f069023 100644 --- a/paper-server/patches/sources/net/minecraft/server/Bootstrap.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/Bootstrap.java.patch @@ -3,7 +3,7 @@ @@ -43,6 +_,7 @@ if (!isBootstrapped) { isBootstrapped = true; - Instant instant = Instant.now(); + Instant start = Instant.now(); + io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler.enterBootstrappers(); // Paper - Entrypoint for bootstrapping if (BuiltInRegistries.REGISTRY.keySet().isEmpty()) { throw new IllegalStateException("Unable to load registries"); @@ -11,7 +11,7 @@ @@ -54,11 +_,85 @@ EntitySelectorOptions.bootStrap(); DispenseItemBehavior.bootStrap(); - CauldronInteraction.bootStrap(); + CauldronInteractions.bootStrap(); - BuiltInRegistries.bootStrap(); + // Paper start + BuiltInRegistries.bootStrap(() -> { @@ -20,7 +20,7 @@ + // Paper end CreativeModeTabs.validate(); wrapStreams(); - bootstrapDuration.set(Duration.between(instant, Instant.now()).toMillis()); + bootstrapDuration.set(Duration.between(start, Instant.now()).toMillis()); } + // CraftBukkit start + // TODO Check what of this is needed, maybe report it to Mojira. if deemed relevant, move to the respective classes diff --git a/paper-server/patches/sources/net/minecraft/server/Main.java.patch b/paper-server/patches/sources/net/minecraft/server/Main.java.patch index 261d5d355c1c..3e045b797299 100644 --- a/paper-server/patches/sources/net/minecraft/server/Main.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/Main.java.patch @@ -1,67 +1,73 @@ --- a/net/minecraft/server/Main.java +++ b/net/minecraft/server/Main.java -@@ -64,8 +_,10 @@ +@@ -63,8 +_,10 @@ + private static final Logger LOGGER = LogUtils.getLogger(); @SuppressForbidden(reason = "System.out needed before bootstrap") - @DontObfuscate -- public static void main(String[] args) { -+ public static void main(final OptionSet optionSet) { // CraftBukkit - replaces main(String[] args) +- public static void main(final String[] args) { ++ public static void main(final OptionSet options) { // CraftBukkit - replaces main(String[] args) + io.papermc.paper.log.LogManagerShutdownThread.hook(); // Paper - Improved watchdog support SharedConstants.tryDetectVersion(); + /* CraftBukkit start - Replace everything - OptionParser optionParser = new OptionParser(); - OptionSpec optionSpec = optionParser.accepts("nogui"); - OptionSpec optionSpec1 = optionParser.accepts("initSettings", "Initializes 'server.properties' and 'eula.txt', then quits"); -@@ -90,41 +_,94 @@ - optionParser.printHelpOn(System.err); + OptionParser parser = new OptionParser(); + OptionSpec nogui = parser.accepts("nogui"); + OptionSpec initSettings = parser.accepts("initSettings", "Initializes 'server.properties' and 'eula.txt', then quits"); +@@ -89,39 +_,96 @@ + parser.printHelpOn(System.err); return; } + */ // CraftBukkit end + try { -- Path path = optionSet.valueOf(optionSpec14); -+ Path path = (Path) optionSet.valueOf("pidFile"); // CraftBukkit - if (path != null) { - writePidFile(path); +- Path pidFilePath = options.valueOf(pidFile); ++ Path pidFilePath = (Path) options.valueOf("pidFile"); // CraftBukkit + if (pidFilePath != null) { + writePidFile(pidFilePath); } CrashReport.preload(); -- if (optionSet.has(optionSpec13)) { -+ if (optionSet.has("jfrProfile")) { // CraftBukkit +- if (options.has(jfrProfilingOption)) { ++ if (options.has("jfrProfile")) { // CraftBukkit JvmProfiler.INSTANCE.start(Environment.SERVER); } -+ io.papermc.paper.plugin.PluginInitializerManager.load(optionSet); // Paper ++ io.papermc.paper.plugin.PluginInitializerManager.load(options); // Paper Bootstrap.bootStrap(); Bootstrap.validate(); Util.startTimerHackThread(); - Path path1 = Paths.get("server.properties"); -- DedicatedServerSettings dedicatedServerSettings = new DedicatedServerSettings(path1); -+ DedicatedServerSettings dedicatedServerSettings = new DedicatedServerSettings(optionSet); // CraftBukkit - CLI argument support - dedicatedServerSettings.forceSave(); - RegionFileVersion.configure(dedicatedServerSettings.getProperties().regionFileComression); - Path path2 = Paths.get("eula.txt"); - Eula eula = new Eula(path2); -- if (optionSet.has(optionSpec1)) { + Path settingsFile = Paths.get("server.properties"); +- DedicatedServerSettings settings = new DedicatedServerSettings(settingsFile); ++ DedicatedServerSettings settings = new DedicatedServerSettings(options); // CraftBukkit - CLI argument support + settings.forceSave(); ++ // Paper start ++ if (options.has("forceUpgrade") || options.has("recreateRegionFiles")) { ++ LOGGER.error("World upgrade and region file recreation are not yet implemented in Paper 26.1."); ++ return; ++ } ++ // Paper end + RegionFileVersion.configure(settings.getProperties().regionFileComression); + Path eulaFile = Paths.get("eula.txt"); + Eula eula = new Eula(eulaFile); +- if (options.has(initSettings)) { + // Paper start - load config files early for access below if needed -+ org.bukkit.configuration.file.YamlConfiguration bukkitConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionSet.valueOf("bukkit-settings")); -+ org.bukkit.configuration.file.YamlConfiguration spigotConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionSet.valueOf("spigot-settings")); ++ org.bukkit.configuration.file.YamlConfiguration bukkitConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) options.valueOf("bukkit-settings")); ++ org.bukkit.configuration.file.YamlConfiguration spigotConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) options.valueOf("spigot-settings")); + // Paper end - load config files early for access below if needed -+ if (optionSet.has("initSettings")) { // CraftBukkit ++ if (options.has("initSettings")) { // CraftBukkit + // CraftBukkit start - SPIGOT-5761: Create bukkit.yml and commands.yml if not present -+ File configFile = (File) optionSet.valueOf("bukkit-settings"); ++ File configFile = (File) options.valueOf("bukkit-settings"); + org.bukkit.configuration.file.YamlConfiguration configuration = org.bukkit.configuration.file.YamlConfiguration.loadConfiguration(configFile); + configuration.options().copyDefaults(true); + configuration.setDefaults(org.bukkit.configuration.file.YamlConfiguration.loadConfiguration(new java.io.InputStreamReader(Main.class.getClassLoader().getResourceAsStream("configurations/bukkit.yml"), java.nio.charset.StandardCharsets.UTF_8))); + configuration.save(configFile); + -+ File commandFile = (File) optionSet.valueOf("commands-settings"); ++ File commandFile = (File) options.valueOf("commands-settings"); + org.bukkit.configuration.file.YamlConfiguration commandsConfiguration = org.bukkit.configuration.file.YamlConfiguration.loadConfiguration(commandFile); + commandsConfiguration.options().copyDefaults(true); + commandsConfiguration.setDefaults(org.bukkit.configuration.file.YamlConfiguration.loadConfiguration(new java.io.InputStreamReader(Main.class.getClassLoader().getResourceAsStream("configurations/commands.yml"), java.nio.charset.StandardCharsets.UTF_8))); + commandsConfiguration.save(commandFile); + // CraftBukkit end - LOGGER.info("Initialized '{}' and '{}'", path1.toAbsolutePath(), path2.toAbsolutePath()); + LOGGER.info("Initialized '{}' and '{}'", settingsFile.toAbsolutePath(), eulaFile.toAbsolutePath()); return; } @@ -79,9 +85,9 @@ return; } -- File file = new File(optionSet.valueOf(optionSpec9)); -- Services services = Services.create(new YggdrasilAuthenticationService(Proxy.NO_PROXY), file); -- String string = Optional.ofNullable(optionSet.valueOf(optionSpec10)).orElse(dedicatedServerSettings.getProperties().levelName); +- File universePath = new File(options.valueOf(universe)); +- Services services = Services.create(new YggdrasilAuthenticationService(Proxy.NO_PROXY), universePath); +- String levelName = Optional.ofNullable(options.valueOf(worldName)).orElse(settings.getProperties().levelName); + // Paper start - Detect headless JRE + String awtException = io.papermc.paper.util.ServerEnvironment.awtDependencyCheck(); + if (awtException != null) { @@ -97,38 +103,33 @@ + org.spigotmc.SpigotConfig.disabledAdvancements = spigotConfiguration.getStringList("advancements.disabled"); // Paper - fix SPIGOT-5885, must be set early in init + + // Paper start - fix SPIGOT-5824 -+ File file; ++ File universePath; + File userCacheFile = new File(Services.USERID_CACHE_FILE); -+ if (optionSet.has("universe")) { -+ file = (File) optionSet.valueOf("universe"); // CraftBukkit -+ userCacheFile = new File(file, Services.USERID_CACHE_FILE); ++ if (options.has("universe")) { ++ universePath = (File) options.valueOf("universe"); // CraftBukkit ++ userCacheFile = new File(universePath, Services.USERID_CACHE_FILE); + } else { -+ file = new File(bukkitConfiguration.getString("settings.world-container", ".")); ++ universePath = new File(bukkitConfiguration.getString("settings.world-container", ".")); + } + // Paper end - fix SPIGOT-5824 -+ Services services = Services.create(new com.destroystokyo.paper.profile.PaperAuthenticationService(Proxy.NO_PROXY), file, userCacheFile, optionSet); // Paper - pass OptionSet to load paper config files; override authentication service; fix world-container -+ // CraftBukkit start -+ String string = Optional.ofNullable((String) optionSet.valueOf("world")).orElse(dedicatedServerSettings.getProperties().levelName); - LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(file.toPath()); -- LevelStorageSource.LevelStorageAccess levelStorageAccess = levelStorageSource.validateAndCreateAccess(string); -+ LevelStorageSource.LevelStorageAccess levelStorageAccess = levelStorageSource.validateAndCreateAccess(string, LevelStem.OVERWORLD); -+ // CraftBukkit end - Dynamic dataTag; - if (levelStorageAccess.hasWorldData()) { - LevelSummary summary; -@@ -166,12 +_,36 @@ ++ Services services = Services.create(new com.destroystokyo.paper.profile.PaperAuthenticationService(Proxy.NO_PROXY), universePath, userCacheFile, options); // Paper - pass OptionSet to load paper config files; override authentication service; fix world-container ++ String levelName = Optional.ofNullable((String) options.valueOf("world")).orElse(settings.getProperties().levelName); // CraftBukkit + LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(universePath.toPath()); + LevelStorageSource.LevelStorageAccess access = levelStorageSource.validateAndCreateAccess(levelName); + Dynamic levelDataTag; +@@ -150,12 +_,36 @@ + levelDataTag = null; } - Dynamic dynamic = dataTag; -- boolean hasOptionSpec = optionSet.has(optionSpec7); -+ boolean hasOptionSpec = optionSet.has("safeMode"); // CraftBukkit - if (hasOptionSpec) { +- boolean safeModeEnabled = options.has(safeMode); ++ boolean safeModeEnabled = options.has("safeMode"); // CraftBukkit + if (safeModeEnabled) { LOGGER.warn("Safe mode active, only vanilla datapack will be loaded"); } - PackRepository packRepository = ServerPacksSource.createPackRepository(levelStorageAccess); + PackRepository packRepository = ServerPacksSource.createPackRepository(access); + // CraftBukkit start -+ File bukkitDataPackFolder = new File(levelStorageAccess.getLevelPath(net.minecraft.world.level.storage.LevelResource.DATAPACK_DIR).toFile(), "bukkit"); ++ File bukkitDataPackFolder = new File(access.getLevelPath(net.minecraft.world.level.storage.LevelResource.DATAPACK_DIR).toFile(), "bukkit"); + if (!bukkitDataPackFolder.exists()) { + bukkitDataPackFolder.mkdirs(); + } @@ -154,106 +155,135 @@ WorldStem worldStem; try { -@@ -180,6 +_,7 @@ +@@ -164,17 +_,31 @@ executor -> WorldLoader.load( - initConfig, + worldLoadConfig, context -> { + worldLoader.set(context); // CraftBukkit - Registry registry = context.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM); - if (dynamic != null) { - LevelDataAndDimensions levelDataAndDimensions = LevelStorageSource.getLevelDataAndDimensions( -@@ -191,7 +_,7 @@ + Registry datapackDimensions = context.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM); + if (levelDataTag != null) { ++ // Paper start - migrate startup world ++ try { ++ io.papermc.paper.world.migration.WorldFolderMigration.migrateStartupWorld( ++ access, ++ context.datapackWorldgen(), ++ levelName, ++ LevelStem.OVERWORLD, ++ io.papermc.paper.world.PaperWorldLoader.dimensionKey(LevelStem.OVERWORLD) ++ ); ++ } catch (final IOException ex) { ++ throw new UncheckedIOException("Failed to migrate world storage for " + LevelStem.OVERWORLD.identifier(), ex); ++ } ++ // Paper end - migrate startup world + LevelDataAndDimensions worldData = LevelStorageSource.getLevelDataAndDimensions( +- access, levelDataTag, context.dataConfiguration(), datapackDimensions, context.datapackWorldgen() ++ access, levelDataTag, context.dataConfiguration(), datapackDimensions, context.datapackWorldgen(), net.minecraft.world.level.Level.OVERWORLD // Paper + ); + return new WorldLoader.DataLoadOutput<>( + worldData.worldDataAndGenSettings(), worldData.dimensions().dimensionsRegistryAccess() + ); } else { LOGGER.info("No existing world data, creating new world"); - return createNewWorldData( -- dedicatedServerSettings, context, registry, optionSet.has(optionSpec2), optionSet.has(optionSpec3) -+ dedicatedServerSettings, context, registry, optionSet.has("demo"), optionSet.has("bonusChest") // CraftBukkit - ); +- return createNewWorldData(settings, context, datapackDimensions, options.has(demo), options.has(bonusChest)); ++ return createNewWorldData(settings, context, datapackDimensions, options.has("demo"), options.has("bonusChest")); // CraftBukkit } }, -@@ -209,6 +_,7 @@ + WorldStem::new, +@@ -191,6 +_,7 @@ return; } + /* - RegistryAccess.Frozen frozen = worldStem.registries().compositeAccess(); - WorldData worldData = worldStem.worldData(); - boolean hasOptionSpec1 = optionSet.has(optionSpec6); -@@ -217,22 +_,50 @@ + RegistryAccess.Frozen registryHolder = worldStem.registries().compositeAccess(); + WorldData data = worldStem.worldDataAndGenSettings().data(); + boolean recreateRegionFilesValue = options.has(recreateRegionFiles); +@@ -199,22 +_,51 @@ } - levelStorageAccess.saveDataTag(frozen, worldData); + access.saveDataTag(data); + */ final DedicatedServer dedicatedServer = MinecraftServer.spin( - thread1 -> { - DedicatedServer dedicatedServer1 = new DedicatedServer( -- thread1, levelStorageAccess, packRepository, worldStem, dedicatedServerSettings, DataFixers.getDataFixer(), services + thread -> { + DedicatedServer server = new DedicatedServer( +- thread, access, packRepository, worldStem, Optional.empty(), settings, DataFixers.getDataFixer(), services + // CraftBukkit start -+ optionSet, ++ options, + worldLoader.get(), -+ thread1, -+ levelStorageAccess, ++ thread, ++ access, + packRepository, + worldStem, -+ dedicatedServerSettings, ++ Optional.empty(), ++ settings, + DataFixers.getDataFixer(), + services ); + /* - dedicatedServer1.setPort(optionSet.valueOf(optionSpec11)); -- dedicatedServer1.setDemo(optionSet.has(optionSpec2)); + server.setPort(options.valueOf(port)); +- server.setDemo(options.has(demo)); + */ + // Paper start -+ if (optionSet.has("serverId")) { -+ dedicatedServer1.setId((String) optionSet.valueOf("serverId")); ++ if (options.has("serverId")) { ++ server.setId((String) options.valueOf("serverId")); + } -+ dedicatedServer1.setDemo(optionSet.has("demo")); ++ server.setDemo(options.has("demo")); + // Paper end + /* - dedicatedServer1.setId(optionSet.valueOf(optionSpec12)); -- boolean flag = !optionSet.has(optionSpec) && !optionSet.valuesOf(optionSpec15).contains("nogui"); + server.setId(options.valueOf(serverId)); +- boolean gui = !options.has(nogui) && !options.valuesOf(nonOptions).contains("nogui"); + */ -+ boolean flag = !optionSet.has("nogui") && !optionSet.nonOptionArguments().contains("nogui"); - if (flag && !GraphicsEnvironment.isHeadless()) { - dedicatedServer1.showGui(); ++ boolean gui = !options.has("nogui") && !options.nonOptionArguments().contains("nogui"); + if (gui && !GraphicsEnvironment.isHeadless()) { + server.showGui(); } + // Paper start -+ if (optionSet.has("port")) { -+ int port = (Integer) optionSet.valueOf("port"); ++ if (options.has("port")) { ++ int port = (Integer) options.valueOf("port"); + if (port > 0) { -+ dedicatedServer1.setPort(port); ++ server.setPort(port); + } + } + // Paper end - return dedicatedServer1; + return server; } ); + /* CraftBukkit start - Thread thread = new Thread("Server Shutdown Thread") { + Thread shutdownThread = new Thread("Server Shutdown Thread") { @Override public void run() { -@@ -241,12 +_,13 @@ +@@ -223,12 +_,13 @@ }; - thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)); - Runtime.getRuntime().addShutdownHook(thread); + shutdownThread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)); + Runtime.getRuntime().addShutdownHook(shutdownThread); + */ // CraftBukkit end - } catch (Throwable var42) { - LOGGER.error(LogUtils.FATAL_MARKER, "Failed to start the minecraft server", var42); + } catch (Throwable var40) { + LOGGER.error(LogUtils.FATAL_MARKER, "Failed to start the minecraft server", var40); } } -- private static WorldLoader.DataLoadOutput createNewWorldData( -+ public static WorldLoader.DataLoadOutput createNewWorldData( // Paper - public - DedicatedServerSettings settings, WorldLoader.DataLoadContext context, Registry stemRegistry, boolean demo, boolean generateBonusChest - ) { - LevelSettings levelSettings; -@@ -314,7 +_,7 @@ - RegistryAccess registryAccess, - boolean recreateRegionFiles +- private static WorldLoader.DataLoadOutput createNewWorldData( ++ public static WorldLoader.DataLoadOutput createNewWorldData( // Paper - public + final DedicatedServerSettings settings, + final WorldLoader.DataLoadContext context, + final Registry datapackDimensions, +@@ -299,7 +_,11 @@ + final RegistryAccess registryAccess, + final boolean recreateRegionFiles ) { - LOGGER.info("Forcing world upgrade!"); -+ LOGGER.info("Forcing world upgrade! {}", levelStorage.getLevelId()); // CraftBukkit ++ throw new UnsupportedOperationException( ++ "World upgrade and region file recreation are not yet implemented in Paper 26.1." ++ ); ++ /* ++ LOGGER.info("Forcing world upgrade! {}", storageSource.getLevelId()); // CraftBukkit - try (WorldUpgrader worldUpgrader = new WorldUpgrader(levelStorage, dataFixer, worldData, registryAccess, eraseCache, recreateRegionFiles)) { - Component component = null; + try (WorldUpgrader upgrader = new WorldUpgrader(storageSource, fixerUpper, registryAccess, eraseCache, recreateRegionFiles)) { + Component lastStatus = null; +@@ -327,5 +_,6 @@ + } + } + } ++ */ + } + } diff --git a/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch b/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch index b0c73c66dc97..30cc6eed23a1 100644 --- a/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -186,11 +_,13 @@ +@@ -192,11 +_,13 @@ import org.slf4j.Logger; - public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements ServerInfo, CommandSource, ChunkIOErrorReporter { + public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements CommandSource, ServerInfo, ChunkIOErrorReporter { + private static MinecraftServer SERVER; // Paper public static final Logger LOGGER = LogUtils.getLogger(); + public static final net.kyori.adventure.text.logger.slf4j.ComponentLogger COMPONENT_LOGGER = net.kyori.adventure.text.logger.slf4j.ComponentLogger.logger(LOGGER.getName()); // Paper @@ -15,7 +15,16 @@ private static final int OVERLOADED_TICKS_THRESHOLD = 20; private static final long OVERLOADED_WARNING_INTERVAL_NANOS = 10L * TimeUtil.NANOSECONDS_PER_SECOND; private static final int OVERLOADED_TICKS_WARNING_INTERVAL = 100; -@@ -217,7 +_,7 @@ +@@ -219,7 +_,7 @@ + public final PlayerDataStorage playerDataStorage; + private final SavedDataStorage savedDataStorage; + private final List tickables = Lists.newArrayList(); +- private final GameRules gameRules; ++ // Paper - per-level GameRules + private MetricsRecorder metricsRecorder = InactiveMetricsRecorder.INSTANCE; + private Consumer onMetricsRecordingStopped = results -> this.stopRecordingMetrics(); + private Consumer onMetricsRecordingFinished = ignored -> {}; +@@ -227,7 +_,7 @@ private MinecraftServer.@Nullable TimeProfiler debugCommandProfiler; private boolean debugCommandProfilerDelayStart; private ServerConnectionListener connection; @@ -24,7 +33,7 @@ private @Nullable ServerStatus status; private ServerStatus.@Nullable Favicon statusIcon; private final RandomSource random = RandomSource.create(); -@@ -228,13 +_,14 @@ +@@ -238,13 +_,14 @@ private Map, ServerLevel> levels = Maps.newLinkedHashMap(); private PlayerList playerList; private volatile boolean running = true; @@ -40,9 +49,35 @@ private int playerIdleTimeout; private final long[] tickTimesNanos = new long[100]; private long aggregatedTickTimesNanos = 0L; -@@ -281,10 +_,109 @@ +@@ -272,7 +_,7 @@ + private @Nullable CommandStorage commandStorage; + private final CustomBossEvents customBossEvents; + private final RandomSequences randomSequences; +- private final WeatherData weatherData; ++ // Paper - per-level WeatherData + private final ServerFunctionManager functionManager; + private boolean enforceWhitelist; + private boolean usingWhitelist; +@@ -292,13 +_,14 @@ + private final SuppressedExceptionCollector suppressedExceptions = new SuppressedExceptionCollector(); private final DiscontinuousFrame tickFrame; private final PacketProcessor packetProcessor; +- private final TimerQueue scheduledEvents; ++ // Paper - per-level scheduledEvents + private final ServerClockManager clockManager; + + public static S spin(final Function factory) { + AtomicReference serverReference = new AtomicReference<>(); +- Thread thread = new Thread(() -> serverReference.get().runServer(), "Server thread"); ++ Thread thread = new ca.spottedleaf.moonrise.common.util.TickThread(() -> serverReference.get().runServer(), "Server thread"); + thread.setUncaughtExceptionHandler((t, e) -> LOGGER.error("Uncaught exception in server thread", e)); ++ thread.setPriority(Thread.NORM_PRIORITY + 2); // Paper - Perf: Boost priority + if (Runtime.getRuntime().availableProcessors() > 4) { + thread.setPriority(8); + } +@@ -309,7 +_,124 @@ + return server; + } + // CraftBukkit start + public final WorldLoader.DataLoadContext worldLoaderContext; @@ -81,6 +116,21 @@ + private long currentTickStart; + private long scheduledTickStart; + private long taskExecutionTime; ++ /** ++ * The tickCount field is not incremented exactly where and when we want for our ++ * usage here. ++ *

    ++ * There are two problems we need to fix: ++ *
      ++ *
    1. The tickCount field is not incremented when paused through integrated server.
    2. ++ *
    3. The tickCount field is incremented after draining tasks (during server tick).
    4. ++ *
    ++ * Our goal with the tick count here is to prevent executing tasks scheduled after the start ++ * of the current tick, which is marked by the task draining. ++ * ++ * @see #runAllTasksAtTickStart ++ */ ++ private final java.util.concurrent.atomic.AtomicInteger tickTaskTickCount = new java.util.concurrent.atomic.AtomicInteger(); + private final Object statsLock = new Object(); + private @Nullable double[] tps; + private ca.spottedleaf.moonrise.common.time.TickData.@Nullable MSPTData msptData5s; @@ -142,37 +192,30 @@ + } + // Paper end - improve tick loop + - public static S spin(Function threadFunction) { - AtomicReference atomicReference = new AtomicReference<>(); -- Thread thread = new Thread(() -> atomicReference.get().runServer(), "Server thread"); -+ Thread thread = new ca.spottedleaf.moonrise.common.util.TickThread(() -> atomicReference.get().runServer(), "Server thread"); - thread.setUncaughtExceptionHandler((thread1, exception) -> LOGGER.error("Uncaught exception in server thread", exception)); -+ thread.setPriority(Thread.NORM_PRIORITY+2); // Paper - Perf: Boost priority - if (Runtime.getRuntime().availableProcessors() > 4) { - thread.setPriority(8); - } -@@ -296,6 +_,10 @@ - } - public MinecraftServer( + // CraftBukkit start + joptsimple.OptionSet options, + WorldLoader.DataLoadContext worldLoaderContext, + // CraftBukkit end - Thread serverThread, - LevelStorageSource.LevelStorageAccess storageSource, - PackRepository packRepository, -@@ -306,18 +_,19 @@ - LevelLoadListener levelLoadListener + final Thread serverThread, + final LevelStorageSource.LevelStorageAccess storageSource, + final PackRepository packRepository, +@@ -322,27 +_,27 @@ + final boolean propagatesCrashes ) { - super("Server"); + super("Server", propagatesCrashes); + SERVER = this; // Paper - better singleton this.registries = worldStem.registries(); - this.worldData = worldStem.worldData(); - if (!this.registries.compositeAccess().lookupOrThrow(Registries.LEVEL_STEM).containsKey(LevelStem.OVERWORLD)) { + if (false && !this.registries.compositeAccess().lookupOrThrow(Registries.LEVEL_STEM).containsKey(LevelStem.OVERWORLD)) { // CraftBukkit - initialised later throw new IllegalStateException("Missing Overworld dimension data"); } else { +- this.savedDataStorage = new SavedDataStorage(storageSource.getLevelPath(LevelResource.DATA), fixerUpper, this.registries.compositeAccess()); ++ this.savedDataStorage = new SavedDataStorage(storageSource.getLevelPath(LevelResource.ROOT).resolve(LevelResource.DATA.id()), fixerUpper, this.registries.compositeAccess()); + this.worldData = worldStem.worldDataAndGenSettings().data(); + this.worldGenSettings = worldStem.worldDataAndGenSettings().genSettings(); +- this.savedDataStorage.set(WorldGenSettings.TYPE, this.worldGenSettings); ++ // this.savedDataStorage.set(WorldGenSettings.TYPE, this.worldGenSettings); // Paper - save in level storage this.proxy = proxy; this.packRepository = packRepository; this.resources = new MinecraftServer.ReloadableResources(worldStem.resourceManager(), worldStem.dataPackResources()); @@ -181,14 +224,26 @@ + // this.connection = new ServerConnectionListener(this); // Spigot this.tickRateManager = new ServerTickRateManager(this); - this.levelLoadListener = levelLoadListener; -+ // Paper - per level load listener - move LevelLoadListener to ServerLevel ++ // Paper - per-level load listener - move LevelLoadListener to ServerLevel this.storageSource = storageSource; this.playerDataStorage = storageSource.createPlayerStorage(); + this.randomSequences = this.savedDataStorage.computeIfAbsent(RandomSequences.TYPE); +- this.weatherData = this.getDataStorage().computeIfAbsent(WeatherData.TYPE); +- this.gameRules = new GameRules(this.worldData.enabledFeatures(), this.savedDataStorage.computeIfAbsent(GameRuleMap.TYPE)); +- gameRules.ifPresent(g -> this.gameRules.setAll(g, null)); ++ // Paper - per-level WeatherData ++ // Paper - per-level GameRules this.fixerUpper = fixerUpper; -@@ -337,6 +_,38 @@ - this.serverActivityMonitor = new ServerActivityMonitor(this.notificationManager, 30); - this.packetProcessor = new PacketProcessor(serverThread); - } + this.functionManager = new ServerFunctionManager(this, this.resources.managers.getFunctionLibrary()); + HolderGetter blockLookup = this.registries +@@ -362,8 +_,40 @@ + this.clockManager = this.getDataStorage().computeIfAbsent(ServerClockManager.TYPE); + this.clockManager.init(this); + this.customBossEvents = this.savedDataStorage.computeIfAbsent(CustomBossEvents.TYPE); +- this.scheduledEvents = this.savedDataStorage.computeIfAbsent(TimerQueue.TYPE); +- } ++ // Paper - per-level scheduledEvents ++ } + // CraftBukkit start + this.options = options; + this.worldLoaderContext = worldLoaderContext; @@ -224,13 +279,13 @@ } protected abstract boolean initServer() throws IOException; -@@ -369,15 +_,15 @@ +@@ -400,15 +_,15 @@ }; } - protected void loadLevel() { -+ protected void loadLevel(String levelId) { // CraftBukkit - boolean flag = !JvmProfiler.INSTANCE.isRunning() ++ protected void loadLevel(final String levelId) { // CraftBukkit + boolean startedWorldLoadProfiling = !JvmProfiler.INSTANCE.isRunning() && SharedConstants.DEBUG_JFR_PROFILING_ENABLE_LEVEL_LOADING && JvmProfiler.INSTANCE.start(Environment.from(this)); ProfiledDuration profiledDuration = JvmProfiler.INSTANCE.onWorldLoadedStarted(); @@ -245,7 +300,7 @@ if (profiledDuration != null) { profiledDuration.finish(true); } -@@ -391,31 +_,127 @@ +@@ -422,33 +_,139 @@ } } @@ -267,7 +322,6 @@ + this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD); + this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark + this.server.spark.enableAfterPlugins(this.server); // Paper - spark -+ if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins + io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // Paper - reset invalid state for event fire below + io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - call commands event for regular plugins + this.server.getCommandMap().registerServerAliases(); // Paper - relocate initial CommandMap#registerServerAliases() call @@ -281,179 +335,153 @@ } - protected void createLevels() { -- ServerLevelData serverLevelData = this.worldData.overworldData(); -- boolean isDebugWorld = this.worldData.isDebugWorld(); -- Registry registry = this.registries.compositeAccess().lookupOrThrow(Registries.LEVEL_STEM); -- WorldOptions worldOptions = this.worldData.worldGenOptions(); +- ServerLevelData levelData = this.worldData.overworldData(); +- boolean isDebug = this.worldData.isDebugWorld(); +- Registry dimensions = this.registries.compositeAccess().lookupOrThrow(Registries.LEVEL_STEM); +- WorldOptions worldOptions = this.worldGenSettings.options(); + // Paper start - rework world loading process + public void createLevel( + LevelStem levelStem, -+ io.papermc.paper.world.PaperWorldLoader.WorldLoadingInfo loadingInfo, -+ LevelStorageSource.LevelStorageAccess levelStorageAccess, -+ net.minecraft.world.level.storage.PrimaryLevelData serverLevelData ++ io.papermc.paper.world.PaperWorldLoader.WorldLoadingInfoAndData loading, ++ net.minecraft.world.level.storage.LevelDataAndDimensions.WorldDataAndGenSettings worldDataAndGenSettings + ) { -+ WorldOptions worldOptions = serverLevelData.worldGenOptions(); ++ final WorldOptions worldOptions = worldDataAndGenSettings.genSettings().options(); ++ final ResourceKey dimensionKey = loading.info().dimensionKey(); ++ final SavedDataStorage savedDataStorage = new SavedDataStorage(this.storageSource.getDimensionPath(dimensionKey).resolve(LevelResource.DATA.id()), this.getFixerUpper(), this.registryAccess()); ++ savedDataStorage.set(WorldGenSettings.TYPE, new WorldGenSettings(worldDataAndGenSettings.genSettings().options(), worldDataAndGenSettings.genSettings().dimensions())); long seed = worldOptions.seed(); - long l = BiomeManager.obfuscateSeed(seed); - List list = ImmutableList.of( - new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(serverLevelData) - ); -- LevelStem levelStem = registry.getValue(LevelStem.OVERWORLD); -- ServerLevel serverLevel = new ServerLevel( -- this, this.executor, this.storageSource, serverLevelData, Level.OVERWORLD, levelStem, isDebugWorld, l, list, true, null -+ final org.bukkit.generator.ChunkGenerator chunkGenerator = this.server.getGenerator(loadingInfo.name()); -+ org.bukkit.generator.BiomeProvider biomeProvider = this.server.getBiomeProvider(loadingInfo.name()); + long biomeZoomSeed = BiomeManager.obfuscateSeed(seed); + List overworldCustomSpawners = ImmutableList.of( +- new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(this.savedDataStorage) +- ); +- LevelStem overworldData = dimensions.getValue(LevelStem.OVERWORLD); +- ServerLevel overworld = new ServerLevel( +- this, this.executor, this.storageSource, levelData, Level.OVERWORLD, overworldData, isDebug, biomeZoomSeed, overworldCustomSpawners, true +- ); +- this.levels.put(Level.OVERWORLD, overworld); ++ new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(savedDataStorage) // Paper - save to world data ++ ); ++ final org.bukkit.generator.ChunkGenerator chunkGenerator = this.server.getGenerator(loading.data().bukkitName()); ++ org.bukkit.generator.BiomeProvider biomeProvider = this.server.getBiomeProvider(loading.data().bukkitName()); + final org.bukkit.generator.WorldInfo worldInfo = new org.bukkit.craftbukkit.generator.CraftWorldInfo( -+ serverLevelData, -+ levelStorageAccess, -+ org.bukkit.World.Environment.getEnvironment(loadingInfo.dimension()), ++ loading.data().bukkitName(), ++ worldDataAndGenSettings.genSettings().options().seed(), ++ worldDataAndGenSettings.data().enabledFeatures(), ++ loading.info().environment(), + levelStem.type().value(), + levelStem.generator(), -+ this.registryAccess() - ); -- this.levels.put(Level.OVERWORLD, serverLevel); ++ this.registryAccess(), ++ loading.data().uuid() ++ ); + if (biomeProvider == null && chunkGenerator != null) { + biomeProvider = chunkGenerator.getDefaultBiomeProvider(worldInfo); + } -+ final ResourceKey dimensionKey = ResourceKey.create(Registries.DIMENSION, loadingInfo.stemKey().identifier()); + ServerLevel serverLevel; -+ if (loadingInfo.stemKey() == LevelStem.OVERWORLD) { ++ if (loading.info().stemKey() == LevelStem.OVERWORLD) { + serverLevel = new ServerLevel( + this, + this.executor, -+ levelStorageAccess, -+ serverLevelData, ++ this.storageSource, ++ worldDataAndGenSettings.genSettings(), + dimensionKey, + levelStem, -+ serverLevelData.isDebugWorld(), -+ l, -+ list, ++ worldDataAndGenSettings.data().isDebugWorld(), ++ biomeZoomSeed, ++ overworldCustomSpawners, + true, -+ null, -+ org.bukkit.World.Environment.getEnvironment(loadingInfo.dimension()), ++ loading.info().stemKey(), ++ loading.info().environment(), + chunkGenerator, -+ biomeProvider ++ biomeProvider, ++ savedDataStorage, ++ loading.data() + ); -+ this.worldData = serverLevelData; ++ this.worldData = worldDataAndGenSettings.data(); + this.worldData.setGameType(((net.minecraft.server.dedicated.DedicatedServer) this).getProperties().gameMode.get()); // From DedicatedServer.init - DimensionDataStorage dataStorage = serverLevel.getDataStorage(); - this.scoreboard.load(dataStorage.computeIfAbsent(ScoreboardSaveData.TYPE).getData()); - this.commandStorage = new CommandStorage(dataStorage); - this.stopwatches = dataStorage.computeIfAbsent(Stopwatches.TYPE); + this.scoreboard.load(this.savedDataStorage.computeIfAbsent(ScoreboardSaveData.TYPE).getData()); + this.commandStorage = new CommandStorage(this.savedDataStorage); + this.stopwatches = this.savedDataStorage.computeIfAbsent(Stopwatches.TYPE); + this.server.scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(this, serverLevel.getScoreboard()); + } else { + final List spawners; + if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.useDimensionTypeForCustomSpawners && levelStem.type().is(net.minecraft.world.level.dimension.BuiltinDimensionTypes.OVERWORLD)) { -+ spawners = list; ++ spawners = overworldCustomSpawners; + } else { + spawners = Collections.emptyList(); + } + serverLevel = new ServerLevel( + this, + this.executor, -+ levelStorageAccess, -+ serverLevelData, ++ this.storageSource, ++ worldDataAndGenSettings.genSettings(), + dimensionKey, + levelStem, + this.worldData.isDebugWorld(), -+ l, ++ biomeZoomSeed, + spawners, + true, -+ this.overworld().getRandomSequences(), -+ org.bukkit.World.Environment.getEnvironment(loadingInfo.dimension()), ++ loading.info().stemKey(), ++ loading.info().environment(), + chunkGenerator, -+ biomeProvider ++ biomeProvider, ++ savedDataStorage, ++ loading.data() + ); + } + this.addLevel(serverLevel); -+ this.initWorld(serverLevel, serverLevelData, worldOptions); ++ this.initWorld(serverLevel); + } -+ public void initWorld(ServerLevel serverLevel, net.minecraft.world.level.storage.PrimaryLevelData serverLevelData, WorldOptions worldOptions) { -+ final boolean isDebugWorld = this.worldData.isDebugWorld(); -+ if (serverLevel.generator != null) { -+ serverLevel.getWorld().getPopulators().addAll(serverLevel.generator.getDefaultPopulators(serverLevel.getWorld())); ++ public void initWorld(ServerLevel overworld) { ++ final net.minecraft.world.level.storage.ServerLevelData levelData = overworld.serverLevelData; ++ final WorldOptions worldOptions = overworld.worldGenSettings.options(); ++ final boolean isDebug = this.worldData.isDebugWorld(); ++ if (overworld.generator != null) { ++ overworld.getWorld().getPopulators().addAll(overworld.generator.getDefaultPopulators(overworld.getWorld())); + } -+ this.initWorldBorder(serverLevelData, serverLevel); -+ this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldInitEvent(serverLevel.getWorld())); ++ overworld.getWorldBorder().world = overworld; ++ overworld.getWorldBorder().setAbsoluteMaxSize(this.getAbsoluteMaxWorldSize()); ++ this.getPlayerList().addWorldborderListener(overworld); ++ this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldInitEvent(overworld.getWorld())); + // Paper end - rework world loading process - if (!serverLevelData.isInitialized()) { + if (!levelData.isInitialized()) { try { -- setInitialSpawn(serverLevel, serverLevelData, worldOptions.generateBonusChest(), isDebugWorld, this.levelLoadListener); -+ setInitialSpawn(serverLevel, serverLevelData, worldOptions.generateBonusChest(), isDebugWorld, serverLevel.levelLoadListener); // Paper - per world level load listener - serverLevelData.setInitialized(true); - if (isDebugWorld) { - this.setupDebugLevel(this.worldData); -@@ -435,38 +_,16 @@ - } - - GlobalPos globalPos = this.selectLevelLoadFocusPos(); -- this.levelLoadListener.updateFocus(globalPos.dimension(), new ChunkPos(globalPos.pos())); -- if (this.worldData.getCustomBossEvents() != null) { -- this.getCustomBossEvents().load(this.worldData.getCustomBossEvents(), this.registryAccess()); -+ serverLevel.levelLoadListener.updateFocus(globalPos.dimension(), new ChunkPos(globalPos.pos())); // Paper - per world load listener -+ if (serverLevelData.getCustomBossEvents() != null) { // Paper - rework world loading process -+ this.getCustomBossEvents().load(serverLevelData.getCustomBossEvents(), this.registryAccess()); // Paper - rework world loading process +- setInitialSpawn(overworld, levelData, worldOptions.generateBonusChest(), isDebug, this.levelLoadListener); ++ setInitialSpawn(overworld, levelData, worldOptions.generateBonusChest(), isDebug, overworld.levelLoadListener); // Paper - per world level load listener + levelData.setInitialized(true); + if (isDebug) { +- this.setupDebugLevel(this.worldData); ++ this.setupDebugLevel(this.worldData, overworld); // Paper - per-level GameRules + } + } catch (Throwable var20) { + CrashReport report = CrashReport.forThrowable(var20, "Exception initializing level"); +@@ -465,25 +_,7 @@ } -- RandomSequences randomSequences = serverLevel.getRandomSequences(); -- boolean flag = false; + GlobalPos focusPos = this.selectLevelLoadFocusPos(); +- this.levelLoadListener.updateFocus(focusPos.dimension(), ChunkPos.containing(focusPos.pos())); - -- for (Entry, LevelStem> entry : registry.entrySet()) { -- ResourceKey resourceKey = entry.getKey(); -- ServerLevel serverLevel1; -- if (resourceKey != LevelStem.OVERWORLD) { -- ResourceKey resourceKey1 = ResourceKey.create(Registries.DIMENSION, resourceKey.identifier()); -- DerivedLevelData derivedLevelData = new DerivedLevelData(this.worldData, serverLevelData); -- serverLevel1 = new ServerLevel( -- this, -- this.executor, -- this.storageSource, -- derivedLevelData, -- resourceKey1, -- entry.getValue(), -- isDebugWorld, -- l, -- ImmutableList.of(), -- false, -- randomSequences +- for (Entry, LevelStem> entry : dimensions.entrySet()) { +- ResourceKey name = entry.getKey(); +- ServerLevel level; +- if (name != LevelStem.OVERWORLD) { +- ResourceKey dimension = ResourceKey.create(Registries.DIMENSION, name.identifier()); +- DerivedLevelData derivedLevelData = new DerivedLevelData(this.worldData, levelData); +- level = new ServerLevel( +- this, this.executor, this.storageSource, derivedLevelData, dimension, entry.getValue(), isDebug, biomeZoomSeed, ImmutableList.of(), false - ); -- this.levels.put(resourceKey1, serverLevel1); +- this.levels.put(dimension, level); - } else { -- serverLevel1 = serverLevel; +- level = overworld; - } - -+ // Paper start - rework world loading process -+ } -+ private void initWorldBorder(net.minecraft.world.level.storage.PrimaryLevelData serverLevelData, ServerLevel serverLevel) { -+ final ServerLevel serverLevel1 = serverLevel; // Rename for below code -+ // Paper end - rework world loading process - Optional legacyWorldBorderSettings = serverLevelData.getLegacyWorldBorderSettings(); - if (legacyWorldBorderSettings.isPresent()) { - WorldBorder.Settings settings = legacyWorldBorderSettings.get(); -@@ -489,16 +_,14 @@ - dataStorage1.set(WorldBorder.TYPE, worldBorder); - } - -- flag = true; -+ serverLevelData.setLegacyWorldBorderSettings(Optional.empty()); // Paper - rework world loading process - } - -- serverLevel1.getWorldBorder().setAbsoluteMaxSize(this.getAbsoluteMaxWorldSize()); -- this.getPlayerList().addWorldborderListener(serverLevel1); -- } -- -- if (flag) { -- serverLevelData.setLegacyWorldBorderSettings(Optional.empty()); +- level.getWorldBorder().setAbsoluteMaxSize(this.getAbsoluteMaxWorldSize()); +- this.getPlayerList().addWorldborderListener(level); - } -+ // Paper start - rework world loading process -+ serverLevel.getWorldBorder().world = serverLevel; -+ serverLevel.getWorldBorder().setAbsoluteMaxSize(this.getAbsoluteMaxWorldSize()); -+ this.getPlayerList().addWorldborderListener(serverLevel); -+ // Paper end - rework world loading process ++ overworld.levelLoadListener.updateFocus(focusPos.dimension(), ChunkPos.containing(focusPos.pos())); // Paper - per world load listener } private static void setInitialSpawn( -@@ -510,6 +_,28 @@ +@@ -499,6 +_,28 @@ levelData.setSpawn(LevelData.RespawnData.of(level.dimension(), BlockPos.ZERO.above(80), 0.0F, 0.0F)); } else { ServerChunkCache chunkSource = level.getChunkSource(); @@ -469,7 +497,7 @@ + levelData.setSpawn( + net.minecraft.world.level.storage.LevelData.RespawnData.of( + level.dimension(), -+ org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(spawn), ++ org.bukkit.craftbukkit.util.CraftLocation.toBlockPos(spawn), + spawn.getYaw(), + spawn.getPitch() + ) @@ -479,74 +507,63 @@ + } + } + // CraftBukkit end - ChunkPos chunkPos = new ChunkPos(chunkSource.randomState().sampler().findSpawnPosition()); + ChunkPos spawnChunk = ChunkPos.containing(chunkSource.randomState().sampler().findSpawnPosition()); levelLoadListener.start(LevelLoadListener.Stage.PREPARE_GLOBAL_SPAWN, 0); - levelLoadListener.updateFocus(level.dimension(), chunkPos); -@@ -566,10 +_,13 @@ - serverLevelData.setGameType(GameType.SPECTATOR); + levelLoadListener.updateFocus(level.dimension(), spawnChunk); +@@ -544,19 +_,22 @@ + } + } + +- private void setupDebugLevel(final WorldData worldData) { ++ private void setupDebugLevel(final WorldData worldData, final ServerLevel level) { // Paper - pass level + worldData.setDifficulty(Difficulty.PEACEFUL); + worldData.setDifficultyLocked(true); + ServerLevelData levelData = worldData.overworldData(); +- this.getGameRules().set(GameRules.ADVANCE_WEATHER, false, this); ++ level.getGameRules().set(GameRules.ADVANCE_WEATHER, false, level); // Paper - per-level GameRules + this.clockManager.moveToTimeMarker(this.registryAccess().getOrThrow(WorldClocks.OVERWORLD), ClockTimeMarkers.NOON); + levelData.setGameType(GameType.SPECTATOR); } - private void prepareLevels() { + // CraftBukkit start -+ public void prepareLevel(ServerLevel serverLevel) { ++ public void prepareLevel(ServerLevel level) { + this.forceTicks = true; + // CraftBukkit end ChunkLoadCounter chunkLoadCounter = new ChunkLoadCounter(); -- for (ServerLevel serverLevel : this.levels.values()) { +- for (ServerLevel level : this.levels.values()) { + if (true) { // CraftBukkit - chunkLoadCounter.track(serverLevel, () -> { - TicketStorage ticketStorage = serverLevel.getDataStorage().get(TicketStorage.TYPE); - if (ticketStorage != null) { -@@ -578,17 +_,19 @@ + chunkLoadCounter.track(level, () -> { + TicketStorage savedTickets = level.getDataStorage().get(TicketStorage.TYPE); + if (savedTickets != null) { +@@ -565,17 +_,19 @@ }); } - this.levelLoadListener.start(LevelLoadListener.Stage.LOAD_INITIAL_CHUNKS, chunkLoadCounter.totalChunks()); -+ serverLevel.levelLoadListener.start(LevelLoadListener.Stage.LOAD_INITIAL_CHUNKS, chunkLoadCounter.totalChunks()); // Paper - per world load listener ++ level.levelLoadListener.start(LevelLoadListener.Stage.LOAD_INITIAL_CHUNKS, chunkLoadCounter.totalChunks()); // Paper - per world load listener do { - this.levelLoadListener.update(LevelLoadListener.Stage.LOAD_INITIAL_CHUNKS, chunkLoadCounter.readyChunks(), chunkLoadCounter.totalChunks()); - this.nextTickTimeNanos = Util.getNanos() + PREPARE_LEVELS_DEFAULT_DELAY_NANOS; - this.waitUntilNextTick(); -+ serverLevel.levelLoadListener.update(LevelLoadListener.Stage.LOAD_INITIAL_CHUNKS, chunkLoadCounter.readyChunks(), chunkLoadCounter.totalChunks()); // Paper - per world load listener ++ level.levelLoadListener.update(LevelLoadListener.Stage.LOAD_INITIAL_CHUNKS, chunkLoadCounter.readyChunks(), chunkLoadCounter.totalChunks()); // Paper - per world load listener + this.executeModerately(); // CraftBukkit } while (chunkLoadCounter.pendingChunks() > 0); - this.levelLoadListener.finish(LevelLoadListener.Stage.LOAD_INITIAL_CHUNKS); - this.updateMobSpawningFlags(); -+ serverLevel.levelLoadListener.finish(LevelLoadListener.Stage.LOAD_INITIAL_CHUNKS); // Paper - per world load listener -+ serverLevel.setSpawnSettings(serverLevel.isSpawningMonsters()); // Paper - per level difficulty (from setDifficulty(ServerLevel, Difficulty, boolean)) ++ level.levelLoadListener.finish(LevelLoadListener.Stage.LOAD_INITIAL_CHUNKS); // Paper - per world load listener ++ level.setSpawnSettings(level.isSpawningMonsters()); // Paper - per level difficulty (from setDifficulty(ServerLevel, Difficulty, boolean)) this.updateEffectiveRespawnData(); + this.forceTicks = false; // CraftBukkit -+ serverLevel.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API -+ new org.bukkit.event.world.WorldLoadEvent(serverLevel.getWorld()).callEvent(); // Paper - call WorldLoadEvent ++ level.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API ++ new org.bukkit.event.world.WorldLoadEvent(level.getWorld()).callEvent(); // Paper - call WorldLoadEvent } protected GlobalPos selectLevelLoadFocusPos() { -@@ -610,7 +_,7 @@ - public abstract boolean shouldRconBroadcast(); - - public boolean saveAllChunks(boolean suppressLogs, boolean flush, boolean force) { -- this.scoreboard.storeToSaveDataIfDirty(this.overworld().getDataStorage().computeIfAbsent(ScoreboardSaveData.TYPE)); -+ if (this.overworld() != null) this.scoreboard.storeToSaveDataIfDirty(this.overworld().getDataStorage().computeIfAbsent(ScoreboardSaveData.TYPE)); // Paper - don't try to save if the overworld was not loaded, generally during early startup failures - boolean flag = false; - - for (ServerLevel serverLevel : this.getAllLevels()) { -@@ -622,8 +_,10 @@ - flag = true; - } - -- this.worldData.setCustomBossEvents(this.getCustomBossEvents().save(this.registryAccess())); -- this.storageSource.saveDataTag(this.registryAccess(), this.worldData, this.getPlayerList().getSingleplayerData()); -+ // CraftBukkit start - moved to ServerLevel#save -+ // this.worldData.setCustomBossEvents(this.getCustomBossEvents().save(this.registryAccess())); -+ // this.storageSource.saveDataTag(this.registryAccess(), this.worldData, this.getPlayerList().getSingleplayerData()); -+ // CraftBukkit end - if (flush) { - for (ServerLevel serverLevel : this.getAllLevels()) { - LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", serverLevel.getChunkSource().chunkMap.getStorageName()); -@@ -653,19 +_,49 @@ +@@ -649,19 +_,49 @@ this.stopServer(); } @@ -561,14 +578,15 @@ + } + // CraftBukkit end + - public void stopServer() { + protected void stopServer() { ++ if (Thread.currentThread() == this.serverThread) this.executeAllRecentInternalTasks(); // Paper - execute tasks on stop + // CraftBukkit start - prevent double stopping on multiple threads -+ synchronized(this.stopLock) { ++ synchronized (this.stopLock) { + if (this.hasStopped) return; + this.hasStopped = true; + } -+ if (!hasLoggedStop && isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging -+ shutdownThread = Thread.currentThread(); // Paper - Improved watchdog support ++ if (!this.hasLoggedStop && this.isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging ++ this.shutdownThread = Thread.currentThread(); // Paper - Improved watchdog support + org.spigotmc.WatchdogThread.doStop(); // Paper - Improved watchdog support + // CraftBukkit end this.packetProcessor.close(); @@ -585,7 +603,6 @@ + this.server.waitForAsyncTasksShutdown(); // Paper - Wait for Async Tasks during shutdown + } + // CraftBukkit end -+ if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.shutdown(); // Paper - Plugin remapping this.getConnection().stop(); this.isSaving = true; if (this.playerList != null) { @@ -597,7 +614,7 @@ } LOGGER.info("Saving worlds"); -@@ -707,6 +_,20 @@ +@@ -704,6 +_,20 @@ } catch (IOException var4) { LOGGER.error("Failed to unlock level {}", this.storageSource.getLevelId(), var4); } @@ -618,22 +635,22 @@ } public String getLocalIp() { -@@ -722,6 +_,14 @@ +@@ -719,6 +_,14 @@ } - public void halt(boolean waitForShutdown) { + public void halt(final boolean wait) { + // Paper start - allow passing of the intent to restart -+ this.safeShutdown(waitForShutdown, false); ++ this.safeShutdown(wait, false); + } -+ public void safeShutdown(boolean waitForShutdown, boolean isRestarting) { ++ public void safeShutdown(final boolean wait, final boolean isRestarting) { + this.isRestarting = isRestarting; + this.hasLoggedStop = true; // Paper - Debugging -+ if (isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging ++ if (this.isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging + // Paper end this.running = false; - if (waitForShutdown) { + if (wait) { try { -@@ -732,6 +_,118 @@ +@@ -729,6 +_,122 @@ } } @@ -684,8 +701,13 @@ + profiler.push("moonrise:run_all_tasks"); + + profiler.push("moonrise:run_all_server"); -+ // avoid calling MinecraftServer#pollTask - we just want to execute queued tasks -+ while (super.pollTask()) { ++ // avoid calling pollTask - we just want to execute queued tasks ++ final int currentTick = this.tickTaskTickCount.incrementAndGet(); ++ final java.util.function.Predicate taskPredicate = (final TickTask task) -> { ++ // only run tasks scheduled before the current tick - which we incremented above ++ return currentTick - task.getTick() > 0; // currentTick > tick accounting for overflow ++ }; ++ while (this.runTaskIf(taskPredicate)) { + // execute small amounts of other tasks just in case the number of tasks we are + // draining is large - chunk system and packet processing may be latency sensitive + @@ -722,7 +744,6 @@ + } + + // execute tasks while there are tasks and there is time left -+ // note: we do not need to bypass the task execution check here (like managedBlock) since it checks time + while (this.pollTask() && (Util.getNanos() - deadline < 0L)); + + final long now = Util.getNanos(); @@ -752,7 +773,7 @@ protected void runServer() { try { if (!this.initServer()) { -@@ -739,26 +_,73 @@ +@@ -736,30 +_,73 @@ } this.nextTickTimeNanos = Util.getNanos(); @@ -791,70 +812,74 @@ + // Paper end - Improve outdated version checking + while (this.running) { -- long l; +- long thisTickNanos; + final long tickStart = System.nanoTime(); // Paper - improve tick loop -+ long l; // Paper - improve tick loop - diff on change, expect this to be tick interval ++ long thisTickNanos; // Paper - improve tick loop - diff on change, expect this to be tick interval if (!this.isPaused() && this.tickRateManager.isSprinting() && this.tickRateManager.checkShouldSprintThisTick()) { - l = 0L; + thisTickNanos = 0L; - this.nextTickTimeNanos = Util.getNanos(); - this.lastOverloadWarningNanos = this.nextTickTimeNanos; -+ this.tickSchedule.setNextPeriod(tickStart, l); // Paper - improve tick loop ++ this.tickSchedule.setNextPeriod(tickStart, thisTickNanos); // Paper - improve tick loop } else { - l = this.tickRateManager.nanosecondsPerTick(); -- long l1 = Util.getNanos() - this.nextTickTimeNanos; -- if (l1 > OVERLOADED_THRESHOLD_NANOS + 20L * l -- && this.nextTickTimeNanos - this.lastOverloadWarningNanos >= OVERLOADED_WARNING_INTERVAL_NANOS + 100L * l) { -- long l2 = l1 / l; -- LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", l1 / TimeUtil.NANOSECONDS_PER_MILLISECOND, l2); -- this.nextTickTimeNanos += l2 * l; + thisTickNanos = this.tickRateManager.nanosecondsPerTick(); +- long behindTimeNanos = Util.getNanos() - this.nextTickTimeNanos; +- if (behindTimeNanos > OVERLOADED_THRESHOLD_NANOS + 20L * thisTickNanos +- && this.nextTickTimeNanos - this.lastOverloadWarningNanos >= OVERLOADED_WARNING_INTERVAL_NANOS + 100L * thisTickNanos) { +- long ticks = behindTimeNanos / thisTickNanos; +- LOGGER.warn( +- "Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", +- behindTimeNanos / TimeUtil.NANOSECONDS_PER_MILLISECOND, +- ticks +- ); +- this.nextTickTimeNanos += ticks * thisTickNanos; - this.lastOverloadWarningNanos = this.nextTickTimeNanos; + // Paper start - improve tick loop + // handle catchup logic -+ final long ticksBehind = Math.max(1L, this.tickSchedule.getPeriodsAhead(l, tickStart)); ++ final long ticksBehind = Math.max(1L, this.tickSchedule.getPeriodsAhead(thisTickNanos, tickStart)); + final long catchup = (long)Math.max( + 1, + 5 //ConfigHolder.getConfig().tickLoop.catchupTicks.getOrDefault(MoonriseConfig.TickLoop.DEFAULT_CATCHUP_TICKS).intValue() + ); + + // adjust ticksBehind so that it is not greater-than catchup -+ if (ticksBehind > catchup) { ++ if (ticksBehind - catchup > 0L) { + final long difference = ticksBehind - catchup; -+ this.tickSchedule.advanceBy(difference, l); ++ this.tickSchedule.advanceBy(difference, thisTickNanos); } + + // start next tick -+ this.tickSchedule.advanceBy(1L, l); ++ this.tickSchedule.advanceBy(1L, thisTickNanos); + // Paper end - improve tick loop } + -+ this.nextTickTimeNanos = this.tickSchedule.getDeadline(l); ++ this.nextTickTimeNanos = this.tickSchedule.getDeadline(thisTickNanos); + this.lastOverloadWarningNanos = this.nextTickTimeNanos; + + this.currentTickStart = tickStart; + ++MinecraftServer.currentTick; + // Paper end - improve tick loop - boolean flag = l == 0L; + boolean sprinting = thisTickNanos == 0L; if (this.debugCommandProfilerDelayStart) { -@@ -766,7 +_,7 @@ +@@ -767,7 +_,7 @@ this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount); } -- this.nextTickTimeNanos += l; +- this.nextTickTimeNanos += thisTickNanos; + // Paper - improve tick loop - done above - try (Profiler.Scope scope = Profiler.use(this.createProfiler())) { - this.processPacketsAndTick(flag); -@@ -775,7 +_,7 @@ + try (Profiler.Scope ignored = Profiler.use(this.createProfiler())) { + this.processPacketsAndTick(sprinting); +@@ -776,7 +_,7 @@ this.mayHaveDelayedTasks = true; - this.delayedTasksMaxNextTickTimeNanos = Math.max(Util.getNanos() + l, this.nextTickTimeNanos); + this.delayedTasksMaxNextTickTimeNanos = Math.max(Util.getNanos() + thisTickNanos, this.nextTickTimeNanos); this.startMeasuringTaskExecutionTime(); - this.waitUntilNextTick(); + this.recordTaskExecutionTimeWhileWaiting(); // Paper - improve tick loop - record task execution here on MSPT this.finishMeasuringTaskExecutionTime(); - if (flag) { + if (sprinting) { this.tickRateManager.endTickWork(); -@@ -809,7 +_,7 @@ +@@ -810,7 +_,7 @@ } catch (Throwable var64) { LOGGER.error("Exception stopping the server", var64); } finally { @@ -863,7 +888,7 @@ } } } -@@ -861,7 +_,14 @@ +@@ -862,7 +_,14 @@ } private boolean haveTime() { @@ -878,8 +903,8 @@ + // CraftBukkit end } - public static boolean throwIfFatalException() { -@@ -887,11 +_,11 @@ + public NotificationManager notificationManager() { +@@ -870,11 +_,11 @@ } protected void waitUntilNextTick() { @@ -893,40 +918,57 @@ } finally { this.waitingForNextTick = false; } -@@ -910,17 +_,23 @@ +@@ -892,18 +_,38 @@ + } @Override - public TickTask wrapRunnable(Runnable runnable) { -+ // Paper start - anything that does try to post to main during watchdog crash, run on watchdog -+ if (this.hasStopped && Thread.currentThread().equals(shutdownThread)) { +- public TickTask wrapRunnable(final Runnable runnable) { +- return new TickTask(this.tickCount, runnable); ++ // Paper start - anything that does try to post to main during watchdog crash, run on watchdog ++ public TickTask wrapRunnable(Runnable runnable) { ++ if (this.hasStopped && Thread.currentThread().equals(this.shutdownThread)) { + runnable.run(); + runnable = () -> {}; + } -+ // Paper end - return new TickTask(this.tickCount, runnable); ++ // Paper end - anything that does try to post to main during watchdog crash, run on watchdog ++ return new TickTask(this.tickTaskTickCount.get(), runnable); // Paper - use different tick field for tick tasks (see #shouldRun) } @Override - protected boolean shouldRun(TickTask runnable) { -- return runnable.getTick() + 3 < this.tickCount || this.haveTime(); -+ return runnable.getTick() + 1 < this.tickCount || this.haveTime(); // Paper - improve tick loop - do not stall queued tasks + protected boolean shouldRun(final TickTask task) { +- return task.getTick() + 3 < this.tickCount || this.haveTime(); ++ // Paper start - improve tick loop - do not stall queued tasks ++ // note: make this overflow safe as well ++ return this.tickTaskTickCount.getPlain() - task.getTick() > 0 || ++ /* ++ * Ensure that we execute any task as long as we are waiting for the next tick. ++ * The Vanilla server will use managedBlock when awaiting the next tick, but ++ * we do not. The Vanilla managedBlock function will bypass task execution ++ * checks, and in order to ensure we execute tasks like Vanilla we need to also ++ * bypass task execution checks. ++ * This fixes {@link #recordTaskExecutionTimeWhileWaiting} not executing tasks ++ * that it should be executing. ++ */ ++ this.waitingForNextTick || ++ this.haveTime(); ++ // Paper end - improve tick loop - do not stall queued tasks } @Override - public boolean pollTask() { -- boolean flag = this.pollTaskInternal(); -+ boolean flag = this.packetProcessor.executeSinglePacket() | this.pollTaskInternal(); // Paper - improve tick loop - process packets while waiting inbetween ticks - this.mayHaveDelayedTasks = flag; - return flag; + protected boolean pollTask() { +- boolean mayHaveMoreTasks = this.pollTaskInternal(); ++ boolean mayHaveMoreTasks = this.packetProcessor.executeSinglePacket() | this.pollTaskInternal(); // Paper - improve tick loop - process packets while waiting inbetween ticks + this.mayHaveDelayedTasks = mayHaveMoreTasks; + return mayHaveMoreTasks; } -@@ -929,15 +_,16 @@ +@@ -912,15 +_,16 @@ if (super.pollTask()) { return true; } else { + boolean ret = false; // Paper - force execution of all worlds, do not just bias the first if (this.tickRateManager.isSprinting() || this.shouldRunAllTasks() || this.haveTime()) { - for (ServerLevel serverLevel : this.getAllLevels()) { - if (serverLevel.getChunkSource().pollTask()) { + for (ServerLevel level : this.getAllLevels()) { + if (level.getChunkSource().pollTask()) { - return true; + ret = true; // Paper - force execution of all worlds, do not just bias the first } @@ -938,15 +980,15 @@ } } -@@ -990,26 +_,44 @@ +@@ -973,26 +_,44 @@ } - public void tickServer(BooleanSupplier hasTimeLeft) { + protected void tickServer(final BooleanSupplier haveTime) { + org.spigotmc.WatchdogThread.tick(); // Spigot - long nanos = Util.getNanos(); - int i = this.pauseWhenEmptySeconds() * 20; + long nano = Util.getNanos(); + int emptyTickThreshold = this.pauseWhenEmptySeconds() * 20; + this.removeDisabledPluginsBlockingSleep(); // Paper - API to allow/disallow tick sleeping - if (i > 0) { + if (emptyTickThreshold > 0) { - if (this.playerList.getPlayerCount() == 0 && !this.tickRateManager.isSprinting()) { + if (this.playerList.getPlayerCount() == 0 && !this.tickRateManager.isSprinting() && this.pluginsBlockingSleep.isEmpty()) { // Paper - API to allow/disallow tick sleeping this.emptyTicks++; @@ -954,9 +996,9 @@ this.emptyTicks = 0; } - if (this.emptyTicks >= i) { + if (this.emptyTicks >= emptyTickThreshold) { + this.server.spark.tickStart(); // Paper - spark - if (this.emptyTicks == i) { + if (this.emptyTicks == emptyTickThreshold) { LOGGER.info("Server empty for {} seconds, pausing", this.pauseWhenEmptySeconds()); this.autoSave(); } @@ -983,8 +1025,8 @@ + new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.tickCount+1).callEvent(); // Paper - Server Tick Events this.tickCount++; this.tickRateManager.tick(); - this.tickChildren(hasTimeLeft); -@@ -1019,11 +_,18 @@ + this.tickChildren(haveTime); +@@ -1002,11 +_,18 @@ } this.ticksUntilAutosave--; @@ -993,7 +1035,7 @@ this.autoSave(); } - ProfilerFiller profilerFiller = Profiler.get(); + ProfilerFiller profiler = Profiler.get(); + this.server.spark.executeMainThreadTasks(); // Paper - spark + // Paper start - Server Tick Events + long endTime = System.nanoTime(); @@ -1001,31 +1043,31 @@ + new com.destroystokyo.paper.event.server.ServerTickEndEvent(this.tickCount, ((double)(endTime - this.currentTickStart) / 1000000D), remaining).callEvent(); + // Paper end - Server Tick Events + this.server.spark.tickEnd(((double)(endTime - this.currentTickStart) / 1000000D)); // Paper - spark - profilerFiller.push("tallying"); - long l = Util.getNanos() - nanos; - int i1 = this.tickCount % 100; -@@ -1039,16 +_,16 @@ - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("tick"); + profiler.push("tallying"); + long tickTime = Util.getNanos() - nano; + int tickIndex = this.tickCount % 100; +@@ -1022,16 +_,16 @@ + ProfilerFiller profiler = Profiler.get(); + profiler.push("tick"); this.tickFrame.start(); -- profilerFiller.push("scheduledPacketProcessing"); +- profiler.push("scheduledPacketProcessing"); - this.packetProcessor.processQueuedPackets(); -- profilerFiller.pop(); +- profiler.pop(); + // Paper - improve tick loop - moved into runAllTasksAtTickStart + this.runAllTasksAtTickStart(); // Paper - improve tick loop this.tickServer(sprinting ? () -> false : this::haveTime); this.tickFrame.end(); + this.recordEndOfTick(); // Paper - improve tick loop - profilerFiller.pop(); + profiler.pop(); } private void autoSave() { - this.ticksUntilAutosave = this.computeNextAutosaveInterval(); + this.ticksUntilAutosave = this.autosavePeriod; // CraftBukkit LOGGER.debug("Autosave started"); - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("save"); -@@ -1090,7 +_,7 @@ + ProfilerFiller profiler = Profiler.get(); + profiler.push("save"); +@@ -1073,7 +_,7 @@ private ServerStatus buildServerStatus() { ServerStatus.Players players = this.buildPlayerStatus(); return new ServerStatus( @@ -1034,19 +1076,19 @@ Optional.of(players), Optional.of(ServerStatus.Version.current()), Optional.ofNullable(this.statusIcon), -@@ -1104,7 +_,7 @@ +@@ -1087,7 +_,7 @@ if (this.hidesOnlinePlayers()) { return new ServerStatus.Players(maxPlayers, players.size(), List.of()); } else { -- int min = Math.min(players.size(), 12); -+ int min = Math.min(players.size(), org.spigotmc.SpigotConfig.playerSample); // Paper - PaperServerListPingEvent - ObjectArrayList list = new ObjectArrayList<>(min); - int randomInt = Mth.nextInt(this.random, 0, players.size() - min); - -@@ -1121,18 +_,77 @@ - protected void tickChildren(BooleanSupplier hasTimeLeft) { - ProfilerFiller profilerFiller = Profiler.get(); - this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing()); +- int sampleSize = Math.min(players.size(), 12); ++ int sampleSize = Math.min(players.size(), org.spigotmc.SpigotConfig.playerSample); // Paper - PaperServerListPingEvent + ObjectArrayList sample = new ObjectArrayList<>(sampleSize); + int offset = Mth.nextInt(this.random, 0, players.size() - sampleSize); + +@@ -1104,6 +_,32 @@ + protected void tickChildren(final BooleanSupplier haveTime) { + ProfilerFiller profiler = Profiler.get(); + this.getPlayerList().getPlayers().forEach(playerx -> playerx.connection.suspendFlushing()); + this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit + // Paper start - Folia scheduler API + ((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler) org.bukkit.Bukkit.getGlobalRegionScheduler()).tick(); @@ -1073,80 +1115,65 @@ + // Paper end - Folia scheduler API + 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 - profilerFiller.push("commandFunctions"); + profiler.push("commandFunctions"); this.getFunctions().tick(); - profilerFiller.popPush("levels"); - this.updateEffectiveRespawnData(); - + profiler.pop(); +@@ -1115,14 +_,28 @@ + + if (this.tickCount % 20 == 0) { + profiler.push("timeSync"); +- this.forceGameTimeSynchronization(); ++ // Paper start - per-world game time ++ for (final ServerLevel level : this.getAllLevels()) { ++ this.playerList.broadcastAll(new ClientboundSetTimePacket(level.getGameTime(), Map.of()), level); ++ } ++ // Paper end - per-world game time + profiler.pop(); ++ } ++ + // CraftBukkit start + // Run tasks that are waiting on processing + while (!this.processQueue.isEmpty()) { + this.processQueue.remove().run(); -+ } -+ -+ // Send time updates to everyone, it will get the right time from the world the player is in. -+ // Paper start - Perf: Optimize time updates -+ for (final ServerLevel level : this.getAllLevels()) { -+ final boolean doDaylight = level.getGameRules().get(GameRules.ADVANCE_TIME); -+ final long dayTime = level.getDayTime(); -+ long worldTime = level.getGameTime(); -+ final ClientboundSetTimePacket worldPacket = new ClientboundSetTimePacket(worldTime, dayTime, doDaylight); -+ for (Player entityhuman : level.players()) { -+ if (!(entityhuman instanceof ServerPlayer) || (tickCount + entityhuman.getId()) % 20 != 0) { -+ continue; -+ } -+ ServerPlayer entityplayer = (ServerPlayer) entityhuman; -+ long playerTime = entityplayer.getPlayerTime(); -+ boolean relativeTime = entityplayer.relativeTime; -+ ClientboundSetTimePacket packet = ((relativeTime || !doDaylight) && playerTime == dayTime) ? worldPacket : -+ new ClientboundSetTimePacket(worldTime, playerTime, relativeTime && doDaylight); -+ entityplayer.connection.send(packet); // Add support for per player time -+ // Paper end - Perf: Optimize time updates -+ } -+ } -+ -+ this.isIteratingOverLevels = true; // Paper - Throw exception on world create while being ticked - for (ServerLevel serverLevel : this.getAllLevels()) { -+ serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent -+ serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent -+ serverLevel.updateLagCompensationTick(); // Paper - lag compensation - profilerFiller.push(() -> serverLevel + " " + serverLevel.dimension().identifier()); -+ /* Drop global time updates - if (this.tickCount % 20 == 0) { - profilerFiller.push("timeSync"); - this.synchronizeTime(serverLevel); - profilerFiller.pop(); - } -+ // CraftBukkit end */ - - profilerFiller.push("tick"); + } -@@ -1146,7 +_,9 @@ + profiler.push("levels"); + this.updateEffectiveRespawnData(); - profilerFiller.pop(); - profilerFiller.pop(); -+ serverLevel.explosionDensityCache.clear(); // Paper - Optimize explosions ++ this.isIteratingOverLevels = true; // Paper - Throw exception on world create while being ticked + for (ServerLevel level : this.getAllLevels()) { ++ 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 + profiler.push(() -> level + " " + level.dimension().identifier()); + profiler.push("tick"); + +@@ -1136,7 +_,9 @@ + + profiler.pop(); + profiler.pop(); ++ level.explosionDensityCache.clear(); // Paper - Optimize explosions } + this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked - profilerFiller.popPush("connection"); + profiler.popPush("connection"); this.tickConnection(); -@@ -1176,9 +_,12 @@ +@@ -1166,9 +_,12 @@ this.serverActivityMonitor.tick(); } - private void updateEffectiveRespawnData() { - LevelData.RespawnData respawnData = this.worldData.overworldData().getRespawnData(); -+ // Paper start - per world respawn data - read "server global" respawn data from overworld dimension reference ++ // Paper start - per world respawn data + public void updateEffectiveRespawnData() { - ServerLevel serverLevel = this.findRespawnDimension(); -+ LevelData.RespawnData respawnData = serverLevel.serverLevelData.getRespawnData(); -+ respawnData = respawnData.withLevel(serverLevel.dimension()); -+ // Paper end - per world respawn data - read "server global" respawn data from overworld dimension reference - this.effectiveRespawnData = serverLevel.getWorldBorderAdjustedRespawnData(respawnData); + ServerLevel respawnLevel = this.findRespawnDimension(); ++ LevelData.RespawnData respawnData = respawnLevel.serverLevelData.getRespawnData(); ++ this.worldData.overworldData().setSpawn(respawnData); // Sync back to level.dat for Paper->Vanilla spawn integrity ++ // Paper end - per world respawn data + this.effectiveRespawnData = respawnLevel.getWorldBorderAdjustedRespawnData(respawnData); } -@@ -1228,6 +_,22 @@ +@@ -1207,6 +_,22 @@ return this.levels.get(dimension); } @@ -1169,48 +1196,60 @@ public Set> levelKeys() { return this.levels.keySet(); } -@@ -1252,7 +_,7 @@ +@@ -1230,7 +_,7 @@ + } - @DontObfuscate public String getServerModName() { - return "vanilla"; + return io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); // Paper } - public SystemReport fillSystemReport(SystemReport systemReport) { -@@ -1287,7 +_,7 @@ + public ServerClockManager clockManager() { +@@ -1269,7 +_,7 @@ @Override - public void sendSystemMessage(Component message) { + public void sendSystemMessage(final Component message) { - LOGGER.info(message.getString()); + LOGGER.info(io.papermc.paper.adventure.PaperAdventure.ANSI_SERIALIZER.serialize(io.papermc.paper.adventure.PaperAdventure.asAdventure(message))); // Paper - Log message with colors } public KeyPair getKeyPair() { -@@ -1324,11 +_,17 @@ +@@ -1306,11 +_,17 @@ } } -- public void setDifficulty(Difficulty difficulty, boolean forced) { -- if (forced || !this.worldData.isDifficultyLocked()) { +- public void setDifficulty(final Difficulty difficulty, final boolean ignoreLock) { +- if (ignoreLock || !this.worldData.isDifficultyLocked()) { - this.worldData.setDifficulty(this.worldData.isHardcore() ? Difficulty.HARD : difficulty); - this.updateMobSpawningFlags(); - this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate); + // Paper start - per level difficulty, WorldDifficultyChangeEvent -+ public void setDifficulty(ServerLevel level, Difficulty difficulty, @Nullable CommandSourceStack source, boolean forced) { -+ net.minecraft.world.level.storage.PrimaryLevelData worldData = level.serverLevelData; -+ if (forced || !worldData.isDifficultyLocked()) { ++ public void setDifficulty(final ServerLevel level, final Difficulty difficulty, final @Nullable CommandSourceStack source, final boolean ignoreLock) { ++ io.papermc.paper.world.saveddata.PaperLevelOverrides worldData = level.serverLevelData; ++ if (ignoreLock || !worldData.isDifficultyLocked()) { + new io.papermc.paper.event.world.WorldDifficultyChangeEvent( + level.getWorld(), source, org.bukkit.craftbukkit.util.CraftDifficulty.toBukkit(difficulty) + ).callEvent(); + worldData.setDifficulty(worldData.isHardcore() ? Difficulty.HARD : difficulty); + level.setSpawnSettings(level.isSpawningMonsters()); -+ // this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate); ++ level.players().forEach(this::sendDifficultyUpdate); + // Paper end - per level difficulty } } -@@ -1400,10 +_,20 @@ +@@ -1326,6 +_,11 @@ + + public void setDifficultyLocked(final boolean locked) { + this.worldData.setDifficultyLocked(locked); ++ // Paper start - set for all worlds ++ for (final ServerLevel level : this.getAllLevels()) { ++ level.serverLevelData.setDifficultyLocked(locked); ++ } ++ // Paper end - set for all worlds + this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate); + } + +@@ -1382,10 +_,20 @@ @Override public String getMotd() { @@ -1218,7 +1257,7 @@ + return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(this.motd); // Paper - Adventure } - public void setMotd(String motd) { + public void setMotd(final String motd) { + // Paper start - Adventure + this.motd = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserializeOr(motd, net.kyori.adventure.text.Component.empty()); + } @@ -1227,28 +1266,28 @@ + return this.motd; + } + -+ public void motd(net.kyori.adventure.text.Component motd) { ++ public void motd(final net.kyori.adventure.text.Component motd) { + // Paper end - Adventure this.motd = motd; } -@@ -1432,9 +_,13 @@ - int i = 0; +@@ -1414,17 +_,20 @@ + int count = 0; - for (ServerPlayer serverPlayer : this.getPlayerList().getPlayers()) { -- if (serverPlayer.setGameMode(gameMode)) { -- i++; + for (ServerPlayer player : this.getPlayerList().getPlayers()) { +- if (player.setGameMode(gameType)) { +- count++; + // Paper start - Expand PlayerGameModeChangeEvent -+ org.bukkit.event.player.PlayerGameModeChangeEvent event = serverPlayer.setGameMode(gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, null); ++ org.bukkit.event.player.PlayerGameModeChangeEvent event = player.setGameMode(gameType, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, null); + if (event == null || event.isCancelled()) { + continue; } -+ i++; ++ count++; + // Paper end - Expand PlayerGameModeChangeEvent } - - return i; -@@ -1442,7 +_,7 @@ +- + return count; + } } public ServerConnectionListener getConnection() { @@ -1257,135 +1296,170 @@ } public boolean isReady() { -@@ -1505,7 +_,7 @@ +@@ -1487,7 +_,7 @@ @Override - public void executeIfPossible(Runnable task) { + public void executeIfPossible(final Runnable command) { if (this.isStopped()) { - throw new RejectedExecutionException("Server already shutting down"); + throw new io.papermc.paper.util.ServerStopRejectedExecutionException("Server already shutting down"); // Paper - do not prematurely disconnect players on stop } else { - super.executeIfPossible(task); + super.executeIfPossible(command); } -@@ -1540,7 +_,14 @@ +@@ -1522,7 +_,16 @@ return this.functionManager; } + // Paper start - Add ServerResourcesReloadedEvent + @Deprecated @io.papermc.paper.annotation.DoNotUse - public CompletableFuture reloadResources(Collection selectedIds) { -+ return this.reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN); + public CompletableFuture reloadResources(final Collection packsToEnable) { ++ return this.reloadResources(packsToEnable, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN); + } + -+ public CompletableFuture reloadResources(Collection selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause cause) { ++ public CompletableFuture reloadResources( ++ final Collection packsToEnable, final io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause cause ++ ) { + // Paper end - Add ServerResourcesReloadedEvent - CompletableFuture completableFuture = CompletableFuture.supplyAsync( - () -> selectedIds.stream().map(this.packRepository::getPack).filter(Objects::nonNull).map(Pack::open).collect(ImmutableList.toImmutableList()), - this -@@ -1548,7 +_,7 @@ + CompletableFuture result = CompletableFuture.supplyAsync( + () -> packsToEnable.stream() + .map(this.packRepository::getPack) +@@ -1534,7 +_,7 @@ .thenCompose( - list -> { - CloseableResourceManager closeableResourceManager = new MultiPackResourceManager(PackType.SERVER_DATA, list); -- List> list1 = TagLoader.loadTagsForExistingRegistries(closeableResourceManager, this.registries.compositeAccess()); -+ List> list1 = TagLoader.loadTagsForExistingRegistries(closeableResourceManager, this.registries.compositeAccess(), io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // Paper - tag lifecycle - add cause + packsToLoad -> { + CloseableResourceManager resources = new MultiPackResourceManager(PackType.SERVER_DATA, packsToLoad); +- List> postponedTags = TagLoader.loadTagsForExistingRegistries(resources, this.registries.compositeAccess()); ++ List> postponedTags = TagLoader.loadTagsForExistingRegistries(resources, this.registries.compositeAccess(), io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // Paper - tag lifecycle - add cause return ReloadableServerResources.loadResources( - closeableResourceManager, + resources, this.registries, -@@ -1569,20 +_,39 @@ +@@ -1554,18 +_,37 @@ + } ) - .thenAcceptAsync( - reloadableResources -> { -+ io.papermc.paper.command.brigadier.PaperBrigadier.moveBukkitCommands(this.resources.managers().getCommands(), reloadableResources.managers().commands); // Paper - this.resources.close(); - this.resources = reloadableResources; -- this.packRepository.setSelected(selectedIds); -+ this.packRepository.setSelected(selectedIds, false); // Paper - add pendingReload flag to determine required pack loading - false as this is *after* a reload (see above) - WorldDataConfiguration worldDataConfiguration = new WorldDataConfiguration( - getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures() - ); - this.worldData.setDataConfiguration(worldDataConfiguration); - this.resources.managers.updateStaticRegistryTags(); - this.resources.managers.getRecipeManager().finalizeRecipeLoading(this.worldData.enabledFeatures()); -- this.getPlayerList().saveAll(); -+ this.potionBrewing = this.potionBrewing.reload(this.worldData.enabledFeatures()); // Paper - Custom Potion Mixes -+ if (Thread.currentThread() != this.serverThread) return; // Paper -+ // Paper start - we don't need to save everything, just advancements -+ // this.getPlayerList().saveAll(); -+ for (final ServerPlayer player : this.getPlayerList().getPlayers()) { -+ player.getAdvancements().save(); -+ } -+ // Paper end - we don't need to save everything, just advancements - this.getPlayerList().reloadResources(); - this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary()); - this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager); - this.fuelValues = FuelValues.vanillaBurnTimes(this.registries.compositeAccess(), this.worldData.enabledFeatures()); -+ org.bukkit.craftbukkit.block.data.CraftBlockData.reloadCache(); // Paper - cache block data strings; they can be defined by datapacks so refresh it here -+ // Paper start - brigadier command API -+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // reset invalid state for event fire below -+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // call commands event for regular plugins -+ final org.bukkit.craftbukkit.help.SimpleHelpMap helpMap = (org.bukkit.craftbukkit.help.SimpleHelpMap) this.server.getHelpMap(); -+ helpMap.clear(); -+ helpMap.initializeGeneralTopics(); -+ helpMap.initializeCommands(); -+ this.server.syncCommands(); // Refresh commands after event -+ // Paper end -+ new io.papermc.paper.event.server.ServerResourcesReloadedEvent(cause).callEvent(); // Paper - Add ServerResourcesReloadedEvent; fire after everything has been reloaded - }, - this - ); -@@ -1599,7 +_,7 @@ + .thenAcceptAsync(newResources -> { ++ io.papermc.paper.command.brigadier.PaperBrigadier.moveBukkitCommands(this.resources.managers().getCommands(), newResources.managers().commands); // Paper + this.resources.close(); + this.resources = newResources; +- this.packRepository.setSelected(packsToEnable); ++ this.packRepository.setSelected(packsToEnable, false); // Paper - add pendingReload flag to determine required pack loading - false as this is *after* a reload (see above) + WorldDataConfiguration newConfig = new WorldDataConfiguration(getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures()); + this.worldData.setDataConfiguration(newConfig); + this.resources.managers.updateComponentsAndStaticRegistryTags(); + this.resources.managers.getRecipeManager().finalizeRecipeLoading(this.worldData.enabledFeatures()); +- this.getPlayerList().saveAll(); ++ this.potionBrewing = this.potionBrewing.reload(this.worldData.enabledFeatures()); // Paper - Custom Potion Mixes ++ if (Thread.currentThread() != this.serverThread) return; // Paper ++ // Paper start - we don't need to save everything, just advancements ++ // this.getPlayerList().saveAll(); ++ for (final ServerPlayer player : this.getPlayerList().getPlayers()) { ++ player.getAdvancements().save(); ++ } ++ // Paper end - we don't need to save everything, just advancements + this.getPlayerList().reloadResources(); + this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary()); + this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager); + this.fuelValues = FuelValues.vanillaBurnTimes(this.registries.compositeAccess(), this.worldData.enabledFeatures()); ++ org.bukkit.craftbukkit.block.data.CraftBlockData.reloadCache(); // Paper - cache block data strings; they can be defined by datapacks so refresh it here ++ // Paper start - brigadier command API ++ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // reset invalid state for event fire below ++ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // call commands event for regular plugins ++ final org.bukkit.craftbukkit.help.SimpleHelpMap helpMap = (org.bukkit.craftbukkit.help.SimpleHelpMap) this.server.getHelpMap(); ++ helpMap.clear(); ++ helpMap.initializeGeneralTopics(); ++ helpMap.initializeCommands(); ++ this.server.syncCommands(); // Refresh commands after event ++ // Paper end ++ new io.papermc.paper.event.server.ServerResourcesReloadedEvent(cause).callEvent(); // Paper - Add ServerResourcesReloadedEvent; fire after everything has been reloaded + }, this); + if (this.isSameThread()) { + this.managedBlock(result::isDone); +@@ -1580,7 +_,7 @@ DataPackConfig dataPackConfig = initialDataConfig.dataPacks(); - FeatureFlagSet featureFlagSet = initMode ? FeatureFlagSet.of() : initialDataConfig.enabledFeatures(); - FeatureFlagSet featureFlagSet1 = initMode ? FeatureFlags.REGISTRY.allFlags() : initialDataConfig.enabledFeatures(); + FeatureFlagSet forcedFeatures = initMode ? FeatureFlagSet.of() : initialDataConfig.enabledFeatures(); + FeatureFlagSet allowedFeatures = initMode ? FeatureFlags.REGISTRY.allFlags() : initialDataConfig.enabledFeatures(); - packRepository.reload(); + packRepository.reload(true); // Paper - will load resource packs if (safeMode) { - return configureRepositoryWithSelection(packRepository, List.of("vanilla"), featureFlagSet, false); + return configureRepositoryWithSelection(packRepository, List.of("vanilla"), forcedFeatures, false); } else { -@@ -1654,7 +_,7 @@ +@@ -1635,7 +_,7 @@ private static WorldDataConfiguration configureRepositoryWithSelection( - PackRepository packRepository, Collection selectedPacks, FeatureFlagSet enabledFeatures, boolean safeMode + final PackRepository packRepository, final Collection selected, final FeatureFlagSet forcedFeatures, final boolean disableInactive ) { -- packRepository.setSelected(selectedPacks); -+ packRepository.setSelected(selectedPacks, true); // Paper - add pendingReload flag to determine required pack loading - before the initial server load - enableForcedFeaturePacks(packRepository, enabledFeatures); - DataPackConfig selectedPacks1 = getSelectedPacks(packRepository, safeMode); - FeatureFlagSet featureFlagSet = packRepository.getRequestedFeatureFlags().join(enabledFeatures); -@@ -1686,7 +_,7 @@ +- packRepository.setSelected(selected); ++ packRepository.setSelected(selected, true); // Paper - add pendingReload flag to determine required pack loading - before the initial server load + enableForcedFeaturePacks(packRepository, forcedFeatures); + DataPackConfig packConfig = getSelectedPacks(packRepository, disableInactive); + FeatureFlagSet packRequestedFeatures = packRepository.getRequestedFeatureFlags().join(forcedFeatures); +@@ -1667,7 +_,7 @@ } } -- packRepository.setSelected(set); -+ packRepository.setSelected(set, true); // Paper - add pendingReload flag to determine required pack loading - before the initial server start +- packRepository.setSelected(selected); ++ packRepository.setSelected(selected, true); // Paper - add pendingReload flag to determine required pack loading - before the initial server start } } -@@ -1703,8 +_,8 @@ +@@ -1684,8 +_,8 @@ UserWhiteList whiteList = playerList.getWhiteList(); - for (ServerPlayer serverPlayer : Lists.newArrayList(playerList.getPlayers())) { -- if (!whiteList.isWhiteListed(serverPlayer.nameAndId())) { -- serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.not_whitelisted")); -+ if (!whiteList.isWhiteListed(serverPlayer.nameAndId()) && !this.getPlayerList().isOp(serverPlayer.nameAndId())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420) -+ serverPlayer.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage), org.bukkit.event.player.PlayerKickEvent.Cause.WHITELIST); // Paper - use configurable message & kick event cause + for (ServerPlayer player : Lists.newArrayList(playerList.getPlayers())) { +- if (!whiteList.isWhiteListed(player.nameAndId())) { +- player.connection.disconnect(Component.translatable("multiplayer.disconnect.not_whitelisted")); ++ if (!whiteList.isWhiteListed(player.nameAndId()) && !this.getPlayerList().isOp(player.nameAndId())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420) ++ player.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage), org.bukkit.event.player.PlayerKickEvent.Cause.WHITELIST); // Paper - use configurable message & kick event cause } } } -@@ -1734,12 +_,12 @@ +@@ -1715,12 +_,12 @@ } public ServerLevel findRespawnDimension() { - LevelData.RespawnData respawnData = this.getWorldData().overworldData().getRespawnData(); -- ResourceKey resourceKey = respawnData.dimension(); -+ ResourceKey resourceKey = ((net.minecraft.world.level.storage.PrimaryLevelData) this.getWorldData().overworldData()).respawnDimension; // Paper - per world respawn data - read "server global" respawn data from overworld dimension reference - ServerLevel level = this.getLevel(resourceKey); - return level != null ? level : this.overworld(); +- ResourceKey respawnDimension = respawnData.dimension(); ++ ResourceKey respawnDimension = ((net.minecraft.world.level.storage.PrimaryLevelData) this.getWorldData().overworldData()).respawnDimension; // Paper - root cross-world respawn dimension selector + ServerLevel respawnLevel = this.getLevel(respawnDimension); + return respawnLevel != null ? respawnLevel : this.overworld(); } + @io.papermc.paper.annotation.DoNotUse @Deprecated(forRemoval = true) // Paper - per world respawn data - set through Level - public void setRespawnData(LevelData.RespawnData respawnData) { - ServerLevelData serverLevelData = this.worldData.overworldData(); - LevelData.RespawnData respawnData1 = serverLevelData.getRespawnData(); -@@ -1941,6 +_,17 @@ + public void setRespawnData(final LevelData.RespawnData respawnData) { + ServerLevelData levelData = this.worldData.overworldData(); + LevelData.RespawnData oldRespawnData = levelData.getRespawnData(); +@@ -1788,17 +_,20 @@ + return this.randomSequences; + } + +- public void setWeatherParameters(final int clearTime, final int rainTime, final boolean raining, final boolean thundering) { +- WeatherData weatherData = this.getWeatherData(); ++ // Paper start - per-level WeatherData ++ public void setWeatherParameters(final ServerLevel level, final int clearTime, final int rainTime, final boolean raining, final boolean thundering) { ++ WeatherData weatherData = level.getWeatherData(); ++ // Paper end - per-level WeatherData + weatherData.setClearWeatherTime(clearTime); + weatherData.setRainTime(rainTime); + weatherData.setThunderTime(rainTime); +- weatherData.setRaining(raining); +- weatherData.setThundering(thundering); ++ weatherData.setRaining(raining, org.bukkit.event.weather.WeatherChangeEvent.Cause.COMMAND); // Paper - per-level WeatherData ++ weatherData.setThundering(thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.COMMAND); // Paper - per-level WeatherData + } + ++ @Deprecated(forRemoval = true) @io.papermc.paper.annotation.DoNotUse // Paper + public WeatherData getWeatherData() { +- return this.weatherData; ++ throw new UnsupportedOperationException("Use ServerLevel.getWeatherData() instead"); // Paper + } + + public boolean isEnforceWhitelist() { +@@ -1889,7 +_,7 @@ + private void dumpGameRules(final Path path) throws IOException { + try (Writer output = Files.newBufferedWriter(path)) { + final List entries = Lists.newArrayList(); +- final GameRules gameRules = this.getGameRules(); ++ final GameRules gameRules = this.overworld().getGameRules(); // Paper - per-level GameRules + gameRules.visitGameRuleTypes(new GameRuleTypeVisitor() { + { + Objects.requireNonNull(MinecraftServer.this); +@@ -1951,6 +_,17 @@ } } @@ -1403,19 +1477,19 @@ private ProfilerFiller createProfiler() { if (this.willStartRecordingMetrics) { this.metricsRecorder = ActiveMetricsRecorder.createStarted( -@@ -2061,16 +_,22 @@ +@@ -2071,16 +_,22 @@ } - public void logChatMessage(Component content, ChatType.Bound boundChatType, @Nullable String header) { -- String string = boundChatType.decorate(content).getString(); + public void logChatMessage(final Component message, final ChatType.Bound chatType, final @Nullable String tag) { +- String decoratedMessage = chatType.decorate(message).getString(); + // Paper start -+ net.kyori.adventure.text.Component string = io.papermc.paper.adventure.PaperAdventure.asAdventure(boundChatType.decorate(content)); - if (header != null) { -- LOGGER.info("[{}] {}", header, string); -+ COMPONENT_LOGGER.info("[{}] {}", header, string); ++ net.kyori.adventure.text.Component decoratedMessage = io.papermc.paper.adventure.PaperAdventure.asAdventure(chatType.decorate(message)); + if (tag != null) { +- LOGGER.info("[{}] {}", tag, decoratedMessage); ++ COMPONENT_LOGGER.info("[{}] {}", tag, decoratedMessage); } else { -- LOGGER.info("{}", string); -+ COMPONENT_LOGGER.info("{}", string); +- LOGGER.info("{}", decoratedMessage); ++ COMPONENT_LOGGER.info("{}", decoratedMessage); + // Paper end } } @@ -1430,7 +1504,7 @@ } public boolean logIPs() { -@@ -2081,8 +_,9 @@ +@@ -2091,8 +_,9 @@ LOGGER.debug("Received custom click action {} with payload {}", id, payload.orElse(null)); } @@ -1440,37 +1514,38 @@ + throw new UnsupportedOperationException(); // Paper - per level load listener } - public boolean setAutoSave(boolean autoSave) { -@@ -2108,12 +_,14 @@ + public boolean setAutoSave(final boolean enable) { +@@ -2118,12 +_,14 @@ return false; } -- public void onGameRuleChanged(GameRule rule, T value) { +- public void onGameRuleChanged(final GameRule rule, final T value) { - this.notificationManager().onGameRuleChanged(rule, value); + // Paper start - per-world game rules -+ public void onGameRuleChanged(ServerLevel serverLevel, GameRule rule, T value) { -+ this.notificationManager().onGameRuleChanged(serverLevel, rule, value); ++ public void onGameRuleChanged(final ServerLevel level, final GameRule rule, final T value) { ++ this.notificationManager().onGameRuleChanged(level, rule, value); + // Paper end - per-world game rules if (rule == GameRules.REDUCED_DEBUG_INFO) { - byte b = (byte)((Boolean)value ? 22 : 23); + byte event = (byte)((Boolean)value ? 22 : 23); -- for (ServerPlayer serverPlayer : this.getPlayerList().getPlayers()) { -+ for (ServerPlayer serverPlayer : serverLevel.players()) { // Paper - per-world game rules - serverPlayer.connection.send(new ClientboundEntityEventPacket(serverPlayer, b)); +- for (ServerPlayer player : this.getPlayerList().getPlayers()) { ++ for (ServerPlayer player : level.players()) { // Paper - per-world game rules + player.connection.send(new ClientboundEntityEventPacket(player, event)); } } else if (rule == GameRules.LIMITED_CRAFTING || rule == GameRules.IMMEDIATE_RESPAWN) { -@@ -2121,18 +_,18 @@ +@@ -2131,19 +_,21 @@ ? ClientboundGameEventPacket.LIMITED_CRAFTING : ClientboundGameEventPacket.IMMEDIATE_RESPAWN; - ClientboundGameEventPacket clientboundGameEventPacket = new ClientboundGameEventPacket(type, (Boolean)value ? 1.0F : 0.0F); -- this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.send(clientboundGameEventPacket)); -+ serverLevel.players().forEach(serverPlayer1 -> serverPlayer1.connection.send(clientboundGameEventPacket)); // Paper - per-world game rules + ClientboundGameEventPacket packet = new ClientboundGameEventPacket(eventType, (Boolean)value ? 1.0F : 0.0F); +- this.getPlayerList().getPlayers().forEach(playerx -> playerx.connection.send(packet)); ++ level.players().forEach(playerx -> playerx.connection.send(packet)); // Paper - per-world game rules } else if (rule == GameRules.LOCATOR_BAR) { -- this.getAllLevels().forEach(serverLevel -> { -+ // this.getAllLevels().forEach(serverLevel -> { // Paper - per-world game rules - ServerWaypointManager waypointManager = serverLevel.getWaypointManager(); +- this.getAllLevels().forEach(level -> { ++ // this.getAllLevels().forEach(level -> { // Paper - per-world game rules + ServerWaypointManager waypointManager = level.getWaypointManager(); ++ waypointManager.locatorBarEnabled = (Boolean) value; // Paper - optimize ServerWaypointManager with locator bar disabled if ((Boolean)value) { - serverLevel.players().forEach(waypointManager::updatePlayer); + level.players().forEach(waypointManager::updatePlayer); } else { waypointManager.breakAllConnections(); } @@ -1478,11 +1553,30 @@ + // }); // Paper - per-world game rules } else if (rule == GameRules.SPAWN_MONSTERS) { - this.updateMobSpawningFlags(); -+ serverLevel.setSpawnSettings(serverLevel.isSpawningMonsters()); // Paper - per-world game rules ++ level.setSpawnSettings(level.isSpawningMonsters()); // Paper - per-world game rules + } else if (rule == GameRules.ADVANCE_TIME) { ++ // TODO - snapshot - time handling + this.getPlayerList().broadcastAll(this.clockManager().createFullSyncPacket()); } } +@@ -2157,12 +_,14 @@ + return this.savedDataStorage; + } + ++ @Deprecated(forRemoval = true) @io.papermc.paper.annotation.DoNotUse + public TimerQueue getScheduledEvents() { +- return this.scheduledEvents; ++ throw new UnsupportedOperationException("Use ServerLevel.getScheduledEvents() instead"); // Paper + } + ++ @Deprecated(forRemoval = true) @io.papermc.paper.annotation.DoNotUse // Paper + public GameRules getGameRules() { +- return this.gameRules; ++ throw new UnsupportedOperationException("Use ServerLevel.getGameRules() instead"); // Paper + } -@@ -2267,4 +_,53 @@ + public boolean acceptsTransfers() { +@@ -2312,4 +_,53 @@ }; } } diff --git a/paper-server/patches/sources/net/minecraft/server/PlayerAdvancements.java.patch b/paper-server/patches/sources/net/minecraft/server/PlayerAdvancements.java.patch index 91cc5562a305..f8391f805185 100644 --- a/paper-server/patches/sources/net/minecraft/server/PlayerAdvancements.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/PlayerAdvancements.java.patch @@ -15,51 +15,51 @@ private final Codec codec; + public final Map, Set>> criterionData = new java.util.IdentityHashMap<>(); // Paper - fix advancement data player leakage - public PlayerAdvancements(DataFixer dataFixer, PlayerList playerList, ServerAdvancementManager manager, Path playerSavePath, ServerPlayer player) { - this.playerList = playerList; -@@ -126,6 +_,7 @@ + public PlayerAdvancements( + final DataFixer dataFixer, final PlayerList playerList, final ServerAdvancementManager manager, final Path playerSavePath, final ServerPlayer player +@@ -128,6 +_,7 @@ } public void save() { + if (org.spigotmc.SpigotConfig.disableAdvancementSaving) return; // Spigot - JsonElement jsonElement = this.codec.encodeStart(JsonOps.INSTANCE, this.asData()).getOrThrow(); + JsonElement json = this.codec.encodeStart(JsonOps.INSTANCE, this.asData()).getOrThrow(); try { -@@ -143,6 +_,7 @@ - data.forEach((path, progress) -> { - AdvancementHolder advancementHolder = advancementManager.get(path); - if (advancementHolder == null) { -+ if (!path.getNamespace().equals(Identifier.DEFAULT_NAMESPACE)) return; // CraftBukkit - LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", path, this.playerSavePath); +@@ -145,6 +_,7 @@ + data.forEach((id, progress) -> { + AdvancementHolder advancement = manager.get(id); + if (advancement == null) { ++ if (!id.getNamespace().equals(Identifier.DEFAULT_NAMESPACE)) return; // CraftBukkit + LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", id, this.playerSavePath); } else { - this.startProgress(advancementHolder, progress); -@@ -167,14 +_,31 @@ - AdvancementProgress orStartProgress = this.getOrStartProgress(advancement); - boolean isDone = orStartProgress.isDone(); - if (orStartProgress.grantProgress(criterionKey)) { + this.startProgress(advancement, progress); +@@ -169,14 +_,31 @@ + AdvancementProgress progress = this.getOrStartProgress(holder); + boolean wasDone = progress.isDone(); + if (progress.grantProgress(criterion)) { + // Paper start - Add PlayerAdvancementCriterionGrantEvent -+ if (!new com.destroystokyo.paper.event.player.PlayerAdvancementCriterionGrantEvent(this.player.getBukkitEntity(), advancement.toBukkit(), criterionKey).callEvent()) { -+ orStartProgress.revokeProgress(criterionKey); ++ if (!new com.destroystokyo.paper.event.player.PlayerAdvancementCriterionGrantEvent(this.player.getBukkitEntity(), holder.toBukkit(), criterion).callEvent()) { ++ progress.revokeProgress(criterion); + return false; + } + // Paper end - Add PlayerAdvancementCriterionGrantEvent - this.unregisterListeners(advancement); - this.progressChanged.add(advancement); - flag = true; - if (!isDone && orStartProgress.isDone()) { + this.unregisterListeners(holder); + this.progressChanged.add(holder); + result = true; + if (!wasDone && progress.isDone()) { + // Paper start - Add Adventure message to PlayerAdvancementDoneEvent -+ final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> { ++ final net.kyori.adventure.text.Component message = holder.value().display().flatMap(info -> { + return java.util.Optional.ofNullable( -+ info.shouldAnnounceChat() ? io.papermc.paper.adventure.PaperAdventure.asAdventure(info.getType().createAnnouncement(advancement, this.player)) : null ++ info.shouldAnnounceChat() ? io.papermc.paper.adventure.PaperAdventure.asAdventure(info.getType().createAnnouncement(holder, this.player)) : null + ); + }).orElse(null); -+ final org.bukkit.event.player.PlayerAdvancementDoneEvent event = new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), advancement.toBukkit(), message); ++ final org.bukkit.event.player.PlayerAdvancementDoneEvent event = new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), holder.toBukkit(), message); + this.player.level().getCraftServer().getPluginManager().callEvent(event); // CraftBukkit + // Paper end - advancement.value().rewards().grant(this.player); - advancement.value().display().ifPresent(displayInfo -> { -- if (displayInfo.shouldAnnounceChat() && this.player.level().getGameRules().get(GameRules.SHOW_ADVANCEMENT_MESSAGES)) { -- this.playerList.broadcastSystemMessage(displayInfo.getType().createAnnouncement(advancement, this.player), false); + holder.value().rewards().grant(this.player); + holder.value().display().ifPresent(display -> { +- if (display.shouldAnnounceChat() && this.player.level().getGameRules().get(GameRules.SHOW_ADVANCEMENT_MESSAGES)) { +- this.playerList.broadcastSystemMessage(display.getType().createAnnouncement(holder, this.player), false); + // Paper start - Add Adventure message to PlayerAdvancementDoneEvent + if (event.message() != null && this.player.level().getGameRules().get(GameRules.SHOW_ADVANCEMENT_MESSAGES)) { + this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); @@ -67,12 +67,12 @@ } }); } -@@ -245,7 +_,7 @@ - public void flushDirty(ServerPlayer player, boolean showAdvancements) { +@@ -247,7 +_,7 @@ + public void flushDirty(final ServerPlayer player, final boolean showAdvancements) { if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !this.progressChanged.isEmpty()) { - Map map = new HashMap<>(); -- Set set = new HashSet<>(); -+ Set set = new java.util.TreeSet<>(java.util.Comparator.comparing(adv -> adv.id().toString())); // Paper - Changed from HashSet to TreeSet ordered alphabetically. - Set set1 = new HashSet<>(); + Map progress = new HashMap<>(); +- Set added = new HashSet<>(); ++ Set added = new java.util.TreeSet<>(java.util.Comparator.comparing(adv -> adv.id().toString())); // Paper - Changed from HashSet to TreeSet ordered alphabetically. + Set removed = new HashSet<>(); - for (AdvancementNode advancementNode : this.rootsToUpdate) { + for (AdvancementNode root : this.rootsToUpdate) { diff --git a/paper-server/patches/sources/net/minecraft/server/ReloadableServerRegistries.java.patch b/paper-server/patches/sources/net/minecraft/server/ReloadableServerRegistries.java.patch index 17d2976357a1..2408760e9830 100644 --- a/paper-server/patches/sources/net/minecraft/server/ReloadableServerRegistries.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/ReloadableServerRegistries.java.patch @@ -1,38 +1,37 @@ --- a/net/minecraft/server/ReloadableServerRegistries.java +++ b/net/minecraft/server/ReloadableServerRegistries.java -@@ -46,8 +_,9 @@ - List> list = TagLoader.buildUpdatedLookups(registryAccess.getAccessForLoading(RegistryLayer.RELOADABLE), postponedTags); - HolderLookup.Provider provider = HolderLookup.Provider.create(list.stream()); - RegistryOps registryOps = provider.createSerializationContext(JsonOps.INSTANCE); -+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(registryOps.lookupProvider); // Paper - List>> list1 = LootDataType.values() -- .map(lootDataType -> scheduleRegistryLoad((LootDataType)lootDataType, registryOps, resourceManager, backgroundExecutor)) -+ .map(lootDataType -> scheduleRegistryLoad((LootDataType)lootDataType, registryOps, resourceManager, backgroundExecutor, conversions)) // Paper +@@ -48,8 +_,9 @@ + ); + HolderLookup.Provider loadingContextWithTags = HolderLookup.Provider.create(contextRegistriesWithTags.stream()); + RegistryOps ops = loadingContextWithTags.createSerializationContext(JsonOps.INSTANCE); ++ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(ops.lookupProvider); // Paper + List>> registryLoads = LootDataType.values() +- .map(type -> scheduleRegistryLoad((LootDataType)type, ops, manager, executor)) ++ .map(type -> scheduleRegistryLoad((LootDataType)type, ops, manager, executor, conversions)) // Paper .toList(); - CompletableFuture>> completableFuture = Util.sequence(list1); - return completableFuture.thenApplyAsync( -@@ -56,19 +_,20 @@ + CompletableFuture>> sequence = Util.sequence(registryLoads); + return sequence.thenApplyAsync( +@@ -58,14 +_,20 @@ } - private static CompletableFuture> scheduleRegistryLoad( -- LootDataType lootDataType, RegistryOps ops, ResourceManager resourceManager, Executor backgroundExecutor -+ LootDataType lootDataType, RegistryOps ops, ResourceManager resourceManager, Executor backgroundExecutor, io.papermc.paper.registry.data.util.Conversions conversions // Paper + private static CompletableFuture> scheduleRegistryLoad( +- final LootDataType type, final RegistryOps ops, final ResourceManager manager, final Executor taskExecutor ++ final LootDataType type, final RegistryOps ops, final ResourceManager manager, final Executor taskExecutor, final io.papermc.paper.registry.data.util.Conversions conversions // Paper ) { - return CompletableFuture.supplyAsync( - () -> { - WritableRegistry writableRegistry = new MappedRegistry<>(lootDataType.registryKey(), Lifecycle.experimental()); -+ io.papermc.paper.registry.PaperRegistryAccess.instance().registerReloadableRegistry(writableRegistry); // Paper - register reloadable registry - Map map = new HashMap<>(); - SimpleJsonResourceReloadListener.scanDirectory(resourceManager, lootDataType.registryKey(), ops, lootDataType.codec(), map); - map.forEach( -- (identifier, object) -> writableRegistry.register( -- ResourceKey.create(lootDataType.registryKey(), identifier), (T)object, DEFAULT_REGISTRATION_INFO -+ (identifier, object) -> io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(writableRegistry, // Paper - register with listeners -+ ResourceKey.create(lootDataType.registryKey(), identifier), (T)object, DEFAULT_REGISTRATION_INFO, conversions // Paper - register with listeners - ) - ); -- TagLoader.loadTagsForRegistry(resourceManager, writableRegistry); -+ TagLoader.loadTagsForRegistry(resourceManager, writableRegistry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // Paper - tag life cycle - reload - return writableRegistry; - }, - backgroundExecutor + return CompletableFuture.supplyAsync(() -> { + WritableRegistry registry = new MappedRegistry<>(type.registryKey(), Lifecycle.experimental()); ++ io.papermc.paper.registry.PaperRegistryAccess.instance().registerReloadableRegistry(registry); // Paper - register reloadable registry + Map elements = new HashMap<>(); + SimpleJsonResourceReloadListener.scanDirectory(manager, type.registryKey(), ops, type.codec(), elements); +- elements.forEach((id, element) -> registry.register(ResourceKey.create(type.registryKey(), id), (T)element, DEFAULT_REGISTRATION_INFO)); +- TagLoader.loadTagsForRegistry(manager, registry); ++ // Paper start - register with listeners ++ elements.forEach((id, element) -> { ++ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners( ++ registry, ResourceKey.create(type.registryKey(), id), (T)element, DEFAULT_REGISTRATION_INFO, conversions); ++ }); ++ // Paper end - register with listeners ++ TagLoader.loadTagsForRegistry(manager, registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // Paper - tag life cycle - reload + return registry; + }, taskExecutor); + } diff --git a/paper-server/patches/sources/net/minecraft/server/ReloadableServerResources.java.patch b/paper-server/patches/sources/net/minecraft/server/ReloadableServerResources.java.patch index 37f4eb620c5e..16ad8fec5242 100644 --- a/paper-server/patches/sources/net/minecraft/server/ReloadableServerResources.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/ReloadableServerResources.java.patch @@ -1,28 +1,28 @@ --- a/net/minecraft/server/ReloadableServerResources.java +++ b/net/minecraft/server/ReloadableServerResources.java -@@ -39,7 +_,9 @@ - this.fullRegistryHolder = new ReloadableServerRegistries.Holder(registryAccess.compositeAccess()); +@@ -44,7 +_,9 @@ this.postponedTags = postponedTags; - this.recipes = new RecipeManager(registries); -- this.commands = new Commands(commandSelection, CommandBuildContext.simple(registries, enabledFeatures)); -+ this.commands = new Commands(commandSelection, CommandBuildContext.simple(registries, enabledFeatures), true); // Paper - Brigadier Command API - use modern alias registration -+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setDispatcher(this.commands, CommandBuildContext.simple(registries, enabledFeatures)); // Paper - Brigadier Command API + this.newComponents = newComponents; + this.recipes = new RecipeManager(loadingContext); +- this.commands = new Commands(commandSelection, CommandBuildContext.simple(loadingContext, enabledFeatures)); ++ this.commands = new Commands(commandSelection, CommandBuildContext.simple(loadingContext, enabledFeatures), true); // Paper - Brigadier Command API - use modern alias registration ++ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setDispatcher(this.commands, CommandBuildContext.simple(loadingContext, enabledFeatures)); // Paper - Brigadier Command API + io.papermc.paper.command.PaperCommands.registerCommands(); // Paper - this.advancements = new ServerAdvancementManager(registries); - this.functionLibrary = new ServerFunctionLibrary(permissions, this.commands.getDispatcher()); + this.advancements = new ServerAdvancementManager(loadingContext); + this.functionLibrary = new ServerFunctionLibrary(functionCompilationPermissions, this.commands.getDispatcher()); } -@@ -84,6 +_,14 @@ - ReloadableServerResources reloadableServerResources = new ReloadableServerResources( - loadResult.layers(), loadResult.lookupWithUpdatedTags(), enabledFeatures, commandSelection, postponedTags, permissions - ); -+ // Paper start - call commands event for bootstraps -+ //noinspection ConstantValue -+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent( -+ io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, -+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, -+ io.papermc.paper.plugin.bootstrap.BootstrapContext.class, -+ MinecraftServer.getServer() == null ? io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL : io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); -+ // Paper end - call commands event - return SimpleReloadInstance.create( - resourceManager, - reloadableServerResources.listeners(), +@@ -99,6 +_,14 @@ + functionCompilationPermissions, + (List>)pendingComponents + ); ++ // Paper start - call commands event for bootstraps ++ //noinspection ConstantValue ++ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent( ++ io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, ++ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, ++ io.papermc.paper.plugin.bootstrap.BootstrapContext.class, ++ MinecraftServer.getServer() == null ? io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL : io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); ++ // Paper end - call commands event + return SimpleReloadInstance.create( + resourceManager, + result.listeners(), diff --git a/paper-server/patches/sources/net/minecraft/server/ServerAdvancementManager.java.patch b/paper-server/patches/sources/net/minecraft/server/ServerAdvancementManager.java.patch index 3f0820b171c8..a31bc2aeb710 100644 --- a/paper-server/patches/sources/net/minecraft/server/ServerAdvancementManager.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/ServerAdvancementManager.java.patch @@ -10,22 +10,22 @@ private final HolderLookup.Provider registries; @@ -35,12 +_,18 @@ - protected void apply(Map object, ResourceManager resourceManager, ProfilerFiller profiler) { + protected void apply(final Map preparations, final ResourceManager manager, final ProfilerFiller profiler) { Builder builder = ImmutableMap.builder(); - object.forEach((identifier, advancement) -> { + preparations.forEach((id, advancement) -> { + // Spigot start -+ if (org.spigotmc.SpigotConfig.disabledAdvancements != null && (org.spigotmc.SpigotConfig.disabledAdvancements.contains("*") || org.spigotmc.SpigotConfig.disabledAdvancements.contains(identifier.toString()) || org.spigotmc.SpigotConfig.disabledAdvancements.contains(identifier.getNamespace()))) { ++ if (org.spigotmc.SpigotConfig.disabledAdvancements != null && (org.spigotmc.SpigotConfig.disabledAdvancements.contains("*") || org.spigotmc.SpigotConfig.disabledAdvancements.contains(id.toString()) || org.spigotmc.SpigotConfig.disabledAdvancements.contains(id.getNamespace()))) { + return; + } + // Spigot end - this.validate(identifier, advancement); - builder.put(identifier, new AdvancementHolder(identifier, advancement)); + this.validate(id, advancement); + builder.put(id, new AdvancementHolder(id, advancement)); }); - this.advancements = builder.buildOrThrow(); + this.advancements = new java.util.HashMap<>(builder.buildOrThrow()); // CraftBukkit - SPIGOT-7734: mutable - AdvancementTree advancementTree = new AdvancementTree(); - advancementTree.addAll(this.advancements.values()); -+ LOGGER.info("Loaded {} advancements", advancementTree.nodes().size()); // Paper - Improve logging and errors; moved from AdvancementTree#addAll + AdvancementTree tree = new AdvancementTree(); + tree.addAll(this.advancements.values()); ++ LOGGER.info("Loaded {} advancements", tree.nodes().size()); // Paper - Improve logging and errors; moved from AdvancementTree#addAll - for (AdvancementNode advancementNode : advancementTree.roots()) { - if (advancementNode.holder().value().display().isPresent()) { + for (AdvancementNode root : tree.roots()) { + if (root.holder().value().display().isPresent()) { diff --git a/paper-server/patches/sources/net/minecraft/server/ServerFunctionLibrary.java.patch b/paper-server/patches/sources/net/minecraft/server/ServerFunctionLibrary.java.patch index 5b4c62b4b947..9b7775ebb2f8 100644 --- a/paper-server/patches/sources/net/minecraft/server/ServerFunctionLibrary.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/ServerFunctionLibrary.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/server/ServerFunctionLibrary.java +++ b/net/minecraft/server/ServerFunctionLibrary.java -@@ -108,7 +_,7 @@ +@@ -111,7 +_,7 @@ return null; }).join()); - this.functions = builder.build(); -- this.tags = this.tagsLoader.build((Map>)pair.getFirst()); -+ this.tags = this.tagsLoader.build((Map>)pair.getFirst(), null); // Paper - command function tags are not implemented yet + this.functions = newFunctions.build(); +- this.tags = this.tagsLoader.build((Map>)data.getFirst()); ++ this.tags = this.tagsLoader.build((Map>)data.getFirst(), null); // Paper - command function tags are not implemented yet }, - gameExecutor + reloadExecutor ); diff --git a/paper-server/patches/sources/net/minecraft/server/ServerScoreboard.java.patch b/paper-server/patches/sources/net/minecraft/server/ServerScoreboard.java.patch index a88e27ddb03e..f515b4a2c308 100644 --- a/paper-server/patches/sources/net/minecraft/server/ServerScoreboard.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/ServerScoreboard.java.patch @@ -1,42 +1,42 @@ --- a/net/minecraft/server/ServerScoreboard.java +++ b/net/minecraft/server/ServerScoreboard.java @@ -51,9 +_,7 @@ - protected void onScoreChanged(ScoreHolder scoreHolder, Objective objective, Score score) { - super.onScoreChanged(scoreHolder, objective, score); + protected void onScoreChanged(final ScoreHolder owner, final Objective objective, final Score score) { + super.onScoreChanged(owner, objective, score); if (this.trackedObjectives.contains(objective)) { - this.server - .getPlayerList() - .broadcastAll( + this.broadcastAll( // CraftBukkit new ClientboundSetScorePacket( - scoreHolder.getScoreboardName(), + owner.getScoreboardName(), objective.getName(), @@ -76,7 +_,7 @@ @Override - public void onPlayerRemoved(ScoreHolder scoreHolder) { - super.onPlayerRemoved(scoreHolder); -- this.server.getPlayerList().broadcastAll(new ClientboundResetScorePacket(scoreHolder.getScoreboardName(), null)); -+ this.broadcastAll(new ClientboundResetScorePacket(scoreHolder.getScoreboardName(), null)); // CraftBukkit + public void onPlayerRemoved(final ScoreHolder player) { + super.onPlayerRemoved(player); +- this.server.getPlayerList().broadcastAll(new ClientboundResetScorePacket(player.getScoreboardName(), null)); ++ this.broadcastAll(new ClientboundResetScorePacket(player.getScoreboardName(), null)); // CraftBukkit this.setDirty(); } @@ -84,7 +_,7 @@ - public void onPlayerScoreRemoved(ScoreHolder scoreHolder, Objective objective) { - super.onPlayerScoreRemoved(scoreHolder, objective); + public void onPlayerScoreRemoved(final ScoreHolder player, final Objective objective) { + super.onPlayerScoreRemoved(player, objective); if (this.trackedObjectives.contains(objective)) { -- this.server.getPlayerList().broadcastAll(new ClientboundResetScorePacket(scoreHolder.getScoreboardName(), objective.getName())); -+ this.broadcastAll(new ClientboundResetScorePacket(scoreHolder.getScoreboardName(), objective.getName())); // CraftBukkit +- this.server.getPlayerList().broadcastAll(new ClientboundResetScorePacket(player.getScoreboardName(), objective.getName())); ++ this.broadcastAll(new ClientboundResetScorePacket(player.getScoreboardName(), objective.getName())); // CraftBukkit } this.setDirty(); @@ -96,7 +_,7 @@ super.setDisplayObjective(slot, objective); - if (displayObjective != objective && displayObjective != null) { - if (this.getObjectiveDisplaySlotCount(displayObjective) > 0) { + if (old != objective && old != null) { + if (this.getObjectiveDisplaySlotCount(old) > 0) { - this.server.getPlayerList().broadcastAll(new ClientboundSetDisplayObjectivePacket(slot, objective)); + this.broadcastAll(new ClientboundSetDisplayObjectivePacket(slot, objective)); // CraftBukkit } else { - this.stopTrackingObjective(displayObjective); + this.stopTrackingObjective(old); } @@ -104,7 +_,7 @@ @@ -49,16 +49,16 @@ } @@ -116,9 +_,7 @@ @Override - public boolean addPlayerToTeam(String playerName, PlayerTeam team) { - if (super.addPlayerToTeam(playerName, team)) { + public boolean addPlayerToTeam(final String player, final PlayerTeam team) { + if (super.addPlayerToTeam(player, team)) { - this.server - .getPlayerList() -- .broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, playerName, ClientboundSetPlayerTeamPacket.Action.ADD)); -+ this.broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, playerName, ClientboundSetPlayerTeamPacket.Action.ADD)); // CraftBukkit - this.updatePlayerWaypoint(playerName); +- .broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, player, ClientboundSetPlayerTeamPacket.Action.ADD)); ++ this.broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, player, ClientboundSetPlayerTeamPacket.Action.ADD)); // CraftBukkit + this.updatePlayerWaypoint(player); this.setDirty(); return true; -@@ -127,16 +_,44 @@ +@@ -127,14 +_,44 @@ } } @@ -82,13 +82,11 @@ + // Paper end - Multiple Entries with Scoreboards + @Override - public void removePlayerFromTeam(String playerName, PlayerTeam team) { - super.removePlayerFromTeam(playerName, team); -- this.server -- .getPlayerList() -- .broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, playerName, ClientboundSetPlayerTeamPacket.Action.REMOVE)); -+ this.broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, playerName, ClientboundSetPlayerTeamPacket.Action.REMOVE)); // CraftBukkit - this.updatePlayerWaypoint(playerName); + public void removePlayerFromTeam(final String player, final PlayerTeam team) { + super.removePlayerFromTeam(player, team); +- this.server.getPlayerList().broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, player, ClientboundSetPlayerTeamPacket.Action.REMOVE)); ++ this.broadcastAll(ClientboundSetPlayerTeamPacket.createPlayerPacket(team, player, ClientboundSetPlayerTeamPacket.Action.REMOVE)); // CraftBukkit + this.updatePlayerWaypoint(player); this.setDirty(); } @@ -104,10 +102,10 @@ + // Paper end - Multiple Entries with Scoreboards + @Override - public void onObjectiveAdded(Objective objective) { + public void onObjectiveAdded(final Objective objective) { super.onObjectiveAdded(objective); -@@ -147,7 +_,7 @@ - public void onObjectiveChanged(Objective objective) { +@@ -145,7 +_,7 @@ + public void onObjectiveChanged(final Objective objective) { super.onObjectiveChanged(objective); if (this.trackedObjectives.contains(objective)) { - this.server.getPlayerList().broadcastAll(new ClientboundSetObjectivePacket(objective, ClientboundSetObjectivePacket.METHOD_CHANGE)); @@ -115,9 +113,9 @@ } this.setDirty(); -@@ -166,14 +_,14 @@ +@@ -164,14 +_,14 @@ @Override - public void onTeamAdded(PlayerTeam team) { + public void onTeamAdded(final PlayerTeam team) { super.onTeamAdded(team); - this.server.getPlayerList().broadcastAll(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true)); + this.broadcastAll(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true)); // CraftBukkit @@ -125,40 +123,40 @@ } @Override - public void onTeamChanged(PlayerTeam team) { + public void onTeamChanged(final PlayerTeam team) { super.onTeamChanged(team); - this.server.getPlayerList().broadcastAll(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, false)); + this.broadcastAll(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, false)); // CraftBukkit this.updateTeamWaypoints(team); this.setDirty(); } -@@ -181,7 +_,7 @@ +@@ -179,7 +_,7 @@ @Override - public void onTeamRemoved(PlayerTeam team) { + public void onTeamRemoved(final PlayerTeam team) { super.onTeamRemoved(team); - this.server.getPlayerList().broadcastAll(ClientboundSetPlayerTeamPacket.createRemovePacket(team)); + this.broadcastAll(ClientboundSetPlayerTeamPacket.createRemovePacket(team)); // CraftBukkit this.updateTeamWaypoints(team); this.setDirty(); } -@@ -226,6 +_,7 @@ - List> startTrackingPackets = this.getStartTrackingPackets(objective); +@@ -220,6 +_,7 @@ + List> packets = this.getStartTrackingPackets(objective); - for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) { -+ if (serverPlayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board - for (Packet packet : startTrackingPackets) { - serverPlayer.connection.send(packet); + for (ServerPlayer player : this.server.getPlayerList().getPlayers()) { ++ if (player.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board + for (Packet packet : packets) { + player.connection.send(packet); } -@@ -251,6 +_,7 @@ - List> stopTrackingPackets = this.getStopTrackingPackets(objective); +@@ -245,6 +_,7 @@ + List> packets = this.getStopTrackingPackets(objective); - for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) { -+ if (serverPlayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board - for (Packet packet : stopTrackingPackets) { - serverPlayer.connection.send(packet); + for (ServerPlayer player : this.server.getPlayerList().getPlayers()) { ++ if (player.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board + for (Packet packet : packets) { + player.connection.send(packet); } -@@ -287,4 +_,13 @@ - .forEach(serverPlayer -> serverLevel.getWaypointManager().remakeConnections(serverPlayer)); +@@ -281,4 +_,13 @@ + .forEach(player -> level.getWaypointManager().remakeConnections(player)); } } + // CraftBukkit start - Send to players diff --git a/paper-server/patches/sources/net/minecraft/server/ServerTickRateManager.java.patch b/paper-server/patches/sources/net/minecraft/server/ServerTickRateManager.java.patch index ca15194f68e6..2c6809bc1715 100644 --- a/paper-server/patches/sources/net/minecraft/server/ServerTickRateManager.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/ServerTickRateManager.java.patch @@ -6,29 +6,31 @@ private final MinecraftServer server; + private boolean silent; // Paper - silence feedback when API requests sprint - public ServerTickRateManager(MinecraftServer server) { + public ServerTickRateManager(final MinecraftServer server) { this.server = server; @@ -68,6 +_,13 @@ } - public boolean requestGameToSprint(int sprintTime) { + public boolean requestGameToSprint(final int time) { + // Paper start - silence feedback when API requests sprint -+ return requestGameToSprint(sprintTime, false); ++ return requestGameToSprint(time, false); + } + -+ public boolean requestGameToSprint(int sprintTime, boolean silent) { ++ public boolean requestGameToSprint(final int time, final boolean silent) { + if (!isSprinting()) this.silent = silent; + // Paper end - silence feedback when API requests sprint - boolean flag = this.remainingSprintTicks > 0L; + boolean interrupted = this.remainingSprintTicks > 0L; this.sprintTimeSpend = 0L; - this.scheduledCurrentSprintTicks = sprintTime; -@@ -84,7 +_,10 @@ - String string = String.format(Locale.ROOT, "%.2f", l == 0L ? this.millisecondsPerTick() : d / l); + this.scheduledCurrentSprintTicks = time; +@@ -86,9 +_,12 @@ + ); this.scheduledCurrentSprintTicks = 0L; this.sprintTimeSpend = 0L; -- this.server.createCommandSourceStack().sendSuccess(() -> Component.translatable("commands.tick.sprint.report", i, string), true); +- this.server + // Paper start - silence feedback when API requests sprint -+ if (!this.silent) this.server.createCommandSourceStack().sendSuccess(() -> Component.translatable("commands.tick.sprint.report", i, string), true); ++ if (!this.silent) this.server + .createCommandSourceStack() + .sendSuccess(() -> Component.translatable("commands.tick.sprint.report", ticksPerSecond, millisecondsPerTick), true); + this.silent = false; + // Paper end - silence feedback when API requests sprint this.remainingSprintTicks = 0L; diff --git a/paper-server/patches/sources/net/minecraft/server/Services.java.patch b/paper-server/patches/sources/net/minecraft/server/Services.java.patch index 7c8740bb9afa..7870efcfc8d8 100644 --- a/paper-server/patches/sources/net/minecraft/server/Services.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/Services.java.patch @@ -4,11 +4,11 @@ GameProfileRepository profileRepository, UserNameToIdResolver nameToIdCache, ProfileResolver profileResolver -+ , @javax.annotation.Nullable PaperServices paper // Paper ++ , @Nullable PaperServices paper // Paper ) { public static final String USERID_CACHE_FILE = "usercache.json"; -- public static Services create(YggdrasilAuthenticationService authenticationService, File profileRepository) { +- public static Services create(final YggdrasilAuthenticationService serviceAccess, final File nameCacheDir) { + // Paper start - add paper configuration files + public record PaperServices( + io.papermc.paper.configuration.PaperConfigurations configurations, @@ -25,23 +25,23 @@ + } + // Paper end - add paper configuration files + -+ public static Services create(YggdrasilAuthenticationService authenticationService, File profileRepository, File userCacheFile, joptsimple.OptionSet optionSet) throws Exception { // Paper - add optionset to load paper config files; add userCacheFile parameter - MinecraftSessionService minecraftSessionService = authenticationService.createMinecraftSessionService(); - GameProfileRepository gameProfileRepository = authenticationService.createProfileRepository(); -- UserNameToIdResolver userNameToIdResolver = new CachedUserNameToIdResolver(gameProfileRepository, new File(profileRepository, "usercache.json")); -- ProfileResolver profileResolver = new ProfileResolver.Cached(minecraftSessionService, userNameToIdResolver); -- return new Services(minecraftSessionService, authenticationService.getServicesKeySet(), gameProfileRepository, userNameToIdResolver, profileResolver); -+ UserNameToIdResolver userNameToIdResolver = new CachedUserNameToIdResolver(gameProfileRepository, userCacheFile); // Paper ++ public static Services create(final YggdrasilAuthenticationService serviceAccess, final File nameCacheDir, final File userCacheFile, final joptsimple.OptionSet options) throws Exception { // Paper - add options to load paper config files; add userCacheFile parameter + MinecraftSessionService sessionService = serviceAccess.createMinecraftSessionService(); + GameProfileRepository profileRepository = serviceAccess.createProfileRepository(); +- UserNameToIdResolver profileCache = new CachedUserNameToIdResolver(profileRepository, new File(nameCacheDir, "usercache.json")); +- ProfileResolver profileResolver = new ProfileResolver.Cached(sessionService, profileCache); +- return new Services(sessionService, serviceAccess.getServicesKeySet(), profileRepository, profileCache, profileResolver); ++ UserNameToIdResolver profileCache = new CachedUserNameToIdResolver(profileRepository, userCacheFile); // Paper + // Paper start - load paper config files from cli options -+ final java.nio.file.Path legacyConfigPath = ((File) optionSet.valueOf("paper-settings")).toPath(); -+ final java.nio.file.Path configDirPath = ((File) optionSet.valueOf("paper-settings-directory")).toPath(); -+ io.papermc.paper.configuration.PaperConfigurations paperConfigurations = io.papermc.paper.configuration.PaperConfigurations.setup(legacyConfigPath, configDirPath, profileRepository.toPath(), (File) optionSet.valueOf("spigot-settings")); ++ final java.nio.file.Path legacyConfigPath = ((File) options.valueOf("paper-settings")).toPath(); ++ final java.nio.file.Path configDirPath = ((File) options.valueOf("paper-settings-directory")).toPath(); ++ io.papermc.paper.configuration.PaperConfigurations paperConfigurations = io.papermc.paper.configuration.PaperConfigurations.setup(legacyConfigPath, configDirPath, nameCacheDir.toPath(), (File) options.valueOf("spigot-settings")); + PaperServices paperServices = new PaperServices( + paperConfigurations, + new io.papermc.paper.profile.PaperFilledProfileCache() + ); -+ ProfileResolver profileResolver = new ProfileResolver.Cached(minecraftSessionService, userNameToIdResolver, paperServices.filledProfileCache()); -+ return new Services(minecraftSessionService, authenticationService.getServicesKeySet(), gameProfileRepository, userNameToIdResolver, profileResolver, paperServices); ++ ProfileResolver profileResolver = new ProfileResolver.Cached(sessionService, profileCache, paperServices.filledProfileCache()); ++ return new Services(sessionService, serviceAccess.getServicesKeySet(), profileRepository, profileCache, profileResolver, paperServices); + // Paper end - load paper config files from cli options } diff --git a/paper-server/patches/sources/net/minecraft/server/WorldLoader.java.patch b/paper-server/patches/sources/net/minecraft/server/WorldLoader.java.patch index 58978e5e9ff9..d4cd12d70873 100644 --- a/paper-server/patches/sources/net/minecraft/server/WorldLoader.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/WorldLoader.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/server/WorldLoader.java +++ b/net/minecraft/server/WorldLoader.java -@@ -38,7 +_,7 @@ - CloseableResourceManager closeableResourceManager = pair.getSecond(); - LayeredRegistryAccess layeredRegistryAccess = RegistryLayer.createRegistryAccess(); - List> list = TagLoader.loadTagsForExistingRegistries( -- closeableResourceManager, layeredRegistryAccess.getLayer(RegistryLayer.STATIC) -+ closeableResourceManager, layeredRegistryAccess.getLayer(RegistryLayer.STATIC), io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL // Paper - tag lifecycle - add cause - ); - RegistryAccess.Frozen accessForLoading = layeredRegistryAccess.getAccessForLoading(RegistryLayer.WORLDGEN); - List> list1 = TagLoader.buildUpdatedLookups(accessForLoading, list); +@@ -35,7 +_,7 @@ + CloseableResourceManager resources = packsAndResourceManager.getSecond(); + LayeredRegistryAccess initialLayers = RegistryLayer.createRegistryAccess(); + List> staticLayerTags = TagLoader.loadTagsForExistingRegistries( +- resources, initialLayers.getLayer(RegistryLayer.STATIC) ++ resources, initialLayers.getLayer(RegistryLayer.STATIC), io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL // Paper - tag lifecycle - add cause + ); + RegistryAccess.Frozen worldgenLoadContext = initialLayers.getAccessForLoading(RegistryLayer.WORLDGEN); + List> worldgenContextRegistries = TagLoader.buildUpdatedLookups(worldgenLoadContext, staticLayerTags); diff --git a/paper-server/patches/sources/net/minecraft/server/bossevents/CustomBossEvent.java.patch b/paper-server/patches/sources/net/minecraft/server/bossevents/CustomBossEvent.java.patch index 511f66f064a7..df4b3371f51a 100644 --- a/paper-server/patches/sources/net/minecraft/server/bossevents/CustomBossEvent.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/bossevents/CustomBossEvent.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/server/bossevents/CustomBossEvent.java +++ b/net/minecraft/server/bossevents/CustomBossEvent.java -@@ -23,6 +_,16 @@ - private final Set players = Sets.newHashSet(); +@@ -24,6 +_,16 @@ private int value; private int max = 100; + private final Runnable dirtyCallback; + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.boss.KeyedBossBar bossBar; ++ private org.bukkit.boss.@org.jspecify.annotations.Nullable KeyedBossBar bossBar; + + public org.bukkit.boss.KeyedBossBar getBukkitEntity() { + if (this.bossBar == null) { @@ -15,5 +15,5 @@ + } + // CraftBukkit end - public CustomBossEvent(Identifier id, Component name) { - super(name, BossEvent.BossBarColor.WHITE, BossEvent.BossBarOverlay.PROGRESS); + public CustomBossEvent(final UUID id, final Identifier customId, final Component name, final Runnable dirtyCallback) { + super(id, name, BossEvent.BossBarColor.WHITE, BossEvent.BossBarOverlay.PROGRESS); diff --git a/paper-server/patches/sources/net/minecraft/server/commands/BanIpCommands.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/BanIpCommands.java.patch index 3a29620e927c..e1adcc14f76f 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/BanIpCommands.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/BanIpCommands.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/server/commands/BanIpCommands.java +++ b/net/minecraft/server/commands/BanIpCommands.java -@@ -68,7 +_,7 @@ +@@ -62,7 +_,7 @@ } - for (ServerPlayer serverPlayer : playersWithAddress) { -- serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.ip_banned")); -+ serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.ip_banned"), org.bukkit.event.player.PlayerKickEvent.Cause.IP_BANNED); // Paper - kick event cause + for (ServerPlayer player : players) { +- player.connection.disconnect(Component.translatable("multiplayer.disconnect.ip_banned")); ++ player.connection.disconnect(Component.translatable("multiplayer.disconnect.ip_banned"), org.bukkit.event.player.PlayerKickEvent.Cause.IP_BANNED); // Paper - kick event cause } - return playersWithAddress.size(); + return players.size(); diff --git a/paper-server/patches/sources/net/minecraft/server/commands/BanPlayerCommands.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/BanPlayerCommands.java.patch index b5141c2f0765..f61d1e26a18b 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/BanPlayerCommands.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/BanPlayerCommands.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/server/commands/BanPlayerCommands.java +++ b/net/minecraft/server/commands/BanPlayerCommands.java -@@ -57,7 +_,7 @@ - ); - ServerPlayer player = source.getServer().getPlayerList().getPlayer(nameAndId.id()); - if (player != null) { -- player.connection.disconnect(Component.translatable("multiplayer.disconnect.banned")); -+ player.connection.disconnect(Component.translatable("multiplayer.disconnect.banned"), org.bukkit.event.player.PlayerKickEvent.Cause.BANNED); // Paper - kick event cause +@@ -47,7 +_,7 @@ + source.sendSuccess(() -> Component.translatable("commands.ban.success", Component.literal(player.name()), entry.getReasonMessage()), true); + ServerPlayer online = source.getServer().getPlayerList().getPlayer(player.id()); + if (online != null) { +- online.connection.disconnect(Component.translatable("multiplayer.disconnect.banned")); ++ online.connection.disconnect(Component.translatable("multiplayer.disconnect.banned"), org.bukkit.event.player.PlayerKickEvent.Cause.BANNED); // Paper - kick event cause } } } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/DeOpCommands.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/DeOpCommands.java.patch index 3b45d545db0d..b3a5b42896d0 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/DeOpCommands.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/DeOpCommands.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/server/commands/DeOpCommands.java +++ b/net/minecraft/server/commands/DeOpCommands.java -@@ -39,7 +_,7 @@ - if (playerList.isOp(nameAndId)) { - playerList.deop(nameAndId); - i++; +@@ -35,7 +_,7 @@ + if (list.isOp(player)) { + list.deop(player); + count++; - source.sendSuccess(() -> Component.translatable("commands.deop.success", players.iterator().next().name()), true); -+ source.sendSuccess(() -> Component.translatable("commands.deop.success", nameAndId.name()), true); // Paper - fixes MC-253721 ++ source.sendSuccess(() -> Component.translatable("commands.deop.success", player.name()), true); // Paper - fixes MC-253721 } } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/DebugCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/DebugCommand.java.patch index 8a3de200eb96..c6335f8a1965 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/DebugCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/DebugCommand.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/commands/DebugCommand.java +++ b/net/minecraft/server/commands/DebugCommand.java -@@ -263,6 +_,13 @@ +@@ -277,6 +_,13 @@ return true; } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/DifficultyCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/DifficultyCommand.java.patch index bacdafe0105a..940dc6091475 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/DifficultyCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/DifficultyCommand.java.patch @@ -2,11 +2,11 @@ +++ b/net/minecraft/server/commands/DifficultyCommand.java @@ -31,10 +_,10 @@ - public static int setDifficulty(CommandSourceStack source, Difficulty difficulty) throws CommandSyntaxException { + public static int setDifficulty(final CommandSourceStack source, final Difficulty difficulty) throws CommandSyntaxException { MinecraftServer server = source.getServer(); - if (server.getWorldData().getDifficulty() == difficulty) { + if (source.getLevel().getDifficulty() == difficulty) { // CraftBukkit - throw ERROR_ALREADY_DIFFICULT.create(difficulty.getKey()); + throw ERROR_ALREADY_SAME_DIFFICULTY.create(difficulty.getSerializedName()); } else { - server.setDifficulty(difficulty, true); + server.setDifficulty(source.getLevel(), difficulty, source, true); // Paper - per level difficulty; don't skip other difficulty-changing logic (fix upstream's fix); WorldDifficultyChangeEvent diff --git a/paper-server/patches/sources/net/minecraft/server/commands/EffectCommands.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/EffectCommands.java.patch index 23d1fde806cd..e82fad73ce0e 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/EffectCommands.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/EffectCommands.java.patch @@ -1,29 +1,29 @@ --- a/net/minecraft/server/commands/EffectCommands.java +++ b/net/minecraft/server/commands/EffectCommands.java -@@ -182,7 +_,7 @@ - for (Entity entity : targets) { +@@ -178,7 +_,7 @@ + for (Entity entity : entities) { if (entity instanceof LivingEntity) { - MobEffectInstance mobEffectInstance = new MobEffectInstance(effect, i1, amplifier, false, showParticles); -- if (((LivingEntity)entity).addEffect(mobEffectInstance, source.getEntity())) { -+ if (((LivingEntity)entity).addEffect(mobEffectInstance, source.getEntity(), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.COMMAND)) { // CraftBukkit - i++; + MobEffectInstance instance = new MobEffectInstance(effectHolder, duration, amplifier, false, particles); +- if (((LivingEntity)entity).addEffect(instance, source.getEntity())) { ++ if (((LivingEntity)entity).addEffect(instance, source.getEntity(), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.COMMAND)) { // CraftBukkit + count++; } } -@@ -212,7 +_,7 @@ - int i = 0; +@@ -208,7 +_,7 @@ + int count = 0; - for (Entity entity : targets) { + for (Entity entity : entities) { - if (entity instanceof LivingEntity && ((LivingEntity)entity).removeAllEffects()) { + if (entity instanceof LivingEntity && ((LivingEntity)entity).removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.COMMAND)) { // CraftBukkit - i++; + count++; } } -@@ -237,7 +_,7 @@ - int i = 0; +@@ -233,7 +_,7 @@ + int count = 0; - for (Entity entity : targets) { -- if (entity instanceof LivingEntity && ((LivingEntity)entity).removeEffect(effect)) { -+ if (entity instanceof LivingEntity && ((LivingEntity)entity).removeEffect(effect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.COMMAND)) { // CraftBukkit - i++; + for (Entity entity : entities) { +- if (entity instanceof LivingEntity && ((LivingEntity)entity).removeEffect(effectHolder)) { ++ if (entity instanceof LivingEntity && ((LivingEntity)entity).removeEffect(effectHolder, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.COMMAND)) { // CraftBukkit + count++; } } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/GameModeCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/GameModeCommand.java.patch index 9ffa66f958ae..0af763405329 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/GameModeCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/GameModeCommand.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/server/commands/GameModeCommand.java +++ b/net/minecraft/server/commands/GameModeCommand.java -@@ -69,9 +_,21 @@ +@@ -63,9 +_,21 @@ } - private static boolean setGameMode(CommandSourceStack source, ServerPlayer player, GameType gameMode) { -- if (player.setGameMode(gameMode)) { + private static boolean setGameMode(final CommandSourceStack source, final ServerPlayer player, final GameType type) { +- if (player.setGameMode(type)) { + // Paper start - Expand PlayerGameModeChangeEvent -+ return setGameMode(source, player, gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.COMMAND); ++ return setGameMode(source, player, type, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.COMMAND); + } + -+ public static boolean setGameMode(CommandSourceStack source, ServerPlayer player, GameType gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause cause) { -+ org.bukkit.event.player.PlayerGameModeChangeEvent event = player.setGameMode(gameMode, cause, null); ++ public static boolean setGameMode(final CommandSourceStack source, final ServerPlayer player, final GameType type, final org.bukkit.event.player.PlayerGameModeChangeEvent.Cause cause) { ++ org.bukkit.event.player.PlayerGameModeChangeEvent event = player.setGameMode(type, cause, null); + if (event != null && !event.isCancelled()) { + // Paper end - Expand PlayerGameModeChangeEvent - logGamemodeChange(source, player, gameMode); + logGamemodeChange(source, player, type); return true; + // Paper start - Expand PlayerGameModeChangeEvent + } else if (event != null && event.cancelMessage() != null) { diff --git a/paper-server/patches/sources/net/minecraft/server/commands/GameRuleCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/GameRuleCommand.java.patch index c308208079fb..aadb635807bc 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/GameRuleCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/GameRuleCommand.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/server/commands/GameRuleCommand.java +++ b/net/minecraft/server/commands/GameRuleCommand.java -@@ -37,8 +_,7 @@ +@@ -34,8 +_,7 @@ - private static int setRule(CommandContext context, GameRule rule) { - CommandSourceStack commandSourceStack = context.getSource(); -- T argument = context.getArgument("value", rule.valueClass()); -- commandSourceStack.getLevel().getGameRules().set(rule, argument, context.getSource().getServer()); -+ T argument = org.bukkit.craftbukkit.event.CraftEventFactory.handleGameRuleSet(rule, context.getArgument("value", rule.valueClass()), commandSourceStack.getLevel(), context.getSource().getBukkitSender()).value(); // Paper - per-world game rules and event - commandSourceStack.sendSuccess(() -> Component.translatable("commands.gamerule.set", rule.id(), rule.serialize(argument)), true); - return rule.getCommandResult(argument); + private static int setRule(final CommandContext context, final GameRule gameRule) { + CommandSourceStack source = context.getSource(); +- T value = context.getArgument("value", gameRule.valueClass()); +- source.getLevel().getGameRules().set(gameRule, value, context.getSource().getServer()); ++ T value = org.bukkit.craftbukkit.event.CraftEventFactory.handleGameRuleSet(gameRule, context.getArgument("value", gameRule.valueClass()), source.getLevel(), source.getBukkitSender()).value(); // Paper - per-world game rules and event + source.sendSuccess(() -> Component.translatable("commands.gamerule.set", gameRule.id(), gameRule.serialize(value)), true); + return gameRule.getCommandResult(value); } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/GiveCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/GiveCommand.java.patch index 8549e57e209f..341dc8af2e35 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/GiveCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/GiveCommand.java.patch @@ -1,29 +1,29 @@ --- a/net/minecraft/server/commands/GiveCommand.java +++ b/net/minecraft/server/commands/GiveCommand.java -@@ -69,7 +_,7 @@ - ItemStack itemStack1 = item.createItemStack(min, false); - boolean flag = serverPlayer.getInventory().add(itemStack1); - if (flag && itemStack1.isEmpty()) { -- ItemEntity itemEntity = serverPlayer.drop(itemStack, false); -+ ItemEntity itemEntity = serverPlayer.drop(itemStack, false, false, false, null); // Paper - do not fire PlayerDropItemEvent for /give command - if (itemEntity != null) { - itemEntity.makeFakeItem(); +@@ -62,7 +_,7 @@ + ItemStack copyToDrop = prototypeItemStack.copyWithCount(size); + boolean added = player.getInventory().add(copyToDrop); + if (added && copyToDrop.isEmpty()) { +- ItemEntity drop = player.drop(prototypeItemStack.copy(), false); ++ ItemEntity drop = player.drop(prototypeItemStack.copy(), false, false, false, null); // Paper - do not fire PlayerDropItemEvent for /give command + if (drop != null) { + drop.makeFakeItem(); } -@@ -87,7 +_,7 @@ +@@ -80,7 +_,7 @@ ); - serverPlayer.containerMenu.broadcastChanges(); + player.containerMenu.broadcastChanges(); } else { -- ItemEntity itemEntity = serverPlayer.drop(itemStack1, false); -+ ItemEntity itemEntity = serverPlayer.drop(itemStack1, false, false, false, null); // Paper - do not fire PlayerDropItemEvent for /give command - if (itemEntity != null) { - itemEntity.setNoPickUpDelay(); - itemEntity.setTarget(serverPlayer.getUUID()); -@@ -102,7 +_,7 @@ - true +- ItemEntity drop = player.drop(copyToDrop, false); ++ ItemEntity drop = player.drop(copyToDrop, false, false, false, null); // Paper - do not fire PlayerDropItemEvent for /give command + if (drop != null) { + drop.setNoPickUpDelay(); + drop.setTarget(player.getUUID()); +@@ -98,7 +_,7 @@ ); } else { -- source.sendSuccess(() -> Component.translatable("commands.give.success.single", count, itemStack.getDisplayName(), targets.size()), true); -+ source.sendSuccess(() -> Component.translatable("commands.give.success.multiple", count, itemStack.getDisplayName(), targets.size()), true); // Paper - MC-151857 - correct translation key + source.sendSuccess( +- () -> Component.translatable("commands.give.success.single", count, prototypeItemStack.getDisplayName(), players.size()), true ++ () -> Component.translatable("commands.give.success.multiple", count, prototypeItemStack.getDisplayName(), players.size()), true // Paper - MC-151857 - correct translation key + ); } - return targets.size(); diff --git a/paper-server/patches/sources/net/minecraft/server/commands/KickCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/KickCommand.java.patch index c40eb5ee9982..6427c3df646f 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/KickCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/KickCommand.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/server/commands/KickCommand.java +++ b/net/minecraft/server/commands/KickCommand.java -@@ -50,7 +_,7 @@ +@@ -42,7 +_,7 @@ - for (ServerPlayer serverPlayer : players) { - if (!source.getServer().isSingleplayerOwner(serverPlayer.nameAndId())) { -- serverPlayer.connection.disconnect(reason); -+ serverPlayer.connection.disconnect(reason, org.bukkit.event.player.PlayerKickEvent.Cause.KICK_COMMAND); // Paper - kick event cause - source.sendSuccess(() -> Component.translatable("commands.kick.success", serverPlayer.getDisplayName(), reason), true); - i++; + for (ServerPlayer player : players) { + if (!source.getServer().isSingleplayerOwner(player.nameAndId())) { +- player.connection.disconnect(reason); ++ player.connection.disconnect(reason, org.bukkit.event.player.PlayerKickEvent.Cause.KICK_COMMAND); // Paper - kick event cause + source.sendSuccess(() -> Component.translatable("commands.kick.success", player.getDisplayName(), reason), true); + count++; } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/ListPlayersCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/ListPlayersCommand.java.patch index feda570539a2..bf0c37218963 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/ListPlayersCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/ListPlayersCommand.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/server/commands/ListPlayersCommand.java @@ -32,7 +_,14 @@ - private static int format(CommandSourceStack source, Function nameExtractor) { + private static int format(final CommandSourceStack source, final Function formatter) { PlayerList playerList = source.getServer().getPlayerList(); - List players = playerList.getPlayers(); + // CraftBukkit start @@ -13,6 +13,6 @@ + } + final List players = playersTemp; + // CraftBukkit end - Component component = ComponentUtils.formatList(players, nameExtractor); - source.sendSuccess(() -> Component.translatable("commands.list.players", players.size(), playerList.getMaxPlayers(), component), false); + Component listComponent = ComponentUtils.formatList(players, formatter); + source.sendSuccess(() -> Component.translatable("commands.list.players", players.size(), playerList.getMaxPlayers(), listComponent), false); return players.size(); diff --git a/paper-server/patches/sources/net/minecraft/server/commands/LocateCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/LocateCommand.java.patch index 697228612e87..eb5a1ba012fb 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/LocateCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/LocateCommand.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/server/commands/LocateCommand.java +++ b/net/minecraft/server/commands/LocateCommand.java -@@ -199,6 +_,6 @@ - private static float dist(int x1, int z1, int x2, int z2) { - int i = x2 - x1; - int i1 = z2 - z1; -- return Mth.sqrt(i * i + i1 * i1); -+ return (float) Math.hypot(i, i1); // Paper - Fix MC-177381 +@@ -191,6 +_,6 @@ + private static float dist(final int x1, final int z1, final int x2, final int z2) { + int dx = x2 - x1; + int dz = z2 - z1; +- return Mth.sqrt(dx * dx + dz * dz); ++ return (float) Math.hypot(dx, dz); // Paper - Fix MC-177381 } } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/PlaceCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/PlaceCommand.java.patch index 2fd0cc8bcb2a..e8f8b0c9f58f 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/PlaceCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/PlaceCommand.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/server/commands/PlaceCommand.java +++ b/net/minecraft/server/commands/PlaceCommand.java -@@ -302,6 +_,7 @@ - if (!structureStart.isValid()) { +@@ -298,6 +_,7 @@ + if (!start.isValid()) { throw ERROR_STRUCTURE_FAILED.create(); } else { -+ structureStart.generationEventCause = org.bukkit.event.world.AsyncStructureGenerateEvent.Cause.COMMAND; // CraftBukkit - set AsyncStructureGenerateEvent.Cause.COMMAND as generation cause - BoundingBox boundingBox = structureStart.getBoundingBox(); - ChunkPos chunkPos = new ChunkPos(SectionPos.blockToSectionCoord(boundingBox.minX()), SectionPos.blockToSectionCoord(boundingBox.minZ())); - ChunkPos chunkPos1 = new ChunkPos(SectionPos.blockToSectionCoord(boundingBox.maxX()), SectionPos.blockToSectionCoord(boundingBox.maxZ())); ++ start.generationEventCause = org.bukkit.event.world.AsyncStructureGenerateEvent.Cause.COMMAND; // CraftBukkit - set AsyncStructureGenerateEvent.Cause.COMMAND as generation cause + BoundingBox boundingBox = start.getBoundingBox(); + ChunkPos chunkMin = new ChunkPos(SectionPos.blockToSectionCoord(boundingBox.minX()), SectionPos.blockToSectionCoord(boundingBox.minZ())); + ChunkPos chunkMax = new ChunkPos(SectionPos.blockToSectionCoord(boundingBox.maxX()), SectionPos.blockToSectionCoord(boundingBox.maxZ())); diff --git a/paper-server/patches/sources/net/minecraft/server/commands/ReloadCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/ReloadCommand.java.patch index a96e3087c595..11a6896a3527 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/ReloadCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/ReloadCommand.java.patch @@ -3,24 +3,24 @@ @@ -16,7 +_,7 @@ private static final Logger LOGGER = LogUtils.getLogger(); - public static void reloadPacks(Collection selectedIds, CommandSourceStack source) { -- source.getServer().reloadResources(selectedIds).exceptionally(throwable -> { -+ source.getServer().reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.COMMAND).exceptionally(throwable -> { // Paper - Add ServerResourcesReloadedEvent + public static void reloadPacks(final Collection selectedPacks, final CommandSourceStack source) { +- source.getServer().reloadResources(selectedPacks).exceptionally(throwable -> { ++ source.getServer().reloadResources(selectedPacks, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.COMMAND).exceptionally(throwable -> { // Paper - Add ServerResourcesReloadedEvent LOGGER.warn("Failed to execute reload", throwable); source.sendFailure(Component.translatable("commands.reload.failure")); return null; @@ -24,7 +_,7 @@ } - private static Collection discoverNewPacks(PackRepository packRepository, WorldData worldData, Collection selectedIds) { + private static Collection discoverNewPacks(final PackRepository packRepository, final WorldData worldData, final Collection currentPacks) { - packRepository.reload(); + packRepository.reload(true); // Paper - will perform a full reload - Collection list = Lists.newArrayList(selectedIds); + Collection selected = Lists.newArrayList(currentPacks); Collection disabled = worldData.getDataConfiguration().dataPacks().getDisabled(); @@ -36,6 +_,16 @@ - return list; + return selected; } + + // CraftBukkit start @@ -33,5 +33,5 @@ + } + // CraftBukkit end - public static void register(CommandDispatcher dispatcher) { - dispatcher.register(Commands.literal("reload").requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS)).executes(commandContext -> { + public static void register(final CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("reload").requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS)).executes(s -> { diff --git a/paper-server/patches/sources/net/minecraft/server/commands/RideCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/RideCommand.java.patch index 1a405cea646d..75ea71d04e42 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/RideCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/RideCommand.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/server/commands/RideCommand.java +++ b/net/minecraft/server/commands/RideCommand.java -@@ -60,7 +_,7 @@ - Entity vehicle1 = target.getVehicle(); - if (vehicle1 != null) { - throw ERROR_ALREADY_RIDING.create(target.getDisplayName(), vehicle1.getDisplayName()); -- } else if (vehicle.getType() == EntityType.PLAYER) { -+ } else if (vehicle.getType() == EntityType.PLAYER && !io.papermc.paper.configuration.GlobalConfiguration.get().commands.rideCommandAllowPlayerAsVehicle) { // Paper - allow player as vehicle +@@ -54,7 +_,7 @@ + Entity currentVehicle = target.getVehicle(); + if (currentVehicle != null) { + throw ERROR_ALREADY_RIDING.create(target.getDisplayName(), currentVehicle.getDisplayName()); +- } else if (vehicle.is(EntityType.PLAYER)) { ++ } else if (vehicle.is(EntityType.PLAYER) && !io.papermc.paper.configuration.GlobalConfiguration.get().commands.rideCommandAllowPlayerAsVehicle) { // Paper - allow player as vehicle throw ERROR_MOUNTING_PLAYER.create(); - } else if (target.getSelfAndPassengers().anyMatch(entity -> entity == vehicle)) { + } else if (target.getSelfAndPassengers().anyMatch(e -> e == vehicle)) { throw ERROR_MOUNTING_LOOP.create(); diff --git a/paper-server/patches/sources/net/minecraft/server/commands/ScheduleCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/ScheduleCommand.java.patch index 1e8073d25ded..ea60bef179a9 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/ScheduleCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/ScheduleCommand.java.patch @@ -3,27 +3,27 @@ @@ -32,7 +_,7 @@ ); private static final SimpleCommandExceptionType ERROR_MACRO = new SimpleCommandExceptionType(Component.translatableEscape("commands.schedule.macro")); - private static final SuggestionProvider SUGGEST_SCHEDULE = (context, builder) -> SharedSuggestionProvider.suggest( -- context.getSource().getServer().getWorldData().overworldData().getScheduledEvents().getEventsIds(), builder -+ context.getSource().getLevel().serverLevelData.getScheduledEvents().getEventsIds(), builder // Paper - Make schedule command per-world + private static final SuggestionProvider SUGGEST_SCHEDULE = (c, p) -> SharedSuggestionProvider.suggest( +- c.getSource().getServer().getScheduledEvents().getEventsIds(), p ++ c.getSource().getLevel().scheduledEvents.getEventsIds(), p // Paper - Make schedule command per-world ); - public static void register(CommandDispatcher dispatcher) { + public static void register(final CommandDispatcher dispatcher) { @@ -101,7 +_,7 @@ } else { - long l = source.getLevel().getGameTime() + time; - Identifier identifier = function.getFirst(); -- TimerQueue scheduledEvents = source.getServer().getWorldData().overworldData().getScheduledEvents(); -+ TimerQueue scheduledEvents = source.getLevel().serverLevelData.overworldData().getScheduledEvents(); // CraftBukkit - SPIGOT-6667: Use world specific function timer - Optional> optional = function.getSecond().left(); - if (optional.isPresent()) { - if (optional.get() instanceof MacroFunction) { -@@ -130,7 +_,7 @@ + long tickTime = source.getLevel().getGameTime() + time; + Identifier callbackId = callback.getFirst(); +- TimerQueue queue = source.getServer().getScheduledEvents(); ++ TimerQueue queue = source.getLevel().scheduledEvents; // CraftBukkit - SPIGOT-6667: Use world specific function timer + Optional> function = callback.getSecond().left(); + if (function.isPresent()) { + if (function.get() instanceof MacroFunction) { +@@ -132,7 +_,7 @@ } - private static int remove(CommandSourceStack source, String function) throws CommandSyntaxException { -- int i = source.getServer().getWorldData().overworldData().getScheduledEvents().remove(function); -+ int i = source.getLevel().serverLevelData.overworldData().getScheduledEvents().remove(function); // Paper - Make schedule command per-world - if (i == 0) { - throw ERROR_CANT_REMOVE.create(function); + private static int remove(final CommandSourceStack source, final String id) throws CommandSyntaxException { +- int count = source.getServer().getScheduledEvents().remove(id); ++ int count = source.getLevel().scheduledEvents.remove(id); // Paper - Make schedule command per-world + if (count == 0) { + throw ERROR_CANT_REMOVE.create(id); } else { diff --git a/paper-server/patches/sources/net/minecraft/server/commands/SetSpawnCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/SetSpawnCommand.java.patch index de35da33ce39..e8bae4a40d90 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/SetSpawnCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/SetSpawnCommand.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/server/commands/SetSpawnCommand.java +++ b/net/minecraft/server/commands/SetSpawnCommand.java -@@ -74,24 +_,34 @@ - float f = Mth.wrapDegrees(rotation.y); - float f1 = Mth.clamp(rotation.x, -90.0F, 90.0F); +@@ -74,12 +_,22 @@ + float yaw = Mth.wrapDegrees(rotationVector.y); + float pitch = Mth.clamp(rotationVector.x, -90.0F, 90.0F); + final Collection actualTargets = new java.util.ArrayList<>(); // Paper - Add PlayerSetSpawnEvent - for (ServerPlayer serverPlayer : targets) { -- serverPlayer.setRespawnPosition(new ServerPlayer.RespawnConfig(LevelData.RespawnData.of(resourceKey, pos, f, f1), true), false); + for (ServerPlayer target : targets) { +- target.setRespawnPosition(new ServerPlayer.RespawnConfig(LevelData.RespawnData.of(dimension, pos, yaw, pitch), true), false); - } + // Paper start - Add PlayerSetSpawnEvent -+ if (serverPlayer.setRespawnPosition(new ServerPlayer.RespawnConfig(LevelData.RespawnData.of(resourceKey, pos, f, f1), true), false, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.COMMAND)) { -+ actualTargets.add(serverPlayer); ++ if (target.setRespawnPosition(new ServerPlayer.RespawnConfig(LevelData.RespawnData.of(dimension, pos, yaw, pitch), true), false, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.COMMAND)) { ++ actualTargets.add(target); + } + // Paper end - Add PlayerSetSpawnEvent + } @@ -20,20 +20,28 @@ + } + // Paper end - Add PlayerSetSpawnEvent - String string = resourceKey.identifier().toString(); + String dimensionName = dimension.identifier().toString(); - if (targets.size() == 1) { + if (actualTargets.size() == 1) { // Paper - Add PlayerSetSpawnEvent source.sendSuccess( () -> Component.translatable( -- "commands.spawnpoint.success.single", pos.getX(), pos.getY(), pos.getZ(), f, f1, string, targets.iterator().next().getDisplayName() -+ "commands.spawnpoint.success.single", pos.getX(), pos.getY(), pos.getZ(), f, f1, string, actualTargets.iterator().next().getDisplayName() // Paper - Add PlayerSetSpawnEvent + "commands.spawnpoint.success.single", +@@ -89,19 +_,19 @@ + yaw, + pitch, + dimensionName, +- targets.iterator().next().getDisplayName() ++ actualTargets.iterator().next().getDisplayName() // Paper - Add PlayerSetSpawnEvent ), true ); } else { source.sendSuccess( -- () -> Component.translatable("commands.spawnpoint.success.multiple", pos.getX(), pos.getY(), pos.getZ(), f, f1, string, targets.size()), true -+ () -> Component.translatable("commands.spawnpoint.success.multiple", pos.getX(), pos.getY(), pos.getZ(), f, f1, string, actualTargets.size()), true // Paper - Add PlayerSetSpawnEvent + () -> Component.translatable( +- "commands.spawnpoint.success.multiple", pos.getX(), pos.getY(), pos.getZ(), yaw, pitch, dimensionName, targets.size() ++ "commands.spawnpoint.success.multiple", pos.getX(), pos.getY(), pos.getZ(), yaw, pitch, dimensionName, actualTargets.size() // Paper - Add PlayerSetSpawnEvent + ), + true ); } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/SpreadPlayersCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/SpreadPlayersCommand.java.patch index a76793b0397b..a1efbaa43e20 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/SpreadPlayersCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/SpreadPlayersCommand.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/server/commands/SpreadPlayersCommand.java +++ b/net/minecraft/server/commands/SpreadPlayersCommand.java -@@ -255,6 +_,7 @@ +@@ -265,6 +_,7 @@ entity.getYRot(), entity.getXRot(), true + , org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.COMMAND // CraftBukkit - handle teleport reason ); - double d1 = Double.MAX_VALUE; + double closest = Double.MAX_VALUE; diff --git a/paper-server/patches/sources/net/minecraft/server/commands/SummonCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/SummonCommand.java.patch index b55632f3d87a..3c6f91342b94 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/SummonCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/SummonCommand.java.patch @@ -1,23 +1,23 @@ --- a/net/minecraft/server/commands/SummonCommand.java +++ b/net/minecraft/server/commands/SummonCommand.java -@@ -80,7 +_,7 @@ +@@ -78,7 +_,7 @@ BlockPos blockPos = BlockPos.containing(pos); if (!Level.isInSpawnableBounds(blockPos)) { throw INVALID_POSITION.create(); - } else if (source.getLevel().getDifficulty() == Difficulty.PEACEFUL && !type.value().isAllowedInPeaceful()) { -+ } else if (source.getLevel().getDifficulty() == Difficulty.PEACEFUL && !type.value().isAllowedInPeaceful(tag)) { // Paper - check peaceful override ++ } else if (source.getLevel().getDifficulty() == Difficulty.PEACEFUL && !type.value().isAllowedInPeaceful(nbt)) { // Paper - check peaceful override throw ERROR_FAILED_PEACEFUL.create(); } else { - CompoundTag compoundTag = tag.copy(); -@@ -88,6 +_,7 @@ + CompoundTag entityTag = nbt.copy(); +@@ -86,6 +_,7 @@ ServerLevel level = source.getLevel(); - Entity entity = EntityType.loadEntityRecursive(compoundTag, level, EntitySpawnReason.COMMAND, entity1 -> { - entity1.snapTo(pos.x, pos.y, pos.z, entity1.getYRot(), entity1.getXRot()); -+ entity1.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.COMMAND; // Paper - Entity#getEntitySpawnReason - return entity1; + Entity entity = EntityType.loadEntityRecursive(entityTag, level, EntitySpawnReason.COMMAND, e -> { + e.snapTo(pos.x, pos.y, pos.z, e.getYRot(), e.getXRot()); ++ e.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.COMMAND; // Paper - Entity#getEntitySpawnReason + return e; }); if (entity == null) { -@@ -97,7 +_,7 @@ +@@ -95,7 +_,7 @@ mob.finalizeSpawn(source.getLevel(), source.getLevel().getCurrentDifficultyAt(entity.blockPosition()), EntitySpawnReason.COMMAND, null); } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/TeleportCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/TeleportCommand.java.patch index 01aa8b939bc4..448fd552bf7f 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/TeleportCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/TeleportCommand.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/server/commands/TeleportCommand.java +++ b/net/minecraft/server/commands/TeleportCommand.java -@@ -263,7 +_,7 @@ - float f1 = relatives.contains(Relative.X_ROT) ? xRot - target.getXRot() : xRot; - float f2 = Mth.wrapDegrees(f); - float f3 = Mth.wrapDegrees(f1); -- if (target.teleportTo(level, d, d1, d2, relatives, f2, f3, true)) { -+ if (target.teleportTo(level, d, d1, d2, relatives, f2, f3, true, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.COMMAND)) { // Paper - teleport cause +@@ -255,7 +_,7 @@ + float relativeOrAbsoluteXRot = relatives.contains(Relative.X_ROT) ? xRot - victim.getXRot() : xRot; + float newYRot = Mth.wrapDegrees(relativeOrAbsoluteYRot); + float newXRot = Mth.wrapDegrees(relativeOrAbsoluteXRot); +- if (victim.teleportTo(level, relativeOrAbsoluteX, relativeOrAbsoluteY, relativeOrAbsoluteZ, relatives, newYRot, newXRot, true)) { ++ if (victim.teleportTo(level, relativeOrAbsoluteX, relativeOrAbsoluteY, relativeOrAbsoluteZ, relatives, newYRot, newXRot, true, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.COMMAND)) { // Paper - teleport cause if (lookAt != null) { - lookAt.perform(source, target); + lookAt.perform(source, victim); } diff --git a/paper-server/patches/sources/net/minecraft/server/commands/TimeCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/TimeCommand.java.patch index efc275401c42..7dbcf005a8e7 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/TimeCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/TimeCommand.java.patch @@ -1,37 +1,67 @@ --- a/net/minecraft/server/commands/TimeCommand.java +++ b/net/minecraft/server/commands/TimeCommand.java -@@ -56,8 +_,15 @@ +@@ -170,23 +_,61 @@ } - public static int setTime(CommandSourceStack source, int time) { -- for (ServerLevel serverLevel : source.getServer().getAllLevels()) { -- serverLevel.setDayTime(time); -+ for (ServerLevel serverLevel : io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels() : java.util.List.of(source.getLevel())) { // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in // Paper - add config option for spigot's change -+ // serverLevel.setDayTime(time); -+ // CraftBukkit start -+ org.bukkit.event.world.TimeSkipEvent event = new org.bukkit.event.world.TimeSkipEvent(serverLevel.getWorld(), org.bukkit.event.world.TimeSkipEvent.SkipReason.COMMAND, time - serverLevel.getDayTime()); -+ org.bukkit.Bukkit.getPluginManager().callEvent(event); -+ if (!event.isCancelled()) { -+ serverLevel.setDayTime(serverLevel.getDayTime() + event.getSkipAmount()); + private static int setTotalTicks(final CommandSourceStack source, final Holder clock, final int totalTicks) { ++ // Paper start ++ // TODO - snapshot - 26.1 ++ for (net.minecraft.server.level.ServerLevel level : io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels() : java.util.List.of(source.getLevel())) { ++ long currentTime = level.clockManager().getTotalTicks(clock); ++ org.bukkit.event.world.TimeSkipEvent event = new org.bukkit.event.world.TimeSkipEvent(level.getWorld(), org.bukkit.event.world.TimeSkipEvent.SkipReason.COMMAND, totalTicks - currentTime); ++ if (event.callEvent()) { ++ level.clockManager().setTotalTicks(clock, currentTime + event.getSkipAmount()); + } -+ // CraftBukkit end - } - - source.getServer().forceTimeSynchronization(); -@@ -66,8 +_,14 @@ ++ } ++ // Paper end + ServerClockManager clockManager = source.getServer().clockManager(); +- clockManager.setTotalTicks(clock, totalTicks); ++ // clockManager.setTotalTicks(clock, totalTicks); + source.sendSuccess(() -> Component.translatable("commands.time.set.absolute", clock.getRegisteredName(), totalTicks), true); + return totalTicks; } - public static int addTime(CommandSourceStack source, int amount) { -- for (ServerLevel serverLevel : source.getServer().getAllLevels()) { -- serverLevel.setDayTime(serverLevel.getDayTime() + amount); -+ for (ServerLevel serverLevel : io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels() : java.util.List.of(source.getLevel())) { // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in // Paper - add config option for spigot's change -+ // CraftBukkit start -+ org.bukkit.event.world.TimeSkipEvent event = new org.bukkit.event.world.TimeSkipEvent(serverLevel.getWorld(), org.bukkit.event.world.TimeSkipEvent.SkipReason.COMMAND, amount); -+ org.bukkit.Bukkit.getPluginManager().callEvent(event); -+ if (!event.isCancelled()) { -+ serverLevel.setDayTime(serverLevel.getDayTime() + event.getSkipAmount()); + private static int addTime(final CommandSourceStack source, final Holder clock, final int time) { ++ // Paper start ++ // TODO - snapshot - 26.1 ++ for (net.minecraft.server.level.ServerLevel level : io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels() : java.util.List.of(source.getLevel())) { ++ org.bukkit.event.world.TimeSkipEvent event = new org.bukkit.event.world.TimeSkipEvent(level.getWorld(), org.bukkit.event.world.TimeSkipEvent.SkipReason.COMMAND, time);; ++ if (event.callEvent()) { ++ level.clockManager().addTicks(clock, event.getSkipAmount()); + } -+ // CraftBukkit end - } ++ } ++ // Paper end + ServerClockManager clockManager = source.getServer().clockManager(); +- clockManager.addTicks(clock, time); ++ // Paper - move up + long totalTicks = clockManager.getTotalTicks(clock); + source.sendSuccess(() -> Component.translatable("commands.time.set.absolute", clock.getRegisteredName(), totalTicks), true); + return wrapTime(totalTicks); + } - source.getServer().forceTimeSynchronization(); + private static int setTimeToTimeMarker(final CommandSourceStack source, final Holder clock, final ResourceKey timeMarkerId) throws CommandSyntaxException { ++ // Paper start ++ // TODO - snapshot - 26.1 ++ boolean foundMarker = false; ++ for (net.minecraft.server.level.ServerLevel level : io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels() : java.util.List.of(source.getLevel())) { ++ java.util.OptionalLong targetTime = level.clockManager().getTotalTicksToTimeMarker(clock, timeMarkerId); ++ if (targetTime.isEmpty()) { ++ continue; ++ } ++ ++ foundMarker = true; ++ long currentTime = level.clockManager().getTotalTicks(clock); ++ long delta = targetTime.getAsLong() - currentTime; ++ org.bukkit.event.world.TimeSkipEvent event = new org.bukkit.event.world.TimeSkipEvent(level.getWorld(), org.bukkit.event.world.TimeSkipEvent.SkipReason.COMMAND, delta); ++ if (event.callEvent()) { ++ level.clockManager().setTotalTicks(clock, currentTime + event.getSkipAmount()); ++ } ++ } ++ // Paper end ++ + ServerClockManager clockManager = source.getServer().clockManager(); +- if (!clockManager.moveToTimeMarker(clock, timeMarkerId)) { ++ if (!foundMarker) { // Paper + throw ERROR_NO_TIME_MARKER_FOUND.create(clock.getRegisteredName(), timeMarkerId); + } else { + source.sendSuccess( diff --git a/paper-server/patches/sources/net/minecraft/server/commands/WaypointCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/WaypointCommand.java.patch index 17c29d6a3d34..a88eca96a9e1 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/WaypointCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/WaypointCommand.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/server/commands/WaypointCommand.java +++ b/net/minecraft/server/commands/WaypointCommand.java -@@ -166,7 +_,7 @@ +@@ -164,7 +_,7 @@ } - private static void mutateIcon(CommandSourceStack source, WaypointTransmitter waypoint, Consumer mutator) { + private static void mutateIcon(final CommandSourceStack source, final WaypointTransmitter waypoint, final Consumer iconConsumer) { - ServerLevel level = source.getLevel(); + ServerLevel level = (waypoint instanceof LivingEntity livingEntity) ? (net.minecraft.server.level.ServerLevel) livingEntity.level() : source.getLevel(); // Paper - MC-300685 use level of waypoint if it's a living entity for broadcast level.getWaypointManager().untrackWaypoint(waypoint); - mutator.accept(waypoint.waypointIcon()); + iconConsumer.accept(waypoint.waypointIcon()); level.getWaypointManager().trackWaypoint(waypoint); diff --git a/paper-server/patches/sources/net/minecraft/server/commands/WeatherCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/WeatherCommand.java.patch index d5d445aa1e96..6907c2cc673c 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/WeatherCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/WeatherCommand.java.patch @@ -1,30 +1,25 @@ --- a/net/minecraft/server/commands/WeatherCommand.java +++ b/net/minecraft/server/commands/WeatherCommand.java -@@ -44,23 +_,23 @@ +@@ -48,19 +_,19 @@ } - private static int getDuration(CommandSourceStack source, int time, IntProvider timeProvider) { -- return time == -1 ? timeProvider.sample(source.getServer().overworld().getRandom()) : time; -+ return time == -1 ? timeProvider.sample(source.getLevel().getRandom()) : time; // CraftBukkit - SPIGOT-7680: per-world - } - - private static int setClear(CommandSourceStack source, int time) { -- source.getServer().overworld().setWeatherParameters(getDuration(source, time, ServerLevel.RAIN_DELAY), 0, false, false); -+ source.getLevel().setWeatherParameters(getDuration(source, time, ServerLevel.RAIN_DELAY), 0, false, false); // CraftBukkit - SPIGOT-7680: per-world + private static int setClear(final CommandSourceStack source, final int duration) { +- source.getServer().setWeatherParameters(getDuration(source, duration, ServerLevel.RAIN_DELAY), 0, false, false); ++ source.getServer().setWeatherParameters(source.getLevel(), getDuration(source, duration, ServerLevel.RAIN_DELAY), 0, false, false); // CraftBukkit - SPIGOT-7680: per-world source.sendSuccess(() -> Component.translatable("commands.weather.set.clear"), true); - return time; + return duration; } - private static int setRain(CommandSourceStack source, int time) { -- source.getServer().overworld().setWeatherParameters(0, getDuration(source, time, ServerLevel.RAIN_DURATION), true, false); -+ source.getLevel().setWeatherParameters(0, getDuration(source, time, ServerLevel.RAIN_DURATION), true, false); // CraftBukkit - SPIGOT-7680: per-world + private static int setRain(final CommandSourceStack source, final int duration) { +- source.getServer().setWeatherParameters(0, getDuration(source, duration, ServerLevel.RAIN_DURATION), true, false); ++ source.getServer().setWeatherParameters(source.getLevel(), 0, getDuration(source, duration, ServerLevel.RAIN_DURATION), true, false); // CraftBukkit - SPIGOT-7680: per-world source.sendSuccess(() -> Component.translatable("commands.weather.set.rain"), true); - return time; + return duration; } - private static int setThunder(CommandSourceStack source, int time) { -- source.getServer().overworld().setWeatherParameters(0, getDuration(source, time, ServerLevel.THUNDER_DURATION), true, true); -+ source.getLevel().setWeatherParameters(0, getDuration(source, time, ServerLevel.THUNDER_DURATION), true, true); // CraftBukkit - SPIGOT-7680: per-world + private static int setThunder(final CommandSourceStack source, final int duration) { +- source.getServer().setWeatherParameters(0, getDuration(source, duration, ServerLevel.THUNDER_DURATION), true, true); ++ source.getServer().setWeatherParameters(source.getLevel(), 0, getDuration(source, duration, ServerLevel.THUNDER_DURATION), true, true); // CraftBukkit - SPIGOT-7680: per-world source.sendSuccess(() -> Component.translatable("commands.weather.set.thunder"), true); - return time; + return duration; } diff --git a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedPlayerList.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedPlayerList.java.patch index e1901d0e5a8b..bf82e77c240c 100644 --- a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedPlayerList.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedPlayerList.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/server/dedicated/DedicatedPlayerList.java +++ b/net/minecraft/server/dedicated/DedicatedPlayerList.java @@ -16,6 +_,11 @@ - super(server, registries, playerIo, server.notificationManager()); + super(server, registries, playerDataStorage, server.notificationManager()); this.setViewDistance(server.viewDistance()); this.setSimulationDistance(server.simulationDistance()); + // Paper start - fix converting txt to json file; moved from constructor diff --git a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch index c8cd84714454..c19fea33bc86 100644 --- a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/server/dedicated/DedicatedServer.java +++ b/net/minecraft/server/dedicated/DedicatedServer.java -@@ -80,9 +_,9 @@ - static final Logger LOGGER = LogUtils.getLogger(); +@@ -84,9 +_,9 @@ + private static final Logger LOGGER = LogUtils.getLogger(); private static final int CONVERSION_RETRY_DELAY_MS = 5000; private static final int CONVERSION_RETRIES = 2; - private final List consoleInput = Collections.synchronizedList(Lists.newArrayList()); @@ -12,20 +12,25 @@ private @Nullable RconThread rconThread; public DedicatedServerSettings settings; private @Nullable MinecraftServerGui gui; -@@ -95,6 +_,7 @@ +@@ -99,6 +_,7 @@ private long lastHeartbeat; public DedicatedServer( + joptsimple.OptionSet options, net.minecraft.server.WorldLoader.DataLoadContext worldLoader, // CraftBukkit - Signature changed - Thread serverThread, - LevelStorageSource.LevelStorageAccess storageSource, - PackRepository packRepository, -@@ -103,9 +_,10 @@ - DataFixer fixerUpper, - Services services + final Thread serverThread, + final LevelStorageSource.LevelStorageAccess levelStorageSource, + final PackRepository packRepository, +@@ -109,6 +_,7 @@ + final Services services ) { -- super(serverThread, storageSource, packRepository, worldStem, Proxy.NO_PROXY, fixerUpper, services, LoggingLevelLoadListener.forDedicatedServer()); -+ super(options, worldLoader, serverThread, storageSource, packRepository, worldStem, Proxy.NO_PROXY, fixerUpper, services, LoggingLevelLoadListener.forDedicatedServer()); // CraftBukkit - Signature changed + super( ++ options, worldLoader, // CraftBukkit - Signature changed + serverThread, + levelStorageSource, + packRepository, +@@ -121,7 +_,8 @@ + true + ); this.settings = settings; - this.rconConsoleSource = new RconConsoleSource(this); + this.setMotd(settings.getProperties().motd.get()); // Paper - set field from initial properties @@ -33,23 +38,22 @@ this.serverTextFilter = ServerTextFilter.createFromConfig(settings.getProperties()); this.serverLinks = createServerLinks(settings); if (settings.getProperties().codeOfConduct) { -@@ -191,6 +_,10 @@ - Thread thread = new Thread("Server console handler") { +@@ -211,6 +_,10 @@ + @Override public void run() { + if (!org.bukkit.craftbukkit.Main.useConsole) return; // CraftBukkit + // Paper start - Use TerminalConsoleAppender + new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start(); + /* - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); - String string4; -@@ -200,17 +_,41 @@ - } + String line; +@@ -221,16 +_,41 @@ } catch (IOException var4) { DedicatedServer.LOGGER.error("Exception handling console input", (Throwable)var4); -- } -+ }*/ + } ++ */ + // Paper end - Use TerminalConsoleAppender } }; @@ -66,10 +70,10 @@ + System.setOut(org.apache.logging.log4j.io.IoBuilder.forLogger(logger).setLevel(org.apache.logging.log4j.Level.INFO).buildPrintStream()); + System.setErr(org.apache.logging.log4j.io.IoBuilder.forLogger(logger).setLevel(org.apache.logging.log4j.Level.WARN).buildPrintStream()); + // CraftBukkit end - thread.setDaemon(true); - thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)); -- thread.start(); -+ // thread.start(); // Paper - Enhance console tab completions for brigadier commands; moved down + consoleThread.setDaemon(true); + consoleThread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)); +- consoleThread.start(); ++ // consoleThread.start(); // Paper - Enhance console tab completions for brigadier commands; moved down LOGGER.info("Starting minecraft server version {}", SharedConstants.getCurrentVersion().name()); if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) { LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\""); @@ -88,7 +92,7 @@ LOGGER.info("Loading properties"); DedicatedServerProperties properties = this.settings.getProperties(); if (this.isSingleplayer()) { -@@ -221,8 +_,46 @@ +@@ -241,8 +_,45 @@ this.setLocalIp(properties.serverIp); } @@ -98,7 +102,6 @@ + org.spigotmc.SpigotConfig.init((java.io.File) this.options.valueOf("spigot-settings")); + org.spigotmc.SpigotConfig.registerCommands(); + // Spigot end -+ io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // Paper - load mappings for stacktrace deobf and etc. + // Paper start - initialize global and world-defaults configuration + this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess()); + this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess()); @@ -111,7 +114,7 @@ + this.getPlayerList().loadAndSaveFiles(); // Must be after convertNames + // Paper end - fix converting txt to json file + org.spigotmc.WatchdogThread.doStart(org.spigotmc.SpigotConfig.timeoutTime, org.spigotmc.SpigotConfig.restartOnCrash); // Paper - start watchdog thread -+ thread.start(); // Paper - Enhance console tab completions for brigadier commands; start console thread after MinecraftServer.console & PaperConfig are initialized ++ consoleThread.start(); // Paper - Enhance console tab completions for brigadier commands; start console thread after MinecraftServer.console & PaperConfig are initialized + io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command + this.server.spark.registerCommandBeforePlugins(this.server); // Paper - spark + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics @@ -133,14 +136,14 @@ + } + bindAddress = new io.netty.channel.unix.DomainSocketAddress(this.getLocalIp().substring("unix:".length())); + } else { - InetAddress inetAddress = null; + InetAddress localAddress = null; if (!this.getLocalIp().isEmpty()) { - inetAddress = InetAddress.getByName(this.getLocalIp()); -@@ -231,46 +_,72 @@ + localAddress = InetAddress.getByName(this.getLocalIp()); +@@ -251,46 +_,72 @@ if (this.getPort() < 0) { this.setPort(properties.serverPort); } -+ bindAddress = new java.net.InetSocketAddress(inetAddress, this.getPort()); ++ bindAddress = new java.net.InetSocketAddress(localAddress, this.getPort()); + } + // Paper end - Unix domain socket support @@ -148,7 +151,7 @@ LOGGER.info("Starting Minecraft server on {}:{}", this.getLocalIp().isEmpty() ? "*" : this.getLocalIp(), this.getPort()); try { -- this.getConnection().startTcpServerListener(inetAddress, this.getPort()); +- this.getConnection().startTcpServerListener(localAddress, this.getPort()); + this.getConnection().startTcpServerListener(bindAddress); // Paper - Unix domain socket support } catch (IOException var11) { LOGGER.warn("**** FAILED TO BIND TO PORT!"); @@ -195,38 +198,38 @@ + */ + // CraftBukkit end - if (!OldUsersConverter.serverReadyAfterUserconversion(this)) { + if (!OldUsersConverter.areOldUserlistsRemoved()) { return false; } else { - this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); + // this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); // CraftBukkit - moved up this.tickTimeLogger = new RemoteSampleLogger(TpsDebugDimensions.values().length, this.debugSubscribers(), RemoteDebugSampleType.TICK_TIME); - long nanos = Util.getNanos(); + long levelNanoTime = Util.getNanos(); this.services.nameToIdCache().resolveOfflineUsers(!this.usesAuthentication()); LOGGER.info("Preparing level \"{}\"", this.getLevelIdName()); - this.loadLevel(); + this.loadLevel(this.storageSource.getLevelId()); // CraftBukkit - long l = Util.getNanos() - nanos; - String string3 = String.format(Locale.ROOT, "%.3fs", l / 1.0E9); -- LOGGER.info("Done ({})! For help, type \"help\"", string3); -+ LOGGER.info("Done preparing level \"{}\" ({})", this.getLevelIdName(), string3); // Paper - Improve startup message, add total time + long elapsed = Util.getNanos() - levelNanoTime; + String time = String.format(Locale.ROOT, "%.3fs", elapsed / 1.0E9); +- LOGGER.info("Done ({})! For help, type \"help\"", time); ++ LOGGER.info("Done preparing level \"{}\" ({})", this.getLevelIdName(), time); // Paper - Improve startup message, add total time + this.initPostWorld(); // Paper - don't include plugins in world preparation time if (properties.announcePlayerAchievements != null) { -- this.worldData.getGameRules().set(GameRules.SHOW_ADVANCEMENT_MESSAGES, properties.announcePlayerAchievements, this); -+ this.worldData.getGameRules().set(GameRules.SHOW_ADVANCEMENT_MESSAGES, properties.announcePlayerAchievements, this.overworld()); // Paper - per-world game rules +- this.getGameRules().set(GameRules.SHOW_ADVANCEMENT_MESSAGES, properties.announcePlayerAchievements, this); ++ this.getAllLevels().forEach(l -> l.getGameRules().set(GameRules.SHOW_ADVANCEMENT_MESSAGES, properties.announcePlayerAchievements, this.overworld())); // Paper - per-world game rules } if (properties.enableQuery) { -@@ -283,7 +_,7 @@ +@@ -303,7 +_,7 @@ this.rconThread = RconThread.create(this); } - if (this.getMaxTickLength() > 0L) { + if (false && this.getMaxTickLength() > 0L) { // Spigot - disable - Thread thread1 = new Thread(new ServerWatchdog(this)); - thread1.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(LOGGER)); - thread1.setName("Server Watchdog"); -@@ -301,6 +_,12 @@ + Thread watchdog = new Thread(new ServerWatchdog(this)); + watchdog.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(LOGGER)); + watchdog.setName("Server Watchdog"); +@@ -322,6 +_,12 @@ } } @@ -239,24 +242,24 @@ @Override public boolean isEnforceWhitelist() { return this.settings.getProperties().enforceWhitelist.get(); -@@ -318,6 +_,7 @@ +@@ -339,6 +_,7 @@ @Override - public void setUsingWhitelist(boolean usingWhitelist) { + public void setUsingWhitelist(final boolean usingWhitelist) { + new com.destroystokyo.paper.event.server.WhitelistToggleEvent(usingWhitelist).callEvent(); // Paper - WhitelistToggleEvent - this.settings.update(dedicatedServerProperties -> dedicatedServerProperties.whiteList.update(this.registryAccess(), usingWhitelist)); + this.settings.update(p -> p.whiteList.update(this.registryAccess(), usingWhitelist)); } -@@ -368,7 +_,7 @@ +@@ -400,7 +_,7 @@ @Override - public void forceDifficulty() { + protected void forceDifficulty() { - this.setDifficulty(this.getProperties().difficulty.get(), true); + // this.setDifficulty(this.getProperties().difficulty.get(), true); // Paper - per level difficulty; Don't overwrite level.dat's difficulty, keep current } public int viewDistance() { -@@ -424,11 +_,11 @@ +@@ -456,11 +_,11 @@ } if (this.rconThread != null) { @@ -270,7 +273,7 @@ } if (this.jsonRpcServer != null) { -@@ -438,6 +_,9 @@ +@@ -470,6 +_,9 @@ LOGGER.error("Interrupted while stopping the management server", (Throwable)var2); } } @@ -280,31 +283,31 @@ } @Override -@@ -447,12 +_,20 @@ +@@ -479,12 +_,20 @@ } - public void handleConsoleInput(String msg, CommandSourceStack source) { + public void handleConsoleInput(final String msg, final CommandSourceStack source) { - this.consoleInput.add(new ConsoleInput(msg, source)); + this.serverCommandQueue.add(new ConsoleInput(msg, source)); // Paper - Perf: use proper queue } public void handleConsoleInputs() { - while (!this.consoleInput.isEmpty()) { -- ConsoleInput consoleInput = this.consoleInput.remove(0); +- ConsoleInput input = this.consoleInput.remove(0); + // Paper start - Perf: use proper queue -+ ConsoleInput consoleInput; -+ while ((consoleInput = this.serverCommandQueue.poll()) != null) { ++ ConsoleInput input; ++ while ((input = this.serverCommandQueue.poll()) != null) { + // Paper end - Perf: use proper queue + // CraftBukkit start - ServerCommand for preprocessing -+ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(this.console, consoleInput.msg); ++ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(this.console, input.msg); + this.server.getPluginManager().callEvent(event); + if (event.isCancelled()) continue; -+ consoleInput = new ConsoleInput(event.getCommand(), consoleInput.source); ++ input = new ConsoleInput(event.getCommand(), input.source); + // CraftBukkit end - this.getCommands().performPrefixedCommand(consoleInput.source, consoleInput.msg); + this.getCommands().performPrefixedCommand(input.source, input.msg); } } -@@ -592,12 +_,15 @@ +@@ -624,12 +_,15 @@ @Override public String getMotd() { @@ -313,16 +316,16 @@ } @Override - public void setMotd(String motd) { -- this.settings.update(dedicatedServerProperties -> dedicatedServerProperties.motd.update(this.registryAccess(), motd)); + public void setMotd(final String motd) { +- this.settings.update(p -> p.motd.update(this.registryAccess(), motd)); + // Paper start + super.setMotd(motd); -+ this.settings.update(dedicatedServerProperties -> dedicatedServerProperties.motd.update(this.registryAccess(), this.getMotd())); ++ this.settings.update(p -> p.motd.update(this.registryAccess(), this.getMotd())); + // Paper end } @Override -@@ -623,7 +_,11 @@ +@@ -655,7 +_,11 @@ @Override public boolean enforceSecureProfile() { DedicatedServerProperties properties = this.getProperties(); @@ -335,7 +338,7 @@ } @Override -@@ -708,21 +_,60 @@ +@@ -740,21 +_,60 @@ @Override public String getPluginNames() { @@ -367,7 +370,7 @@ } @Override - public String runCommand(String command) { + public String runCommand(final String command) { - this.rconConsoleSource.prepareForCommand(); - this.executeBlocking(() -> this.getCommands().performPrefixedCommand(this.rconConsoleSource.createCommandSourceStack(), command)); - return this.rconConsoleSource.getCommandResponse(); @@ -375,13 +378,13 @@ + throw new UnsupportedOperationException("Not supported - remote source required."); + } + -+ public String runCommand(RconConsoleSource rconConsoleSource, String s) { -+ if (s.isBlank()) return ""; // Paper - Do not process empty rcon commands ++ public String runCommand(final RconConsoleSource rconConsoleSource, final String command) { ++ if (command.isBlank()) return ""; // Paper - Do not process empty rcon commands + + rconConsoleSource.prepareForCommand(); + this.executeBlocking(() -> { + CommandSourceStack wrapper = rconConsoleSource.createCommandSourceStack(); -+ org.bukkit.event.server.RemoteServerCommandEvent event = new org.bukkit.event.server.RemoteServerCommandEvent(rconConsoleSource.getBukkitSender(wrapper), s); ++ org.bukkit.event.server.RemoteServerCommandEvent event = new org.bukkit.event.server.RemoteServerCommandEvent(rconConsoleSource.getBukkitSender(wrapper), command); + this.server.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; @@ -393,7 +396,7 @@ } @Override - public void stopServer() { + protected void stopServer() { this.notificationManager().serverShuttingDown(); super.stopServer(); - Util.shutdownExecutors(); @@ -401,7 +404,7 @@ } @Override -@@ -853,4 +_,15 @@ +@@ -882,4 +_,15 @@ public Map getCodeOfConducts() { return this.codeOfConductTexts; } diff --git a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch index 4525693454fd..098f8b33594e 100644 --- a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerProperties.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/server/dedicated/DedicatedServerProperties.java +++ b/net/minecraft/server/dedicated/DedicatedServerProperties.java @@ -50,6 +_,7 @@ - static final Logger LOGGER = LogUtils.getLogger(); + private static final Logger LOGGER = LogUtils.getLogger(); private static final Pattern SHA1 = Pattern.compile("^[a-fA-F0-9]{40}$"); private static final Splitter COMMA_SPLITTER = Splitter.on(',').trimResults(); + public final boolean debug = this.get("debug", false); // CraftBukkit @@ -11,7 +11,7 @@ @@ -106,7 +_,7 @@ public final boolean broadcastRconToOps = this.get("broadcast-rcon-to-ops", true); public final boolean broadcastConsoleToOps = this.get("broadcast-console-to-ops", true); - public final int maxWorldSize = this.get("max-world-size", property -> Mth.clamp(property, 1, 29999984), 29999984); + public final int maxWorldSize = this.get("max-world-size", v -> Mth.clamp(v, 1, 29999984), 29999984); - public final boolean syncChunkWrites = this.get("sync-chunk-writes", true); + public final boolean syncChunkWrites = this.get("sync-chunk-writes", true) && Boolean.getBoolean("Paper.enable-sync-chunk-writes"); // Paper - Hide sync chunk writes behind flag public final String regionFileComression = this.get("region-file-compression", "deflate"); @@ -25,19 +25,19 @@ + public Settings.MutableValue pauseWhenEmptySeconds = this.getMutable("pause-when-empty-seconds", -1); // Paper - disable tick sleeping by default private final DedicatedServerProperties.WorldDimensionData worldDimensionData; public final WorldOptions worldOptions; - public Settings.MutableValue acceptsTransfers = this.getMutable("accepts-transfers", false); + public final Settings.MutableValue acceptsTransfers = this.getMutable("accepts-transfers", false); + public final String rconIp; // Paper - Configurable rcon ip -- public DedicatedServerProperties(Properties properties) { -- super(properties); +- public DedicatedServerProperties(final Properties settings) { +- super(settings); + // CraftBukkit start -+ public DedicatedServerProperties(Properties properties, joptsimple.OptionSet optionset) { -+ super(properties, optionset); ++ public DedicatedServerProperties(final Properties settings, final joptsimple.OptionSet options) { ++ super(settings, options); + // CraftBukkit end - String string = this.get("level-seed", ""); - boolean flag = this.get("generate-structures", true); - long l = WorldOptions.parseSeed(string).orElse(WorldOptions.randomSeed()); -@@ -150,15 +_,21 @@ + String levelSeed = this.get("level-seed", ""); + boolean generateStructures = this.get("generate-structures", true); + long seed = WorldOptions.parseSeed(levelSeed).orElse(WorldOptions.randomSeed()); +@@ -150,15 +_,23 @@ this.get("initial-enabled-packs", String.join(",", WorldDataConfiguration.DEFAULT.dataPacks().getEnabled())), this.get("initial-disabled-packs", String.join(",", WorldDataConfiguration.DEFAULT.dataPacks().getDisabled())) ); @@ -47,19 +47,21 @@ + // Paper end - Configurable rcon ip } -- public static DedicatedServerProperties fromFile(Path path) { -- return new DedicatedServerProperties(loadFromFile(path)); +- public static DedicatedServerProperties fromFile(final Path file) { +- return new DedicatedServerProperties(loadFromFile(file)); + // CraftBukkit start -+ public static DedicatedServerProperties fromFile(Path path, joptsimple.OptionSet optionset) { -+ return new DedicatedServerProperties(loadFromFile(path), optionset); ++ public static DedicatedServerProperties fromFile(final Path file, final joptsimple.OptionSet options) { ++ return new DedicatedServerProperties(loadFromFile(file), options); ++ // CraftBukkit end } @Override -- protected DedicatedServerProperties reload(RegistryAccess registryAccess, Properties properties) { +- protected DedicatedServerProperties reload(final RegistryAccess registryAccess, final Properties properties) { - return new DedicatedServerProperties(properties); -+ public DedicatedServerProperties reload(RegistryAccess registryAccess, Properties properties, joptsimple.OptionSet options) { ++ // CraftBukkit start ++ public DedicatedServerProperties reload(final RegistryAccess registryAccess, final Properties properties, final joptsimple.OptionSet options) { + return new DedicatedServerProperties(properties, options); + // CraftBukkit end } - private static @Nullable Component parseResourcePackPrompt(String json) { + private static @Nullable Component parseResourcePackPrompt(final String prompt) { diff --git a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerSettings.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerSettings.java.patch index acd4a0443e1b..659d08bc0172 100644 --- a/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerSettings.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/DedicatedServerSettings.java.patch @@ -4,13 +4,13 @@ private final Path source; private DedicatedServerProperties properties; -- public DedicatedServerSettings(Path source) { +- public DedicatedServerSettings(final Path source) { - this.source = source; - this.properties = DedicatedServerProperties.fromFile(source); + // CraftBukkit start -+ public DedicatedServerSettings(joptsimple.OptionSet optionset) { -+ this.source = ((java.io.File) optionset.valueOf("config")).toPath(); -+ this.properties = DedicatedServerProperties.fromFile(this.source, optionset); ++ public DedicatedServerSettings(joptsimple.OptionSet options) { ++ this.source = ((java.io.File) options.valueOf("config")).toPath(); ++ this.properties = DedicatedServerProperties.fromFile(this.source, options); + // CraftBukkit end } diff --git a/paper-server/patches/sources/net/minecraft/server/dedicated/ServerWatchdog.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/ServerWatchdog.java.patch new file mode 100644 index 000000000000..d0a306f98961 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/ServerWatchdog.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/server/dedicated/ServerWatchdog.java ++++ b/net/minecraft/server/dedicated/ServerWatchdog.java +@@ -49,7 +_,7 @@ + CrashReport report = createWatchdogCrashReport("Watching Server", this.server.getRunningThread().threadId()); + this.server.fillSystemReport(report.getSystemReport()); + CrashReportCategory serverStats = report.addCategory("Performance stats"); +- serverStats.setDetail("Random tick rate", () -> this.server.getGameRules().getAsString(GameRules.RANDOM_TICK_SPEED)); ++ serverStats.setDetail("Overworld random tick rate", () -> this.server.overworld().getGameRules().getAsString(GameRules.RANDOM_TICK_SPEED)); // Paper - per-level GameRules + serverStats.setDetail( + "Level stats", + () -> Streams.stream(this.server.getAllLevels()) diff --git a/paper-server/patches/sources/net/minecraft/server/dedicated/Settings.java.patch b/paper-server/patches/sources/net/minecraft/server/dedicated/Settings.java.patch index f09c795ed4d6..9b7f098a142d 100644 --- a/paper-server/patches/sources/net/minecraft/server/dedicated/Settings.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/dedicated/Settings.java.patch @@ -8,13 +8,13 @@ + // CraftBukkit start + private joptsimple.OptionSet options = null; -- public Settings(Properties properties) { -+ public Settings(Properties properties, final joptsimple.OptionSet options) { +- public Settings(final Properties properties) { ++ public Settings(final Properties properties, final joptsimple.OptionSet options) { this.properties = properties; + this.options = options; + } + -+ private String getOverride(String name, String value) { ++ private String getOverride(final String name, final String value) { + if (this.options != null && this.options.has(name)) { + return String.valueOf(this.options.valueOf(name)); + } @@ -23,36 +23,36 @@ + // CraftBukkit end } - public static Properties loadFromFile(Path filePath) { -+ if (!Files.exists(filePath)) return new Properties(); // CraftBukkit - SPIGOT-7465, MC-264979: Don't load if file doesn't exist + public static Properties loadFromFile(final Path file) { ++ if (!Files.exists(file)) return new Properties(); // CraftBukkit - SPIGOT-7465, MC-264979: Don't load if file doesn't exist try { try { Properties var13; @@ -65,7 +_,53 @@ } - public void store(Path filePath) { -- try (Writer bufferedWriter = Files.newBufferedWriter(filePath, StandardCharsets.UTF_8)) { -+ try /*(Writer bufferedWriter = Files.newBufferedWriter(filePath, StandardCharsets.UTF_8))*/ { // Paper + public void store(final Path output) { +- try (Writer os = Files.newBufferedWriter(output, StandardCharsets.UTF_8)) { ++ try /*(Writer os = Files.newBufferedWriter(output, StandardCharsets.UTF_8))*/ { // Paper + // CraftBukkit start - Don't attempt writing to file if it's read only -+ if (filePath.toFile().exists() && !filePath.toFile().canWrite()) { -+ Settings.LOGGER.warn("Can not write to file {}, skipping.", filePath); // Paper - log message file is read-only ++ if (output.toFile().exists() && !output.toFile().canWrite()) { ++ Settings.LOGGER.warn("Can not write to file {}, skipping.", output); // Paper - log message file is read-only + return; + } + // CraftBukkit end + // Paper start - allow skipping server.properties comments -+ java.io.OutputStream outputstream = Files.newOutputStream(filePath); ++ java.io.OutputStream outputstream = Files.newOutputStream(output); + java.io.BufferedOutputStream bufferedOutputStream = !skipComments ? new java.io.BufferedOutputStream(outputstream) : new java.io.BufferedOutputStream(outputstream) { + private boolean isRightAfterNewline = true; // If last written char was newline + private boolean isComment = false; // Are we writing comment currently? + + @Override -+ public void write(@org.jetbrains.annotations.NotNull byte[] b) throws IOException { ++ public void write(byte[] b) throws IOException { + this.write(b, 0, b.length); + } + + @Override -+ public void write(@org.jetbrains.annotations.NotNull byte[] bbuf, int off, int len) throws IOException { ++ public void write(byte[] bbuf, int off, int len) throws IOException { + int latest_offset = off; // The latest offset, updated when comment ends + for (int index = off; index < off + len; ++index ) { + byte c = bbuf[index]; @@ -78,51 +78,52 @@ + } + } + }; -+ java.io.BufferedWriter bufferedWriter = new java.io.BufferedWriter(new java.io.OutputStreamWriter(bufferedOutputStream, java.nio.charset.StandardCharsets.UTF_8.newEncoder())); ++ java.io.BufferedWriter os = new java.io.BufferedWriter(new java.io.OutputStreamWriter(bufferedOutputStream, java.nio.charset.StandardCharsets.UTF_8.newEncoder())); + // Paper end - allow skipping server.properties comments - this.properties.store(bufferedWriter, "Minecraft server properties"); + this.properties.store(os, "Minecraft server properties"); } catch (IOException var7) { - LOGGER.error("Failed to store properties to file: {}", filePath); -@@ -93,7 +_,7 @@ + LOGGER.error("Failed to store properties to file: {}", output); +@@ -95,7 +_,7 @@ } - public @Nullable String getStringRaw(String key) { + public @Nullable String getStringRaw(final String key) { - return (String)this.properties.get(key); + return (String)this.getOverride(key, this.properties.getProperty(key)); // CraftBukkit } - protected @Nullable V getLegacy(String key, Function serializer) { -@@ -107,6 +_,15 @@ + protected @Nullable V getLegacy(final String key, final Function deserializer) { +@@ -109,6 +_,16 @@ } - protected V get(String key, Function serializer, Function deserializer, V defaultValue) { + protected V get(final String key, final Function deserializer, final Function serializer, final V defaultValue) { + // CraftBukkit start + try { -+ return this.get0(key, serializer, deserializer, defaultValue); ++ return this.get0(key, deserializer, serializer, defaultValue); + } catch (Exception ex) { + throw new RuntimeException("Could not load invalidly configured property '" + key + "'", ex); + } + } -+ private V get0(String key, Function serializer, Function deserializer, V defaultValue) { ++ ++ private V get0(final String key, final Function deserializer, final Function serializer, final V defaultValue) { + // CraftBukkit end - String stringRaw = this.getStringRaw(key); - V object = MoreObjects.firstNonNull(stringRaw != null ? serializer.apply(stringRaw) : null, defaultValue); - this.properties.put(key, deserializer.apply(object)); -@@ -181,7 +_,7 @@ - return map; + String value = this.getStringRaw(key); + V result = MoreObjects.firstNonNull(value != null ? deserializer.apply(value) : null, defaultValue); + this.properties.put(key, serializer.apply(result)); +@@ -191,7 +_,7 @@ + return result; } -- protected abstract T reload(RegistryAccess registryAccess, Properties properties); -+ protected abstract T reload(RegistryAccess registryAccess, Properties properties, joptsimple.OptionSet optionset); // CraftBukkit +- protected abstract T reload(final RegistryAccess registryAccess, final Properties properties); ++ protected abstract T reload(final RegistryAccess registryAccess, final Properties properties, final joptsimple.OptionSet options); // CraftBukkit public class MutableValue implements Supplier { private final String key; -@@ -202,7 +_,7 @@ - public T update(RegistryAccess registryAccess, V newValue) { - Properties map = Settings.this.cloneProperties(); - map.put(this.key, this.serializer.apply(newValue)); -- return Settings.this.reload(registryAccess, map); -+ return Settings.this.reload(registryAccess, map, Settings.this.options); // CraftBukkit +@@ -214,7 +_,7 @@ + public T update(final RegistryAccess registryAccess, final V value) { + Properties properties = Settings.this.cloneProperties(); + properties.put(this.key, this.serializer.apply(value)); +- return Settings.this.reload(registryAccess, properties); ++ return Settings.this.reload(registryAccess, properties, Settings.this.options); // CraftBukkit } } } diff --git a/paper-server/patches/sources/net/minecraft/server/dialog/body/ItemBody.java.patch b/paper-server/patches/sources/net/minecraft/server/dialog/body/ItemBody.java.patch index d541a55a099d..5e4dba13f2bf 100644 --- a/paper-server/patches/sources/net/minecraft/server/dialog/body/ItemBody.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/dialog/body/ItemBody.java.patch @@ -9,5 +9,5 @@ + ExtraCodecs.intRange(1, 256).optionalFieldOf("width", 16).forGetter(ItemBody::width), // Paper - diff on change - update builder defaults/limits + ExtraCodecs.intRange(1, 256).optionalFieldOf("height", 16).forGetter(ItemBody::height) // Paper - diff on change - update builder defaults/limits ) - .apply(instance, ItemBody::new) + .apply(i, ItemBody::new) ); diff --git a/paper-server/patches/sources/net/minecraft/server/gui/MinecraftServerGui.java.patch b/paper-server/patches/sources/net/minecraft/server/gui/MinecraftServerGui.java.patch index d78cde0d2c0c..53a34b4876cc 100644 --- a/paper-server/patches/sources/net/minecraft/server/gui/MinecraftServerGui.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/gui/MinecraftServerGui.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/server/gui/MinecraftServerGui.java +++ b/net/minecraft/server/gui/MinecraftServerGui.java -@@ -53,6 +_,13 @@ - jFrame.pack(); - jFrame.setLocationRelativeTo(null); - jFrame.setVisible(true); +@@ -54,6 +_,13 @@ + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + // Paper start - Improve ServerGUI -+ jFrame.setName("Minecraft server"); ++ frame.setName("Minecraft server"); + try { -+ jFrame.setIconImage(javax.imageio.ImageIO.read(java.util.Objects.requireNonNull(MinecraftServerGui.class.getClassLoader().getResourceAsStream("logo.png")))); ++ frame.setIconImage(javax.imageio.ImageIO.read(java.util.Objects.requireNonNull(MinecraftServerGui.class.getClassLoader().getResourceAsStream("logo.png")))); + } catch (java.io.IOException ignore) { + } + // Paper end - Improve ServerGUI - jFrame.addWindowListener(new WindowAdapter() { + frame.addWindowListener(new WindowAdapter() { @Override - public void windowClosing(WindowEvent event) { -@@ -74,6 +_,7 @@ + public void windowClosing(final WindowEvent event) { +@@ -75,6 +_,7 @@ this.setLayout(new BorderLayout()); try { @@ -22,24 +22,24 @@ this.add(this.buildChatPanel(), "Center"); this.add(this.buildInfoPanel(), "West"); } catch (Exception var3) { -@@ -87,7 +_,7 @@ +@@ -88,7 +_,7 @@ private JComponent buildInfoPanel() { - JPanel jPanel = new JPanel(new BorderLayout()); -- StatsComponent statsComponent = new StatsComponent(this.server); -+ com.destroystokyo.paper.gui.GuiStatsComponent statsComponent = new com.destroystokyo.paper.gui.GuiStatsComponent(this.server); // Paper - Make GUI graph fancier - this.finalizers.add(statsComponent::close); - jPanel.add(statsComponent, "North"); - jPanel.add(this.buildPlayerPanel(), "Center"); -@@ -150,6 +_,7 @@ + JPanel panel = new JPanel(new BorderLayout()); +- StatsComponent comp = new StatsComponent(this.server); ++ com.destroystokyo.paper.gui.GuiStatsComponent comp = new com.destroystokyo.paper.gui.GuiStatsComponent(this.server); // Paper - Make GUI graph fancier + this.finalizers.add(comp::close); + panel.add(comp, "North"); + panel.add(this.buildPlayerPanel(), "Center"); +@@ -155,6 +_,7 @@ this.finalizers.forEach(Runnable::run); } + private static final java.util.regex.Pattern ANSI = java.util.regex.Pattern.compile("\\e\\[[\\d;]*[^\\d;]"); // CraftBukkit // Paper - public void print(JTextArea textArea, JScrollPane scrollPane, String line) { + public void print(final JTextArea console, final JScrollPane scrollPane, final String line) { if (!SwingUtilities.isEventDispatchThread()) { - SwingUtilities.invokeLater(() -> this.print(textArea, scrollPane, line)); -@@ -162,7 +_,7 @@ + SwingUtilities.invokeLater(() -> this.print(console, scrollPane, line)); +@@ -167,7 +_,7 @@ } try { @@ -48,7 +48,7 @@ } catch (BadLocationException var8) { } -@@ -171,4 +_,37 @@ +@@ -176,4 +_,37 @@ } } } diff --git a/paper-server/patches/sources/net/minecraft/server/gui/StatsComponent.java.patch b/paper-server/patches/sources/net/minecraft/server/gui/StatsComponent.java.patch index 40640f3fda1f..5e806ee764fb 100644 --- a/paper-server/patches/sources/net/minecraft/server/gui/StatsComponent.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/gui/StatsComponent.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/server/gui/StatsComponent.java +++ b/net/minecraft/server/gui/StatsComponent.java -@@ -32,8 +_,17 @@ +@@ -32,12 +_,21 @@ private void tick() { - long l = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + long usedRam = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + // Paper start - Improve ServerGUI + double[] tps = org.bukkit.Bukkit.getTPS(); + String[] tpsAvg = new String[tps.length]; @@ -11,14 +11,18 @@ + for (int g = 0; g < tps.length; g++) { + tpsAvg[g] = format(tps[g]); + } - this.msgs[0] = "Memory use: " + l / 1024L / 1024L + " mb (" + Runtime.getRuntime().freeMemory() * 100L / Runtime.getRuntime().maxMemory() + "% free)"; + this.msgs[0] = "Memory use: " + + usedRam / 1024L / 1024L + + " mb (" + + Runtime.getRuntime().freeMemory() * 100L / Runtime.getRuntime().maxMemory() + + "% free)"; this.msgs[1] = "Avg tick: " + DECIMAL_FORMAT.format((double)this.server.getAverageTickTimeNanos() / TimeUtil.NANOSECONDS_PER_MILLISECOND) + " ms"; + this.msgs[2] = "TPS from last 1m, 5m, 15m: " + String.join(", ", tpsAvg); + // Paper end - Improve ServerGUI - this.values[this.vp++ & 0xFF] = (int)(l * 100L / Runtime.getRuntime().maxMemory()); + this.values[this.vp++ & 0xFF] = (int)(usedRam * 100L / Runtime.getRuntime().maxMemory()); this.repaint(); } -@@ -62,4 +_,10 @@ +@@ -66,4 +_,10 @@ public void close() { this.timer.stop(); } diff --git a/paper-server/patches/sources/net/minecraft/server/jsonrpc/JsonRpcNotificationService.java.patch b/paper-server/patches/sources/net/minecraft/server/jsonrpc/JsonRpcNotificationService.java.patch index e8fa089602cf..e6ae83d7a698 100644 --- a/paper-server/patches/sources/net/minecraft/server/jsonrpc/JsonRpcNotificationService.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/jsonrpc/JsonRpcNotificationService.java.patch @@ -4,9 +4,9 @@ } @Override -- public void onGameRuleChanged(GameRule rule, T value) { -+ public void onGameRuleChanged(net.minecraft.server.level.ServerLevel level, GameRule rule, T value) { // Paper - per-world game rules (add level param) +- public void onGameRuleChanged(final GameRule gameRule, final T value) { ++ public void onGameRuleChanged(net.minecraft.server.level.ServerLevel level, final GameRule gameRule, final T value) { // Paper - per-world game rules (add level param) + if (level != level.getServer().overworld()) return; // Paper - per-world game rules - use overworld for vanilla protocol - this.broadcastNotification(OutgoingRpcMethods.GAMERULE_CHANGED, GameRulesService.getTypedRule(this.minecraftApi, rule, value)); + this.broadcastNotification(OutgoingRpcMethods.GAMERULE_CHANGED, GameRulesService.getTypedRule(this.minecraftApi, gameRule, value)); } diff --git a/paper-server/patches/sources/net/minecraft/server/jsonrpc/internalapi/MinecraftGameRuleServiceImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/jsonrpc/internalapi/MinecraftGameRuleServiceImpl.java.patch index 92b7b4e23401..bafa895fc28d 100644 --- a/paper-server/patches/sources/net/minecraft/server/jsonrpc/internalapi/MinecraftGameRuleServiceImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/jsonrpc/internalapi/MinecraftGameRuleServiceImpl.java.patch @@ -1,11 +1,20 @@ --- a/net/minecraft/server/jsonrpc/internalapi/MinecraftGameRuleServiceImpl.java +++ b/net/minecraft/server/jsonrpc/internalapi/MinecraftGameRuleServiceImpl.java +@@ -15,7 +_,7 @@ + + public MinecraftGameRuleServiceImpl(final DedicatedServer server, final JsonRpcLogger jsonrpcLogger) { + this.server = server; +- this.gameRules = server.getGameRules(); ++ this.gameRules = server.overworld().getGameRules(); // Paper - per-world game rules - use overworld for vanilla protocol + this.jsonrpcLogger = jsonrpcLogger; + } + @@ -24,7 +_,7 @@ GameRule gameRule = update.gameRule(); - T object = this.gameRules.get(gameRule); - T object1 = update.value(); -- this.gameRules.set(gameRule, object1, this.server); -+ this.gameRules.set(gameRule, object1, this.server.overworld()); // Paper - per-world game rules - use overworld for vanilla protocol - this.jsonrpcLogger.log(client, "Game rule '{}' updated from '{}' to '{}'", gameRule.id(), gameRule.serialize(object), gameRule.serialize(object1)); + T oldValue = this.gameRules.get(gameRule); + T newValue = update.value(); +- this.gameRules.set(gameRule, newValue, this.server); ++ this.gameRules.set(gameRule, newValue, this.server.overworld()); // Paper - per-world game rules - use overworld for vanilla protocol + this.jsonrpcLogger + .log(clientInfo, "Game rule '{}' updated from '{}' to '{}'", gameRule.id(), gameRule.serialize(oldValue), gameRule.serialize(newValue)); return update; - } diff --git a/paper-server/patches/sources/net/minecraft/server/level/ChunkHolder.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ChunkHolder.java.patch index dbd31018891f..40e8aed5c903 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ChunkHolder.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ChunkHolder.java.patch @@ -43,14 +43,14 @@ @@ -127,6 +_,7 @@ } else { - boolean flag = this.hasChangedSections; + boolean hadChangedSections = this.hasChangedSections; int sectionIndex = this.levelHeightAccessor.getSectionIndex(pos.getY()); + if (sectionIndex < 0 || sectionIndex >= this.changedBlocksPerSection.length) return false; // CraftBukkit - SPIGOT-6086, SPIGOT-6296 - ShortSet set = this.changedBlocksPerSection[sectionIndex]; - if (set == null) { + ShortSet changedBlocksInSection = this.changedBlocksPerSection[sectionIndex]; + if (changedBlocksInSection == null) { this.hasChangedSections = true; -@@ -274,6 +_,38 @@ - chunkMap.onFullChunkStatusChange(this.pos, fullChunkStatus); +@@ -270,6 +_,38 @@ + scheduler.onFullChunkStatusChange(this.pos, status); } + // CraftBukkit start @@ -85,16 +85,16 @@ + } + // CraftBukkit end + - protected void updateFutures(ChunkMap chunkMap, Executor executor) { - FullChunkStatus fullChunkStatus = ChunkLevel.fullStatus(this.oldTicketLevel); - FullChunkStatus fullChunkStatus1 = ChunkLevel.fullStatus(this.ticketLevel); -@@ -281,12 +_,28 @@ - boolean isOrAfter1 = fullChunkStatus1.isOrAfter(FullChunkStatus.FULL); - this.wasAccessibleSinceLastSave |= isOrAfter1; - if (!isOrAfter && isOrAfter1) { + protected void updateFutures(final ChunkMap scheduler, final Executor mainThreadExecutor) { + FullChunkStatus oldFullStatus = ChunkLevel.fullStatus(this.oldTicketLevel); + FullChunkStatus newFullStatus = ChunkLevel.fullStatus(this.ticketLevel); +@@ -277,12 +_,28 @@ + boolean isAccessible = newFullStatus.isOrAfter(FullChunkStatus.FULL); + this.wasAccessibleSinceLastSave |= isAccessible; + if (!wasAccessible && isAccessible) { + int expectCreateCount = ++this.fullChunkCreateCount; // Paper - this.fullChunkFuture = chunkMap.prepareAccessibleChunk(this); - this.scheduleFullChunkPromotion(chunkMap, this.fullChunkFuture, executor, FullChunkStatus.FULL); + this.fullChunkFuture = scheduler.prepareAccessibleChunk(this); + this.scheduleFullChunkPromotion(scheduler, this.fullChunkFuture, mainThreadExecutor, FullChunkStatus.FULL); + // Paper start - cache ticking ready status + this.fullChunkFuture.thenAccept(chunkResult -> { + chunkResult.ifSuccess(chunk -> { @@ -108,7 +108,7 @@ this.addSaveDependency(this.fullChunkFuture); } - if (isOrAfter && !isOrAfter1) { + if (wasAccessible && !isAccessible) { + // Paper start + if (this.isFullChunkReady) { + ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkNotBorder(this.fullChunkFuture.join().orElseThrow(IllegalStateException::new), this); // Paper @@ -117,10 +117,10 @@ this.fullChunkFuture.complete(UNLOADED_LEVEL_CHUNK); this.fullChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; } -@@ -296,11 +_,25 @@ - if (!isOrAfter2 && isOrAfter3) { - this.tickingChunkFuture = chunkMap.prepareTickingChunk(this); - this.scheduleFullChunkPromotion(chunkMap, this.tickingChunkFuture, executor, FullChunkStatus.BLOCK_TICKING); +@@ -292,11 +_,25 @@ + if (!wasTicking && isTicking) { + this.tickingChunkFuture = scheduler.prepareTickingChunk(this); + this.scheduleFullChunkPromotion(scheduler, this.tickingChunkFuture, mainThreadExecutor, FullChunkStatus.BLOCK_TICKING); + // Paper start - cache ticking ready status + this.tickingChunkFuture.thenAccept(chunkResult -> { + chunkResult.ifSuccess(chunk -> { @@ -129,11 +129,11 @@ + ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkTicking(chunk, this); + }); + }); -+ // Paper end ++ // Paper end - cache ticking ready status this.addSaveDependency(this.tickingChunkFuture); } - if (isOrAfter2 && !isOrAfter3) { + if (wasTicking && !isTicking) { - this.tickingChunkFuture.complete(UNLOADED_LEVEL_CHUNK); + // Paper start + if (this.isTickingReady) { @@ -144,10 +144,10 @@ this.tickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; } -@@ -313,11 +_,24 @@ +@@ -309,11 +_,24 @@ - this.entityTickingChunkFuture = chunkMap.prepareEntityTickingChunk(this); - this.scheduleFullChunkPromotion(chunkMap, this.entityTickingChunkFuture, executor, FullChunkStatus.ENTITY_TICKING); + this.entityTickingChunkFuture = scheduler.prepareEntityTickingChunk(this); + this.scheduleFullChunkPromotion(scheduler, this.entityTickingChunkFuture, mainThreadExecutor, FullChunkStatus.ENTITY_TICKING); + // Paper start - cache ticking ready status + this.entityTickingChunkFuture.thenAccept(chunkResult -> { + chunkResult.ifSuccess(chunk -> { @@ -159,7 +159,7 @@ this.addSaveDependency(this.entityTickingChunkFuture); } - if (isOrAfter4 && !isOrAfter5) { + if (wasEntityTicking && !isEntityTicking) { - this.entityTickingChunkFuture.complete(UNLOADED_LEVEL_CHUNK); + // Paper start + if (this.isEntityTickingReady) { @@ -170,17 +170,17 @@ this.entityTickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; } -@@ -327,6 +_,26 @@ +@@ -323,6 +_,26 @@ this.onLevelChange.onLevelChange(this.pos, this::getQueueLevel, this.ticketLevel, this::setQueueLevel); this.oldTicketLevel = this.ticketLevel; + // CraftBukkit start + // ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins. -+ if (!fullChunkStatus.isOrAfter(FullChunkStatus.FULL) && fullChunkStatus1.isOrAfter(FullChunkStatus.FULL)) { ++ if (!oldFullStatus.isOrAfter(FullChunkStatus.FULL) && newFullStatus.isOrAfter(FullChunkStatus.FULL)) { + this.getFullChunkFuture().thenAccept((either) -> { + LevelChunk chunk = (LevelChunk) either.orElse(null); + if (chunk != null) { -+ chunkMap.callbackExecutor.execute(() -> { ++ scheduler.callbackExecutor.execute(() -> { + chunk.loadCallback(); + }); + } @@ -191,7 +191,7 @@ + }); + + // Run callback right away if the future was already done -+ chunkMap.callbackExecutor.run(); ++ scheduler.callbackExecutor.run(); + } + // CraftBukkit end } diff --git a/paper-server/patches/sources/net/minecraft/server/level/ChunkMap.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ChunkMap.java.patch index face92235b53..3f264811c58b 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ChunkMap.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ChunkMap.java.patch @@ -32,32 +32,32 @@ + // Paper end + public ChunkMap( - ServerLevel level, - LevelStorageSource.LevelStorageAccess levelStorageAccess, -@@ -180,13 +_,19 @@ + final ServerLevel level, + final LevelStorageSource.LevelStorageAccess levelStorage, +@@ -179,13 +_,19 @@ this.level = level; RegistryAccess registryAccess = level.registryAccess(); - long seed = level.getSeed(); -- if (generator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) { + long levelSeed = level.getSeed(); +- if (generator instanceof NoiseBasedChunkGenerator noiseGenerator) { + // CraftBukkit start - SPIGOT-7051: It's a rigged game! Use delegate for random state creation, otherwise it is not so random. + ChunkGenerator randomGenerator = generator; + if (randomGenerator instanceof org.bukkit.craftbukkit.generator.CustomChunkGenerator customChunkGenerator) { + randomGenerator = customChunkGenerator.getDelegate(); + } -+ if (randomGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) { -+ // CraftBukkit end - this.randomState = RandomState.create(noiseBasedChunkGenerator.generatorSettings().value(), registryAccess.lookupOrThrow(Registries.NOISE), seed); ++ if (randomGenerator instanceof NoiseBasedChunkGenerator noiseGenerator) { ++ // CraftBukkit end + this.randomState = RandomState.create(noiseGenerator.generatorSettings().value(), registryAccess.lookupOrThrow(Registries.NOISE), levelSeed); } else { - this.randomState = RandomState.create(NoiseGeneratorSettings.dummy(), registryAccess.lookupOrThrow(Registries.NOISE), seed); + this.randomState = RandomState.create(NoiseGeneratorSettings.dummy(), registryAccess.lookupOrThrow(Registries.NOISE), levelSeed); } -- this.chunkGeneratorState = generator.createState(registryAccess.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, seed); -+ this.chunkGeneratorState = generator.createState(registryAccess.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, seed, level.spigotConfig); // Spigot +- this.chunkGeneratorState = generator.createState(registryAccess.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, levelSeed); ++ this.chunkGeneratorState = generator.createState(registryAccess.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, levelSeed, level.spigotConfig); // Spigot this.mainThreadExecutor = mainThreadExecutor; - ConsecutiveExecutor consecutiveExecutor = new ConsecutiveExecutor(dispatcher, "worldgen"); + ConsecutiveExecutor worldgen = new ConsecutiveExecutor(executor, "worldgen"); this.chunkStatusListener = chunkStatusListener; -@@ -215,6 +_,12 @@ - this.chunksToEagerlySave.add(chunkPos.toLong()); +@@ -212,6 +_,12 @@ + this.chunksToEagerlySave.add(chunkPos.pack()); } + // Paper start @@ -69,47 +69,47 @@ protected ChunkGenerator generator() { return this.worldGenContext.generator(); } -@@ -357,9 +_,9 @@ - } - ); - stringBuilder.append("Updating:").append(System.lineSeparator()); -- this.updatingChunkMap.values().forEach(consumer); -+ ca.spottedleaf.moonrise.common.PlatformHooks.get().getUpdatingChunkHolders(this.level).forEach(consumer); // Paper - stringBuilder.append("Visible:").append(System.lineSeparator()); -- this.visibleChunkMap.values().forEach(consumer); -+ ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.level).forEach(consumer); // Paper - CrashReport crashReport = CrashReport.forThrowable(exception, "Chunk loading"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Chunk loading"); - crashReportCategory.setDetail("Details", details); -@@ -394,6 +_,7 @@ - holder.setTicketLevel(newLevel); +@@ -348,9 +_,9 @@ + } + }); + sb.append("Updating:").append(System.lineSeparator()); +- this.updatingChunkMap.values().forEach(addToDebug); ++ ca.spottedleaf.moonrise.common.PlatformHooks.get().getUpdatingChunkHolders(this.level).forEach(addToDebug); // Paper + sb.append("Visible:").append(System.lineSeparator()); +- this.visibleChunkMap.values().forEach(addToDebug); ++ ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.level).forEach(addToDebug); // Paper + CrashReport report = CrashReport.forThrowable(exception, "Chunk loading"); + CrashReportCategory category = report.addCategory("Chunk loading"); + category.setDetail("Details", details); +@@ -385,6 +_,7 @@ + chunk.setTicketLevel(level); } else { - holder = new ChunkHolder(new ChunkPos(chunkPos), newLevel, this.level, this.lightEngine, this::onLevelChange, this); -+ ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkHolderCreate(this.level, holder); // Paper + chunk = new ChunkHolder(ChunkPos.unpack(node), level, this.level, this.lightEngine, this::onLevelChange, this); ++ ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkHolderCreate(this.level, chunk); // Paper } - this.updatingChunkMap.put(chunkPos, holder); -@@ -422,8 +_,8 @@ + this.updatingChunkMap.put(node, chunk); +@@ -413,8 +_,8 @@ - protected void saveAllChunks(boolean flush) { - if (flush) { -- List list = this.visibleChunkMap + protected void saveAllChunks(final boolean flushStorage) { + if (flushStorage) { +- List chunksToSave = this.visibleChunkMap - .values() -+ List list = ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.level) // Paper - moonrise ++ List chunksToSave = ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.level) // Paper - moonrise + //.values() // Paper - moonrise .stream() .filter(ChunkHolder::wasAccessibleSinceLastSave) .peek(ChunkHolder::refreshAccessibility) -@@ -449,7 +_,7 @@ +@@ -440,7 +_,7 @@ this.nextChunkSaveTime.clear(); - long millis = Util.getMillis(); + long now = Util.getMillis(); -- for (ChunkHolder chunkHolder : this.visibleChunkMap.values()) { -+ for (ChunkHolder chunkHolder : ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.level)) { // Paper - this.saveChunkIfNeeded(chunkHolder, millis); +- for (ChunkHolder chunk : this.visibleChunkMap.values()) { ++ for (ChunkHolder chunk : ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.level)) { // Paper + this.saveChunkIfNeeded(chunk, now); } } -@@ -470,6 +_,7 @@ +@@ -461,6 +_,7 @@ public boolean hasWork() { return this.lightEngine.hasLightWork() || !this.pendingUnloads.isEmpty() @@ -117,31 +117,31 @@ || !this.updatingChunkMap.isEmpty() || this.poiManager.hasWork() || !this.toDrop.isEmpty() -@@ -528,7 +_,11 @@ - this.scheduleUnload(chunkPos, chunkHolder); +@@ -519,7 +_,11 @@ + this.scheduleUnload(pos, chunkHolder); } else { - ChunkAccess latestChunk = chunkHolder.getLatestChunk(); -- if (this.pendingUnloads.remove(chunkPos, chunkHolder) && latestChunk != null) { + ChunkAccess chunk = chunkHolder.getLatestChunk(); +- if (this.pendingUnloads.remove(pos, chunkHolder) && chunk != null) { + // Paper start + boolean removed; -+ if ((removed = this.pendingUnloads.remove(chunkPos, chunkHolder)) && latestChunk != null) { ++ if ((removed = this.pendingUnloads.remove(pos, chunkHolder)) && chunk != null) { + ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkHolderDelete(this.level, chunkHolder); + // Paper end - if (latestChunk instanceof LevelChunk levelChunk) { + if (chunk instanceof LevelChunk levelChunk) { levelChunk.setLoaded(false); } -@@ -541,7 +_,9 @@ - this.lightEngine.updateChunkStatus(latestChunk.getPos()); +@@ -532,7 +_,9 @@ + this.lightEngine.updateChunkStatus(chunk.getPos()); this.lightEngine.tryScheduleUpdate(); - this.nextChunkSaveTime.remove(latestChunk.getPos().toLong()); + this.nextChunkSaveTime.remove(chunk.getPos().pack()); - } + } else if (removed) { // Paper start + ca.spottedleaf.moonrise.common.PlatformHooks.get().onChunkHolderDelete(this.level, chunkHolder); + } // Paper end } - }, this.unloadQueue::add).whenComplete((_void, error) -> { - if (error != null) { -@@ -851,7 +_,7 @@ + }, this.unloadQueue::add).whenComplete((ignored, throwable) -> { + if (throwable != null) { +@@ -841,7 +_,7 @@ } public int size() { @@ -150,115 +150,118 @@ } public net.minecraft.server.level.DistanceManager getDistanceManager() { -@@ -878,10 +_,10 @@ +@@ -868,10 +_,10 @@ .addColumn("fluid_ticks") - .build(writer); + .build(output); - for (Entry entry : this.visibleChunkMap.long2ObjectEntrySet()) { -- long longKey = entry.getLongKey(); +- long posKey = entry.getLongKey(); +- ChunkPos pos = ChunkPos.unpack(posKey); +- ChunkHolder holder = entry.getValue(); + for (ChunkHolder entry : ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.level)) { // Paper - Moonrise -+ long longKey = entry.pos.toLong(); // Paper - Moonrise - ChunkPos chunkPos = new ChunkPos(longKey); -- ChunkHolder chunkHolder = entry.getValue(); -+ ChunkHolder chunkHolder = entry; // Paper - Moonrise - Optional optional = Optional.ofNullable(chunkHolder.getLatestChunk()); - Optional optional1 = optional.flatMap(chunk -> chunk instanceof LevelChunk ? Optional.of((LevelChunk)chunk) : Optional.empty()); - csvOutput.writeRow( -@@ -925,12 +_,12 @@ ++ long posKey = entry.pos.pack(); // Paper - Moonrise ++ ChunkPos pos = entry.pos; // Paper - Moonrise ++ ChunkHolder holder = entry; // Paper - Moonrise + Optional chunk = Optional.ofNullable(holder.getLatestChunk()); + Optional fullChunk = chunk.flatMap( + chunkAccess -> chunkAccess instanceof LevelChunk ? Optional.of((LevelChunk)chunkAccess) : Optional.empty() +@@ -920,14 +_,14 @@ + return this.upgradeChunkTag( + tag, + -1, +- getChunkDataFixContextTag(this.level.dimension(), this.generator().getTypeNameForDataFixer()), ++ getChunkDataFixContextTag(this.level.getTypeKey(), this.generator().getTypeNameForDataFixer()), // CraftBukkit + SharedConstants.getCurrentVersion().dataVersion().version() + ); } - private CompoundTag upgradeChunkTag(CompoundTag tag) { -- return this.upgradeChunkTag(tag, -1, getChunkDataFixContextTag(this.level.dimension(), this.generator().getTypeNameForDataFixer())); -+ return this.upgradeChunkTag(tag, -1, getChunkDataFixContextTag(this.level.getTypeKey(), this.generator().getTypeNameForDataFixer()), this.level); // CraftBukkit +- public static CompoundTag getChunkDataFixContextTag(final ResourceKey dimension, final Optional generatorIdentifier) { ++ public static CompoundTag getChunkDataFixContextTag(final ResourceKey stemKey, final Optional generatorIdentifier) { // CraftBukkit + CompoundTag contextTag = new CompoundTag(); +- contextTag.putString("dimension", dimension.identifier().toString()); ++ contextTag.putString("dimension", stemKey.identifier().toString()); // CraftBukkit + generatorIdentifier.ifPresent(identifier -> contextTag.putString("generator", identifier.toString())); + return contextTag; } - -- public static CompoundTag getChunkDataFixContextTag(ResourceKey dimension, Optional>> generator) { -+ public static CompoundTag getChunkDataFixContextTag(ResourceKey stemKey, Optional>> generator) { // CraftBukkit - CompoundTag compoundTag = new CompoundTag(); -- compoundTag.putString("dimension", dimension.identifier().toString()); -+ compoundTag.putString("dimension", stemKey.identifier().toString()); // CraftBukkit - generator.ifPresent(resourceKey -> compoundTag.putString("generator", resourceKey.identifier().toString())); - return compoundTag; - } -@@ -942,7 +_,7 @@ - ChunkHolder chunkHolder = this.visibleChunkMap.get(spawnCandidateChunks.nextLong()); - if (chunkHolder != null) { - LevelChunk tickingChunk = chunkHolder.getTickingChunk(); -- if (tickingChunk != null && this.anyPlayerCloseEnoughForSpawningInternal(chunkHolder.getPos())) { -+ if (tickingChunk != null && this.anyPlayerCloseEnoughForSpawningInternal(chunkHolder.getPos(), true)) { // Spigot - output.add(tickingChunk); +@@ -939,7 +_,7 @@ + ChunkHolder holder = this.visibleChunkMap.get(spawnCandidateChunks.nextLong()); + if (holder != null) { + LevelChunk chunk = holder.getTickingChunk(); +- if (chunk != null && this.anyPlayerCloseEnoughForSpawningInternal(holder.getPos())) { ++ if (chunk != null && this.anyPlayerCloseEnoughForSpawningInternal(holder.getPos(), true)) { // Spigot + output.add(chunk); } } -@@ -962,8 +_,14 @@ +@@ -959,8 +_,14 @@ } - public boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkPos) { + public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) { + // Spigot start -+ return this.anyPlayerCloseEnoughForSpawning(chunkPos, false); ++ return this.anyPlayerCloseEnoughForSpawning(pos, false); + } + -+ boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkPos, boolean reducedRange) { - TriState triState = this.distanceManager.hasPlayersNearby(chunkPos.toLong()); -- return triState == TriState.DEFAULT ? this.anyPlayerCloseEnoughForSpawningInternal(chunkPos) : triState.toBoolean(true); -+ return triState == TriState.DEFAULT ? this.anyPlayerCloseEnoughForSpawningInternal(chunkPos, reducedRange) : triState.toBoolean(true); ++ boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos, boolean reducedRange) { + TriState triState = this.distanceManager.hasPlayersNearby(pos.pack()); +- return triState == TriState.DEFAULT ? this.anyPlayerCloseEnoughForSpawningInternal(pos) : triState.toBoolean(true); ++ return triState == TriState.DEFAULT ? this.anyPlayerCloseEnoughForSpawningInternal(pos, reducedRange) : triState.toBoolean(true); + // Spigot end } - boolean anyPlayerCloseEnoughTo(BlockPos pos, int radius) { -@@ -979,8 +_,24 @@ + boolean anyPlayerCloseEnoughTo(final BlockPos pos, final int maxDistance) { +@@ -976,8 +_,24 @@ } - private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos chunkPos) { + private boolean anyPlayerCloseEnoughForSpawningInternal(final ChunkPos pos) { + // Spigot start -+ return this.anyPlayerCloseEnoughForSpawningInternal(chunkPos, false); ++ return this.anyPlayerCloseEnoughForSpawningInternal(pos, false); + } + -+ private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos chunkPos, boolean reducedRange) { ++ private boolean anyPlayerCloseEnoughForSpawningInternal(final ChunkPos pos, final boolean reducedRange) { + double blockRange; // Paper - use from event + // Spigot end - for (ServerPlayer serverPlayer : this.playerMap.getAllPlayers()) { -- if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos)) { + for (ServerPlayer player : this.playerMap.getAllPlayers()) { +- if (this.playerIsCloseEnoughForSpawning(player, pos)) { + // Paper start - PlayerNaturallySpawnCreaturesEvent + com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event; -+ blockRange = 16384.0D; ++ blockRange = 16384.0; + if (reducedRange) { -+ event = serverPlayer.playerNaturallySpawnedEvent; ++ event = player.playerNaturallySpawnedEvent; + if (event == null || event.isCancelled()) continue; + blockRange = (double) ((event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4)); + } -+ if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos, blockRange)) { ++ if (this.playerIsCloseEnoughForSpawning(player, pos, blockRange)) { + // Paper end - PlayerNaturallySpawnCreaturesEvent return true; } } -@@ -996,7 +_,7 @@ +@@ -993,7 +_,7 @@ Builder builder = ImmutableList.builder(); - for (ServerPlayer serverPlayer : this.playerMap.getAllPlayers()) { -- if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos)) { -+ if (this.playerIsCloseEnoughForSpawning(serverPlayer, chunkPos, 16384.0D)) { // Spigot - builder.add(serverPlayer); + for (ServerPlayer player : this.playerMap.getAllPlayers()) { +- if (this.playerIsCloseEnoughForSpawning(player, pos)) { ++ if (this.playerIsCloseEnoughForSpawning(player, pos, 16384.0)) { // Spigot + builder.add(player); } } -@@ -1005,12 +_,12 @@ +@@ -1002,12 +_,12 @@ } } -- private boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos chunkPos) { -+ private boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos chunkPos, double range) { // Spigot +- private boolean playerIsCloseEnoughForSpawning(final ServerPlayer player, final ChunkPos pos) { ++ private boolean playerIsCloseEnoughForSpawning(final ServerPlayer player, final ChunkPos pos, final double range) { // Spigot if (player.isSpectator()) { return false; } else { - double d = euclideanDistanceSquared(chunkPos, player.position()); -- return d < 16384.0; -+ return d < range; // Spigot + double distanceToChunk = euclideanDistanceSquared(pos, player.position()); +- return distanceToChunk < 16384.0; ++ return distanceToChunk < range; // Spigot } } -@@ -1141,9 +_,19 @@ +@@ -1136,9 +_,19 @@ } - public void addEntity(Entity entity) { + public void addEntity(final Entity entity) { + org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot + // Paper start - ignore and warn about illegal addEntity calls instead of crashing server + if (!entity.valid || entity.level() != this.level || this.entityMap.containsKey(entity.getId())) { @@ -270,77 +273,75 @@ + if (entity instanceof ServerPlayer && ((ServerPlayer) entity).suppressTrackerForLogin) return; // Paper - Fire PlayerJoinEvent when Player is actually ready; Delay adding to tracker until after list packets if (!(entity instanceof EnderDragonPart)) { EntityType type = entity.getType(); - int i = type.clientTrackingRange() * 16; -+ i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot - if (i != 0) { + int range = type.clientTrackingRange() * 16; ++ range = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, range); // Spigot + if (range != 0) { int updateInterval = type.updateInterval(); if (this.entityMap.containsKey(entity.getId())) { -@@ -1167,6 +_,7 @@ +@@ -1162,6 +_,7 @@ } - protected void removeEntity(Entity entity) { + protected void removeEntity(final Entity entity) { + org.spigotmc.AsyncCatcher.catchOp("entity untrack"); // Spigot - if (entity instanceof ServerPlayer serverPlayer) { - this.updatePlayerStatus(serverPlayer, false); - -@@ -1326,10 +_,10 @@ - final Entity entity; - private final int range; - SectionPos lastSectionPos; -- public final Set seenBy = Sets.newIdentityHashSet(); -+ public final Set seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl + if (entity instanceof ServerPlayer player) { + this.updatePlayerStatus(player, false); +@@ -1329,8 +_,8 @@ public TrackedEntity(final Entity entity, final int range, final int updateInterval, final boolean trackDelta) { + Objects.requireNonNull(ChunkMap.this); + super(); +- this.seenBy = Sets.newIdentityHashSet(); - this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, updateInterval, trackDelta, this); ++ this.seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl + this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, updateInterval, trackDelta, this, this.seenBy); // Paper this.entity = entity; this.range = range; this.lastSectionPos = SectionPos.of(entity); -@@ -1376,6 +_,7 @@ +@@ -1377,6 +_,7 @@ } - public void removePlayer(ServerPlayer player) { + public void removePlayer(final ServerPlayer player) { + org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot if (this.seenBy.remove(player.connection)) { this.serverEntity.removePairing(player); if (this.seenBy.isEmpty()) { -@@ -1385,23 +_,45 @@ +@@ -1386,23 +_,47 @@ } - public void updatePlayer(ServerPlayer player) { + public void updatePlayer(final ServerPlayer player) { + org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot if (player != this.entity) { -- Vec3 vec3 = player.position().subtract(this.entity.position()); +- Vec3 deltaToPlayer = player.position().subtract(this.entity.position()); + // Paper start - remove allocation of Vec3D here -+ // Vec3 vec3 = player.position().subtract(this.entity.position()); -+ double vec3_dx = player.getX() - this.entity.getX(); -+ double vec3_dz = player.getZ() - this.entity.getZ(); ++ // Vec3 deltaToPlayer = player.position().subtract(this.entity.position()); ++ double deltaToPlayerX = player.getX() - this.entity.getX(); ++ double deltaToPlayerZ = player.getZ() - this.entity.getZ(); + // Paper end - remove allocation of Vec3D here int playerViewDistance = ChunkMap.this.getPlayerViewDistance(player); - double d = Math.min(this.getEffectiveRange(), playerViewDistance * 16); -- double d1 = vec3.x * vec3.x + vec3.z * vec3.z; -+ double d1 = vec3_dx * vec3_dx + vec3_dz * vec3_dz; // Paper - double d2 = d * d; -- boolean flag = d1 <= d2 -- && this.entity.broadcastToPlayer(player) -- && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z); + double visibleRange = Math.min(this.getEffectiveRange(), playerViewDistance * 16); +- double distanceSquared = deltaToPlayer.x * deltaToPlayer.x + deltaToPlayer.z * deltaToPlayer.z; ++ double distanceSquared = deltaToPlayerX * deltaToPlayerX + deltaToPlayerZ * deltaToPlayerZ; // Paper + double rangeSquared = visibleRange * visibleRange; +- boolean visibleToPlayer = distanceSquared <= rangeSquared + // Paper start - Configurable entity tracking range by Y -+ boolean flag = d1 <= d2; -+ if (flag && level.paperConfig().entities.trackingRangeY.enabled) { ++ boolean visibleToPlayer = distanceSquared <= rangeSquared; ++ if (visibleToPlayer && level.paperConfig().entities.trackingRangeY.enabled) { + double rangeY = level.paperConfig().entities.trackingRangeY.get(this.entity, -1); + if (rangeY != -1) { -+ double vec3_dy = player.getY() - this.entity.getY(); -+ flag = vec3_dy * vec3_dy <= rangeY * rangeY; ++ double deltaToPlayerY = player.getY() - this.entity.getY(); ++ visibleToPlayer = deltaToPlayerY * deltaToPlayerY <= rangeY * rangeY; + } + } -+ flag = flag && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z); ++ visibleToPlayer = visibleToPlayer + && this.entity.broadcastToPlayer(player) + && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x(), this.entity.chunkPosition().z()); + // Paper end - Configurable entity tracking range by Y + // CraftBukkit start - respect vanish API -+ if (flag && !player.getBukkitEntity().canSee(this.entity.getBukkitEntity())) { // Paper - only consider hits -+ flag = false; ++ if (visibleToPlayer && !player.getBukkitEntity().canSee(this.entity.getBukkitEntity())) { // Paper - only consider hits ++ visibleToPlayer = false; + } + // CraftBukkit end - if (flag) { + if (visibleToPlayer) { if (this.seenBy.add(player.connection)) { + // Paper start - entity tracking events + if (io.papermc.paper.event.player.PlayerTrackEntityEvent.getHandlerList().getRegisteredListeners().length == 0 || new io.papermc.paper.event.player.PlayerTrackEntityEvent(player.getBukkitEntity(), this.entity.getBukkitEntity()).callEvent()) { @@ -355,11 +356,11 @@ } } else { this.removePlayer(player); -@@ -1418,6 +_,7 @@ +@@ -1419,6 +_,7 @@ - for (Entity entity : this.entity.getIndirectPassengers()) { - int i1 = entity.getType().clientTrackingRange() * 16; -+ i1 = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i1); // Paper - if (i1 > i) { - i = i1; + for (Entity passenger : this.entity.getIndirectPassengers()) { + int passengerRange = passenger.getType().clientTrackingRange() * 16; ++ passengerRange = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, passengerRange); // Paper + if (passengerRange > effectiveRange) { + effectiveRange = passengerRange; } diff --git a/paper-server/patches/sources/net/minecraft/server/level/DistanceManager.java.patch b/paper-server/patches/sources/net/minecraft/server/level/DistanceManager.java.patch index 0ef527512714..0b09c2876f86 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/DistanceManager.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/DistanceManager.java.patch @@ -1,28 +1,28 @@ --- a/net/minecraft/server/level/DistanceManager.java +++ b/net/minecraft/server/level/DistanceManager.java -@@ -72,6 +_,12 @@ +@@ -73,6 +_,12 @@ } if (!this.chunksToUpdateFutures.isEmpty()) { + // CraftBukkit start - SPIGOT-7780: Call chunk unload events before updateHighestAllowedStatus -+ for (final ChunkHolder chunkHolder : this.chunksToUpdateFutures) { -+ chunkHolder.callEventIfUnloading(chunkMap); ++ for (final ChunkHolder chunksToUpdateFuture : this.chunksToUpdateFutures) { ++ chunksToUpdateFuture.callEventIfUnloading(scheduler); + } + // CraftBukkit end - SPIGOT-7780: Call chunk unload events before updateHighestAllowedStatus + - for (ChunkHolder chunkHolder : this.chunksToUpdateFutures) { - chunkHolder.updateHighestAllowedStatus(chunkMap); + for (ChunkHolder chunksToUpdateFuture : this.chunksToUpdateFutures) { + chunksToUpdateFuture.updateHighestAllowedStatus(scheduler); } -@@ -121,8 +_,10 @@ - ChunkPos chunkPos = sectionPos.chunk(); - long packedChunkPos = chunkPos.toLong(); - ObjectSet set = this.playersPerChunk.get(packedChunkPos); -- set.remove(player); -- if (set.isEmpty()) { +@@ -120,8 +_,10 @@ + ChunkPos chunk = pos.chunk(); + long chunkPos = chunk.pack(); + ObjectSet chunkPlayers = this.playersPerChunk.get(chunkPos); +- chunkPlayers.remove(player); +- if (chunkPlayers.isEmpty()) { + // Paper start - some state corruption happens here, don't crash, clean up gracefully -+ if (set != null) set.remove(player); -+ if (set == null || set.isEmpty()) { ++ if (chunkPlayers != null) chunkPlayers.remove(player); ++ if (chunkPlayers == null || chunkPlayers.isEmpty()) { + // Paper end - some state corruption happens here, don't crash, clean up gracefully - this.playersPerChunk.remove(packedChunkPos); - this.naturalSpawnChunkCounter.update(packedChunkPos, Integer.MAX_VALUE, false); - this.playerTicketManager.update(packedChunkPos, Integer.MAX_VALUE, false); + this.playersPerChunk.remove(chunkPos); + this.naturalSpawnChunkCounter.update(chunkPos, Integer.MAX_VALUE, false); + this.playerTicketManager.update(chunkPos, Integer.MAX_VALUE, false); diff --git a/paper-server/patches/sources/net/minecraft/server/level/PlayerSpawnFinder.java.patch b/paper-server/patches/sources/net/minecraft/server/level/PlayerSpawnFinder.java.patch index ba090aa9250e..b9e3d08db459 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/PlayerSpawnFinder.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/PlayerSpawnFinder.java.patch @@ -3,9 +3,9 @@ @@ -48,7 +_,7 @@ } - public static CompletableFuture findSpawn(ServerLevel level, BlockPos pos) { + public static CompletableFuture findSpawn(final ServerLevel level, final BlockPos spawnSuggestion) { - if (level.dimensionType().hasSkyLight() && level.getServer().getWorldData().getGameType() != GameType.ADVENTURE) { + if (level.dimensionType().hasSkyLight() && level.serverLevelData.getGameType() != GameType.ADVENTURE) { // CraftBukkit - int max = Math.max(0, level.getGameRules().get(GameRules.RESPAWN_RADIUS)); - int floor = Mth.floor(level.getWorldBorder().getDistanceToBorder(pos.getX(), pos.getZ())); - if (floor < max) { + int radius = Math.max(0, level.getGameRules().get(GameRules.RESPAWN_RADIUS)); + int distToBorder = Mth.floor(level.getWorldBorder().getDistanceToBorder(spawnSuggestion.getX(), spawnSuggestion.getZ())); + if (distToBorder < radius) { diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerChunkCache.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerChunkCache.java.patch index 90afddb38210..054ab747a9fd 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerChunkCache.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerChunkCache.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -67,6 +_,7 @@ +@@ -66,6 +_,7 @@ private final TicketStorage ticketStorage; private long lastInhabitedUpdate; public boolean spawnEnemies = true; @@ -8,12 +8,12 @@ private static final int CACHE_SIZE = 4; private final long[] lastChunkPos = new long[4]; private final @Nullable ChunkStatus[] lastChunkStatus = new ChunkStatus[4]; -@@ -75,6 +_,13 @@ +@@ -74,6 +_,13 @@ private final Set chunkHoldersToBroadcast = new ReferenceOpenHashSet<>(); @VisibleForDebug private NaturalSpawner.@Nullable SpawnState lastSpawnState; + // Paper start -+ public final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>(); ++ public final ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2ReferenceHashTable fullChunks = new ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2ReferenceHashTable<>(); + public int getFullChunksCount() { + return this.fullChunks.size(); + } @@ -21,14 +21,31 @@ + // Paper end public ServerChunkCache( - ServerLevel level, -@@ -123,6 +_,64 @@ + final ServerLevel level, +@@ -87,6 +_,7 @@ + final boolean syncWrites, + final ChunkStatusUpdateListener chunkStatusListener, + final Supplier overworldDataStorage ++ , final SavedDataStorage savedDataStorage // Paper - initialize SavedDataStorage earlier + ) { + this.level = level; + this.mainThreadProcessor = new ServerChunkCache.MainThreadExecutor(level); +@@ -99,7 +_,7 @@ + LOGGER.error("Failed to create dimension data storage directory", (Throwable)var14); + } + +- this.savedDataStorage = new SavedDataStorage(dataFolder, fixerUpper, level.registryAccess()); ++ this.savedDataStorage = savedDataStorage; // Paper - initialize SavedDataStorage earlier + this.ticketStorage = this.savedDataStorage.computeIfAbsent(TicketStorage.TYPE); + this.chunkMap = new ChunkMap( + level, +@@ -122,6 +_,64 @@ this.clearCache(); } + // CraftBukkit start - properly implement isChunkLoaded + public boolean isChunkLoaded(int chunkX, int chunkZ) { -+ ChunkHolder chunk = this.chunkMap.getUpdatingChunkIfPresent(ChunkPos.asLong(chunkX, chunkZ)); ++ ChunkHolder chunk = this.chunkMap.getUpdatingChunkIfPresent(ChunkPos.pack(chunkX, chunkZ)); + if (chunk == null) { + return false; + } @@ -46,7 +63,7 @@ + + @Nullable + public ChunkAccess getChunkAtImmediately(int x, int z) { -+ ChunkHolder holder = this.chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z)); ++ ChunkHolder holder = this.chunkMap.getVisibleChunkIfPresent(ChunkPos.pack(x, z)); + if (holder == null) { + return null; + } @@ -66,7 +83,7 @@ + // Note: Partially copied from the getChunkAt method below + @Nullable + public LevelChunk getChunkAtIfCachedImmediately(int x, int z) { -+ long k = ChunkPos.asLong(x, z); ++ long k = ChunkPos.pack(x, z); + + // Note: Bypass cache since we need to check ticket level, and to make this MT-Safe + @@ -80,57 +97,57 @@ + + @Nullable + public LevelChunk getChunkAtIfLoadedImmediately(int x, int z) { -+ return this.fullChunks.get(ChunkPos.asLong(x, z)); ++ return this.fullChunks.get(ChunkPos.pack(x, z)); + } + // Paper end + @Override public ThreadedLevelLightEngine getLightEngine() { return this.lightEngine; -@@ -156,7 +_,7 @@ +@@ -155,7 +_,7 @@ for (int i = 0; i < 4; i++) { - if (packedChunkPos == this.lastChunkPos[i] && chunkStatus == this.lastChunkStatus[i]) { + if (pos == this.lastChunkPos[i] && targetStatus == this.lastChunkStatus[i]) { ChunkAccess chunkAccess = this.lastChunk[i]; -- if (chunkAccess != null || !requireChunk) { +- if (chunkAccess != null || !loadOrGenerate) { + if (chunkAccess != null) { // CraftBukkit - the chunk can become accessible in the meantime TODO for non-null chunks it might also make sense to check that the chunk's state hasn't changed in the meantime return chunkAccess; } } -@@ -165,6 +_,7 @@ - profilerFiller.incrementCounter("getChunkCacheMiss"); - CompletableFuture> chunkFutureMainThread = this.getChunkFutureMainThread(x, z, chunkStatus, requireChunk); - this.mainThreadProcessor.managedBlock(chunkFutureMainThread::isDone); +@@ -164,6 +_,7 @@ + profiler.incrementCounter("getChunkCacheMiss"); + CompletableFuture> serverFuture = this.getChunkFutureMainThread(x, z, targetStatus, loadOrGenerate); + this.mainThreadProcessor.managedBlock(serverFuture::isDone); + // com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x, z); // Paper - Add debug for sync chunk loads - ChunkResult chunkResult = chunkFutureMainThread.join(); - ChunkAccess chunkAccess1 = chunkResult.orElse(null); - if (chunkAccess1 == null && requireChunk) { -@@ -235,7 +_,15 @@ - long packedChunkPos = chunkPos.toLong(); - int i = ChunkLevel.byStatus(chunkStatus); - ChunkHolder visibleChunkIfPresent = this.getVisibleChunkIfPresent(packedChunkPos); -- if (requireChunk) { + ChunkResult chunkResult = serverFuture.join(); + ChunkAccess chunk = chunkResult.orElse(null); + if (chunk == null && loadOrGenerate) { +@@ -236,7 +_,15 @@ + long key = pos.pack(); + int targetTicketLevel = ChunkLevel.byStatus(targetStatus); + ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(key); +- if (loadOrGenerate) { + // CraftBukkit start - don't add new ticket for currently unloading chunk + boolean currentlyUnloading = false; -+ if (visibleChunkIfPresent != null) { -+ FullChunkStatus oldChunkState = ChunkLevel.fullStatus(visibleChunkIfPresent.oldTicketLevel); -+ FullChunkStatus currentChunkState = ChunkLevel.fullStatus(visibleChunkIfPresent.getTicketLevel()); ++ if (chunkHolder != null) { ++ FullChunkStatus oldChunkState = ChunkLevel.fullStatus(chunkHolder.oldTicketLevel); ++ FullChunkStatus currentChunkState = ChunkLevel.fullStatus(chunkHolder.getTicketLevel()); + currentlyUnloading = (oldChunkState.isOrAfter(FullChunkStatus.FULL) && !currentChunkState.isOrAfter(FullChunkStatus.FULL)); + } -+ if (requireChunk && !currentlyUnloading) { ++ if (loadOrGenerate && !currentlyUnloading) { + // CraftBukkit end - this.addTicket(new Ticket(TicketType.UNKNOWN, i), chunkPos); - if (this.chunkAbsent(visibleChunkIfPresent, i)) { - ProfilerFiller profilerFiller = Profiler.get(); -@@ -255,7 +_,7 @@ + this.addTicket(new Ticket(TicketType.UNKNOWN, targetTicketLevel), pos); + if (this.chunkAbsent(chunkHolder, targetTicketLevel)) { + ProfilerFiller profiler = Profiler.get(); +@@ -256,7 +_,7 @@ } - private boolean chunkAbsent(@Nullable ChunkHolder chunkHolder, int status) { -- return chunkHolder == null || chunkHolder.getTicketLevel() > status; -+ return chunkHolder == null || chunkHolder.oldTicketLevel > status; // CraftBukkit using oldTicketLevel for isLoaded checks + private boolean chunkAbsent(final @Nullable ChunkHolder chunkHolder, final int targetTicketLevel) { +- return chunkHolder == null || chunkHolder.getTicketLevel() > targetTicketLevel; ++ return chunkHolder == null || chunkHolder.oldTicketLevel > targetTicketLevel; // CraftBukkit - using oldTicketLevel for isLoaded checks } @Override -@@ -309,17 +_,39 @@ +@@ -310,17 +_,39 @@ @Override public void close() throws IOException { @@ -144,7 +161,7 @@ + this.save(true); + } + // CraftBukkit end - this.dataStorage.close(); + this.savedDataStorage.close(); this.lightEngine.close(); this.chunkMap.close(); } @@ -164,57 +181,57 @@ + // CraftBukkit end + @Override - public void tick(BooleanSupplier hasTimeLeft, boolean tickChunks) { - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("purge"); + public void tick(final BooleanSupplier haveTime, final boolean tickChunks) { + ProfilerFiller profiler = Profiler.get(); + profiler.push("purge"); - if (this.level.tickRateManager().runsNormally() || !tickChunks) { + if (this.level.tickRateManager().runsNormally() || !tickChunks || this.level.spigotConfig.unloadFrozenChunks) { // Spigot this.ticketStorage.purgeStaleTickets(this.chunkMap); } -@@ -375,12 +_,20 @@ - naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap) +@@ -376,12 +_,20 @@ + chunkCount, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap) ); - this.lastSpawnState = spawnState; -- boolean flag = this.level.getGameRules().get(GameRules.SPAWN_MOBS); -+ boolean flag = this.level.getGameRules().get(GameRules.SPAWN_MOBS) && !this.level.players().isEmpty(); // CraftBukkit - int i = this.level.getGameRules().get(GameRules.RANDOM_TICK_SPEED); - List filteredSpawningCategories; -- if (flag) { -- boolean flag1 = this.level.getGameTime() % 400L == 0L; -- filteredSpawningCategories = NaturalSpawner.getFilteredSpawningCategories(spawnState, true, this.spawnEnemies, flag1); -+ if (flag && (this.spawnEnemies || this.spawnFriendlies)) { // Paper + this.lastSpawnState = spawnCookie; +- boolean doMobSpawning = this.level.getGameRules().get(GameRules.SPAWN_MOBS); ++ boolean doMobSpawning = this.level.getGameRules().get(GameRules.SPAWN_MOBS) && !this.level.players().isEmpty(); // CraftBukkit + int tickSpeed = this.level.getGameRules().get(GameRules.RANDOM_TICK_SPEED); + List spawningCategories; +- if (doMobSpawning) { +- boolean spawnPersistent = this.level.getGameTime() % 400L == 0L; +- spawningCategories = NaturalSpawner.getFilteredSpawningCategories(spawnCookie, true, this.spawnEnemies, spawnPersistent); ++ if (doMobSpawning && (this.spawnEnemies || this.spawnFriendlies)) { // Paper + // Paper start - PlayerNaturallySpawnCreaturesEvent -+ for (ServerPlayer entityPlayer : this.level.players()) { -+ int chunkRange = Math.min(level.spigotConfig.mobSpawnRange, entityPlayer.getBukkitEntity().getViewDistance()); ++ for (ServerPlayer player : this.level.players()) { ++ int chunkRange = Math.min(level.spigotConfig.mobSpawnRange, player.getBukkitEntity().getViewDistance()); + chunkRange = Math.min(chunkRange, 8); -+ entityPlayer.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(entityPlayer.getBukkitEntity(), (byte) chunkRange); -+ entityPlayer.playerNaturallySpawnedEvent.callEvent(); ++ player.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(player.getBukkitEntity(), (byte) chunkRange); ++ player.playerNaturallySpawnedEvent.callEvent(); + } + // Paper end - PlayerNaturallySpawnCreaturesEvent -+ boolean flag1 = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit -+ filteredSpawningCategories = NaturalSpawner.getFilteredSpawningCategories(spawnState, this.spawnFriendlies, this.spawnEnemies, flag1, this.level); // CraftBukkit ++ boolean spawnPersistent = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit ++ spawningCategories = NaturalSpawner.getFilteredSpawningCategories(spawnCookie, this.spawnFriendlies, this.spawnEnemies, spawnPersistent, this.level); // CraftBukkit } else { - filteredSpawningCategories = List.of(); + spawningCategories = List.of(); } -@@ -553,7 +_,13 @@ +@@ -556,7 +_,13 @@ @Override - public void setSpawnSettings(boolean spawnSettings) { + public void setSpawnSettings(final boolean spawnEnemies) { + // CraftBukkit start -+ this.setSpawnSettings(spawnSettings, this.spawnFriendlies); ++ this.setSpawnSettings(spawnEnemies, this.spawnFriendlies); + } -+ public void setSpawnSettings(boolean spawnSettings, boolean spawnFriendlies) { - this.spawnEnemies = spawnSettings; ++ public void setSpawnSettings(final boolean spawnEnemies, final boolean spawnFriendlies) { + this.spawnEnemies = spawnEnemies; + this.spawnFriendlies = spawnFriendlies; + // CraftBukkit end } - public String getChunkDebugData(ChunkPos chunkPos) { -@@ -625,12 +_,18 @@ + public String getChunkDebugData(final ChunkPos pos) { +@@ -624,12 +_,18 @@ @Override - public boolean pollTask() { + protected boolean pollTask() { + try { // CraftBukkit - process pending Chunk loadCallback() and unloadCallback() after each run task if (ServerChunkCache.this.runDistanceManagerUpdates()) { return true; diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerEntity.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerEntity.java.patch index d0eb6ed42506..437e3937161b 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java -@@ -65,12 +_,14 @@ +@@ -65,14 +_,16 @@ private Vec3 lastSentMovement; private int tickCount; private int teleportDelay; @@ -11,15 +11,17 @@ private @Nullable List> trackedDataValues; + private final Set trackedPlayers; // Paper -- public ServerEntity(ServerLevel level, Entity entity, int updateInterval, boolean trackDelta, ServerEntity.Synchronizer synchronizer) { -+ public ServerEntity(ServerLevel level, Entity entity, int updateInterval, boolean trackDelta, ServerEntity.Synchronizer synchronizer, final Set trackedPlayers) { // Paper + public ServerEntity( +- final ServerLevel level, final Entity entity, final int updateInterval, final boolean trackDelta, final ServerEntity.Synchronizer synchronizer ++ final ServerLevel level, final Entity entity, final int updateInterval, final boolean trackDelta, final ServerEntity.Synchronizer synchronizer, final Set trackedPlayers // Paper + ) { + this.trackedPlayers = trackedPlayers; // Paper this.level = level; this.synchronizer = synchronizer; this.entity = entity; -@@ -94,16 +_,22 @@ - new ClientboundSetPassengersPacket(this.entity), - serverPlayer1 -> passengers.contains(serverPlayer1) == this.lastPassengers.contains(serverPlayer1) +@@ -95,16 +_,22 @@ + .sendToTrackingPlayersFiltered( + new ClientboundSetPassengersPacket(this.entity), player -> passengers.contains(player) == this.lastPassengers.contains(player) ); + // Paper start - Allow riding players + if (this.entity instanceof ServerPlayer player) { @@ -29,36 +31,36 @@ this.lastPassengers = passengers; } -- if (this.entity instanceof ItemFrame itemFrame && this.tickCount % 10 == 0) { -+ if (!this.trackedPlayers.isEmpty() && this.entity instanceof ItemFrame itemFrame /*&& this.tickCount % 10 == 0*/) { // CraftBukkit - moved tickCount below // Paper - Perf: Only tick item frames if players can see it - ItemStack item = itemFrame.getItem(); -- if (item.getItem() instanceof MapItem) { -- MapId mapId = item.get(DataComponents.MAP_ID); -+ if (this.level.paperConfig().maps.itemFrameCursorUpdateInterval > 0 && this.tickCount % this.level.paperConfig().maps.itemFrameCursorUpdateInterval == 0 && item.getItem() instanceof MapItem) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks // Paper - Make item frame map cursor update interval configurable -+ MapId mapId = itemFrame.cachedMapId; // Paper - Perf: Cache map ids on item frames - MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level); - if (savedData != null) { -- for (ServerPlayer serverPlayer : this.level.players()) { +- if (this.entity instanceof ItemFrame frame && this.tickCount % 10 == 0) { ++ if (!this.trackedPlayers.isEmpty() && this.entity instanceof ItemFrame frame /*&& this.tickCount % 10 == 0*/) { // CraftBukkit - moved tickCount below // Paper - Perf: Only tick item frames if players can see it + ItemStack itemStack = frame.getItem(); +- if (itemStack.getItem() instanceof MapItem) { +- MapId id = itemStack.get(DataComponents.MAP_ID); ++ if (this.level.paperConfig().maps.itemFrameCursorUpdateInterval > 0 && this.tickCount % this.level.paperConfig().maps.itemFrameCursorUpdateInterval == 0 && itemStack.getItem() instanceof MapItem) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks // Paper - Make item frame map cursor update interval configurable ++ MapId id = frame.cachedMapId; // Paper - Perf: Cache map ids on item frames + MapItemSavedData data = MapItem.getSavedData(id, this.level); + if (data != null) { +- for (ServerPlayer player : this.level.players()) { + for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers) { // Paper -+ final ServerPlayer serverPlayer = connection.getPlayer(); // Paper - savedData.tickCarriedBy(serverPlayer, item); - Packet updatePacket = savedData.getUpdatePacket(mapId, serverPlayer); - if (updatePacket != null) { ++ final ServerPlayer player = connection.getPlayer(); // Paper + data.tickCarriedBy(player, itemStack, frame); + Packet packet = data.getUpdatePacket(id, player); + if (packet != null) { @@ -136,7 +_,13 @@ } else { this.teleportDelay++; - Vec3 vec3 = this.entity.trackingPosition(); -- boolean flag1 = this.positionCodec.delta(vec3).lengthSqr() >= 7.6293945E-6F; + Vec3 currentPosition = this.entity.trackingPosition(); +- boolean positionChanged = this.positionCodec.delta(currentPosition).lengthSqr() >= 7.6293945E-6F; + // Paper start - reduce allocation of Vec3D here + Vec3 base = this.positionCodec.base; -+ double vec3_dx = vec3.x - base.x; -+ double vec3_dy = vec3.y - base.y; -+ double vec3_dz = vec3.z - base.z; -+ boolean flag1 = (vec3_dx * vec3_dx + vec3_dy * vec3_dy + vec3_dz * vec3_dz) >= 7.62939453125E-6D; ++ double vec3_dx = currentPosition.x - base.x; ++ double vec3_dy = currentPosition.y - base.y; ++ double vec3_dz = currentPosition.z - base.z; ++ boolean positionChanged = (vec3_dx * vec3_dx + vec3_dy * vec3_dy + vec3_dz * vec3_dz) >= 7.62939453125E-6D; + // Paper end - reduce allocation of Vec3D here Packet packet = null; - boolean flag2 = flag1 || this.tickCount % 60 == 0; - boolean flag3 = false; + boolean pos = positionChanged || this.tickCount % 60 == 0; + boolean sentPosition = false; @@ -218,6 +_,25 @@ this.tickCount++; @@ -86,7 +88,7 @@ this.synchronizer.sendToTrackingPlayersAndSelf(new ClientboundSetEntityMotionPacket(this.entity)); } @@ -269,7 +_,10 @@ - public void sendPairingData(ServerPlayer player, Consumer> consumer) { + public void sendPairingData(final ServerPlayer player, final Consumer> broadcast) { this.entity.updateDataBeforeSync(); if (this.entity.isRemoved()) { - LOGGER.warn("Fetching packet for removed entity {}", this.entity); @@ -96,25 +98,25 @@ + // CraftBukkit end } - Packet addEntityPacket = this.entity.getAddEntityPacket(this); + Packet packet = this.entity.getAddEntityPacket(this); @@ -280,6 +_,11 @@ if (this.entity instanceof LivingEntity livingEntity) { - Collection syncableAttributes = livingEntity.getAttributes().getSyncableAttributes(); + Collection attributes = livingEntity.getAttributes().getSyncableAttributes(); + // CraftBukkit start - If sending own attributes send scaled health instead of current maximum health + if (this.entity.getId() == player.getId()) { -+ ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(syncableAttributes, false); ++ ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(attributes, false); + } + // CraftBukkit end - if (!syncableAttributes.isEmpty()) { - consumer.accept(new ClientboundUpdateAttributesPacket(this.entity.getId(), syncableAttributes)); + if (!attributes.isEmpty()) { + broadcast.accept(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributes)); } @@ -296,8 +_,9 @@ } - if (!list.isEmpty()) { -- consumer.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list)); -+ consumer.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list, true)); // Paper - data sanitization + if (!slots.isEmpty()) { +- broadcast.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), slots)); ++ broadcast.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), slots, true)); // Paper - data sanitization } + ((LivingEntity) this.entity).detectEquipmentUpdates(); // CraftBukkit - SPIGOT-3789: sync again immediately after sending } @@ -122,13 +124,13 @@ if (!this.entity.getPassengers().isEmpty()) { @@ -344,6 +_,11 @@ if (this.entity instanceof LivingEntity) { - Set attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync(); - if (!attributesToSync.isEmpty()) { + Set attributes = ((LivingEntity)this.entity).getAttributes().getAttributesToSync(); + if (!attributes.isEmpty()) { + // CraftBukkit start - Send scaled max health + if (this.entity instanceof ServerPlayer serverPlayer) { -+ serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); ++ serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributes, false); + } + // CraftBukkit end - this.synchronizer.sendToTrackingPlayersAndSelf(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); + this.synchronizer.sendToTrackingPlayersAndSelf(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributes)); } diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch index 387abb411251..607a73d32773 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch @@ -1,33 +1,38 @@ --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -193,7 +_,7 @@ - final List players = Lists.newArrayList(); +@@ -194,7 +_,7 @@ + private final List players = Lists.newArrayList(); public final ServerChunkCache chunkSource; private final MinecraftServer server; - public final ServerLevelData serverLevelData; -+ public final net.minecraft.world.level.storage.PrimaryLevelData serverLevelData; // CraftBukkit - type - final EntityTickList entityTickList = new EntityTickList(); ++ public final io.papermc.paper.world.saveddata.PaperLevelOverrides serverLevelData; // Paper - type + private final EntityTickList entityTickList = new EntityTickList(); private final ServerWaypointManager waypointManager; - private final EnvironmentAttributeSystem environmentAttributes; -@@ -221,25 +_,170 @@ - private final RandomSequences randomSequences; - final LevelDebugSynchronizers debugSynchronizers = new LevelDebugSynchronizers(this); + private EnvironmentAttributeSystem environmentAttributes; +@@ -221,24 +_,192 @@ + private final boolean tickTime; + private final LevelDebugSynchronizers debugSynchronizers = new LevelDebugSynchronizers(this); + // CraftBukkit start -+ public final LevelStorageSource.LevelStorageAccess levelStorageAccess; ++ private final ResourceKey typeKey; ++ public final String bukkitName; + public final UUID uuid; + public final net.minecraft.server.level.progress.LevelLoadListener levelLoadListener; ++ private final net.minecraft.world.level.gamerules.GameRules gameRules; ++ private final WeatherData weatherData; ++ public final net.minecraft.world.level.timers.TimerQueue scheduledEvents; ++ public final WorldGenSettings worldGenSettings; + public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent + public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent + + @Override + public @Nullable LevelChunk getChunkIfLoaded(int x, int z) { -+ return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately ++ return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkAtIfLoadedImmediately + } + + @Override + public ResourceKey getTypeKey() { -+ return this.levelStorageAccess.dimensionType; ++ return this.typeKey; + } + + // Paper start @@ -35,11 +40,11 @@ + // copied code from collision methods, so that we can guarantee that they won't load chunks (we don't override + // CollisionGetter methods for VoxelShapes) + // be more strict too, add a block (dumb plugins in move events?) -+ int minBlockX = Mth.floor(box.minX - 1.0E-7D) - 3; -+ int maxBlockX = Mth.floor(box.maxX + 1.0E-7D) + 3; ++ int minBlockX = Mth.floor(box.minX - 1.0E-7) - 3; ++ int maxBlockX = Mth.floor(box.maxX + 1.0E-7) + 3; + -+ int minBlockZ = Mth.floor(box.minZ - 1.0E-7D) - 3; -+ int maxBlockZ = Mth.floor(box.maxZ + 1.0E-7D) + 3; ++ int minBlockZ = Mth.floor(box.minZ - 1.0E-7) - 3; ++ int maxBlockZ = Mth.floor(box.maxZ + 1.0E-7) + 3; + + int minChunkX = minBlockX >> 4; + int maxChunkX = maxBlockX >> 4; @@ -68,11 +73,11 @@ + }); + return; + } -+ int minBlockX = Mth.floor(box.minX - 1.0E-7D) - 3; -+ int minBlockZ = Mth.floor(box.minZ - 1.0E-7D) - 3; ++ int minBlockX = Mth.floor(box.minX - 1.0E-7) - 3; ++ int minBlockZ = Mth.floor(box.minZ - 1.0E-7) - 3; + -+ int maxBlockX = Mth.floor(box.maxX + 1.0E-7D) + 3; -+ int maxBlockZ = Mth.floor(box.maxZ + 1.0E-7D) + 3; ++ int maxBlockX = Mth.floor(box.maxX + 1.0E-7) + 3; ++ int maxBlockZ = Mth.floor(box.maxZ + 1.0E-7) + 3; + + int minChunkX = minBlockX >> 4; + int minChunkZ = minBlockZ >> 4; @@ -95,7 +100,7 @@ + + java.util.function.Consumer consumer = (net.minecraft.world.level.chunk.ChunkAccess chunk) -> { + if (chunk != null) { -+ int ticketLevel = Math.max(33, chunkProvider.chunkMap.getUpdatingChunkIfPresent(chunk.getPos().toLong()).getTicketLevel()); ++ int ticketLevel = Math.max(33, chunkProvider.chunkMap.getUpdatingChunkIfPresent(chunk.getPos().pack()).getTicketLevel()); + ret.add(chunk); + ticketLevels.add(ticketLevel); + chunkProvider.addTicketAtLevel(TicketType.FUTURE_AWAIT, chunk.getPos(), ticketLevel); @@ -135,89 +140,113 @@ + // Paper end - optimise getPlayerByUUID + public ServerLevel( - MinecraftServer server, - Executor dispatcher, - LevelStorageSource.LevelStorageAccess levelStorageAccess, -- ServerLevelData serverLevelData, -+ net.minecraft.world.level.storage.PrimaryLevelData serverLevelData, // CraftBukkit - ResourceKey dimension, - LevelStem levelStem, - boolean isDebug, - long biomeZoomSeed, - List customSpawners, - boolean tickTime, -- @Nullable RandomSequences randomSequences -+ @Nullable RandomSequences randomSequences, -+ org.bukkit.World.Environment env, // CraftBukkit -+ org.bukkit.generator.ChunkGenerator gen, // CraftBukkit -+ org.bukkit.generator.BiomeProvider biomeProvider // CraftBukkit + final MinecraftServer server, + final Executor executor, + final LevelStorageSource.LevelStorageAccess levelStorage, +- final ServerLevelData levelData, ++ final WorldGenSettings worldGenSettings, // CraftBukkit + final ResourceKey dimension, + final LevelStem levelStem, + final boolean isDebug, + final long biomeZoomSeed, + final List customSpawners, + final boolean tickTime ++ // Paper start - add parameters ++ , ResourceKey typeKey, ++ org.bukkit.World.Environment env, ++ org.bukkit.generator.ChunkGenerator gen, ++ org.bukkit.generator.BiomeProvider biomeProvider, ++ SavedDataStorage savedDataStorage, ++ io.papermc.paper.world.PaperWorldLoader.LoadedWorldData loadedWorldData ++ // Paper end - add parameters ) { -- super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates()); +- super(levelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates()); + // CraftBukkit start -+ super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(levelStorageAccess.levelDirectory.path(), serverLevelData.getLevelName(), dimension.identifier(), spigotConfig, server.registryAccess(), serverLevelData.getGameRules()))); // Paper - create paper world configs -+ this.levelStorageAccess = levelStorageAccess; -+ this.uuid = org.bukkit.craftbukkit.util.WorldUUID.getOrCreate(levelStorageAccess.levelDirectory.path().toFile()); ++ final io.papermc.paper.world.saveddata.PaperLevelOverrides levelData = loadedWorldData.levelOverrides(); ++ savedDataStorage.set(io.papermc.paper.world.saveddata.PaperLevelOverrides.TYPE, levelData); ++ savedDataStorage.set(io.papermc.paper.world.saveddata.PaperWorldMetadata.TYPE, new io.papermc.paper.world.saveddata.PaperWorldMetadata(loadedWorldData.uuid())); ++ savedDataStorage.set(io.papermc.paper.world.saveddata.PaperWorldPDC.TYPE, loadedWorldData.pdc() == null ? io.papermc.paper.world.saveddata.PaperWorldPDC.TYPE.constructor().get() : loadedWorldData.pdc()); ++ final GameRules gameRules = new GameRules(server.getWorldData().enabledFeatures(), savedDataStorage.computeIfAbsent(net.minecraft.world.level.gamerules.GameRuleMap.TYPE)); ++ this.gameRules = gameRules; ++ super(levelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates(), loadedWorldData.bukkitName(), gen, biomeProvider, env, spigotConfig -> server.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(server.storageSource.getDimensionPath(dimension), loadedWorldData.bukkitName(), dimension.identifier(), spigotConfig, server.registryAccess(), gameRules))); // Paper - create paper world configs ++ this.weatherData = savedDataStorage.computeIfAbsent(WeatherData.TYPE); ++ this.weatherData.setLevel(this); ++ this.typeKey = typeKey; ++ this.bukkitName = loadedWorldData.bukkitName(); ++ this.uuid = loadedWorldData.uuid(); + this.levelLoadListener = new net.minecraft.server.level.progress.LoggingLevelLoadListener(false, this); ++ this.worldGenSettings = worldGenSettings; ++ this.scheduledEvents = savedDataStorage.computeIfAbsent(net.minecraft.world.level.timers.TimerQueue.TYPE); + // CraftBukkit end this.tickTime = tickTime; this.server = server; this.customSpawners = customSpawners; - this.serverLevelData = serverLevelData; - ChunkGenerator chunkGenerator = levelStem.generator(); + this.serverLevelData = levelData; + ChunkGenerator generator = levelStem.generator(); + // CraftBukkit start -+ this.serverLevelData.setWorld(this); ++ if (loadedWorldData.pdc() != null) { ++ this.getWorld().readBukkitValues(loadedWorldData.pdc().persistentData().toTagCompound()); ++ } + + if (biomeProvider != null) { -+ net.minecraft.world.level.biome.BiomeSource biomeSource = new org.bukkit.craftbukkit.generator.CustomWorldChunkManager(this.getWorld(), biomeProvider, this.server.registryAccess().lookupOrThrow(Registries.BIOME), chunkGenerator.getBiomeSource()); // Paper - add vanillaBiomeProvider -+ if (chunkGenerator instanceof net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator noiseBased) { -+ chunkGenerator = new net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator(biomeSource, noiseBased.settings); -+ } else if (chunkGenerator instanceof net.minecraft.world.level.levelgen.FlatLevelSource flatLevel) { -+ chunkGenerator = new net.minecraft.world.level.levelgen.FlatLevelSource(flatLevel.settings(), biomeSource); ++ net.minecraft.world.level.biome.BiomeSource biomeSource = new org.bukkit.craftbukkit.generator.CustomWorldChunkManager(this.getWorld(), biomeProvider, generator.getBiomeSource()); // Paper - add vanillaBiomeProvider ++ if (generator instanceof net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator noiseBased) { ++ generator = new net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator(biomeSource, noiseBased.settings); ++ } else if (generator instanceof net.minecraft.world.level.levelgen.FlatLevelSource flatLevel) { ++ generator = new net.minecraft.world.level.levelgen.FlatLevelSource(flatLevel.settings(), biomeSource); + } + } + + if (gen != null) { -+ chunkGenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkGenerator, gen); ++ generator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, generator, gen); + } + // CraftBukkit end - boolean flag = server.forceSynchronousWrites(); + boolean syncWrites = server.forceSynchronousWrites(); DataFixer fixerUpper = server.getFixerUpper(); - EntityPersistentStorage entityPersistentStorage = new EntityStorage( -@@ -261,8 +_,8 @@ + EntityPersistentStorage entityStorage = new EntityStorage( +@@ -260,16 +_,17 @@ server.getStructureManager(), - dispatcher, - chunkGenerator, + executor, + generator, - server.getPlayerList().getViewDistance(), - server.getPlayerList().getSimulationDistance(), + this.spigotConfig.viewDistance, // Spigot + this.spigotConfig.simulationDistance, // Spigot - flag, + syncWrites, this.entityManager::updateChunkStatus, () -> server.overworld().getDataStorage() -@@ -283,7 +_,7 @@ ++ , savedDataStorage // Paper - initialize SavedDataStorage earlier + ); + this.chunkSource.getGeneratorState().ensureStructuresGenerated(); + this.portalForcer = new PortalForcer(this); + if (this.canHaveWeather()) { +- this.prepareWeather(server.getWeatherData()); ++ this.prepareWeather(this.weatherData); // Paper - per-level WeatherData + } + + this.raids = this.getDataStorage().computeIfAbsent(Raids.TYPE); +@@ -277,14 +_,13 @@ + levelData.setGameType(server.getDefaultGameType()); + } + +- WorldGenSettings worldGenSettings = server.getWorldGenSettings(); + WorldOptions options = worldGenSettings.options(); + long seed = options.seed(); + this.structureCheck = new StructureCheck( this.chunkSource.chunkScanner(), this.registryAccess(), server.getStructureManager(), - dimension, + getTypeKey(), // Paper - Fix missing CB diff - chunkGenerator, + generator, this.chunkSource.randomState(), this, -@@ -291,9 +_,9 @@ - seed, - fixerUpper - ); -- this.structureManager = new StructureManager(this, server.getWorldData().worldGenOptions(), this.structureCheck); -- if (this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END)) { -- this.dragonFight = new EndDragonFight(this, seed, server.getWorldData().endDragonFightData()); -+ this.structureManager = new StructureManager(this, this.serverLevelData.worldGenOptions(), this.structureCheck); // CraftBukkit -+ if (this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END) || env == org.bukkit.World.Environment.THE_END) { // CraftBukkit - Allow to create EnderDragonBattle in default and custom END -+ this.dragonFight = new EndDragonFight(this, this.serverLevelData.worldGenOptions().seed(), this.serverLevelData.endDragonFightData()); // CraftBukkit - } else { - this.dragonFight = null; - } -@@ -304,7 +_,15 @@ - this.waypointManager = new ServerWaypointManager(); +@@ -300,10 +_,18 @@ + + this.sleepStatus = new SleepStatus(); + this.gameEventDispatcher = new GameEventDispatcher(this); +- this.waypointManager = new ServerWaypointManager(); ++ this.waypointManager = new ServerWaypointManager(this); // Paper - optimize ServerWaypointManager with locator bar disabled this.environmentAttributes = EnvironmentAttributeSystem.builder().addDefaultLayers(this).build(); this.updateSkyBrightness(); - } @@ -233,175 +262,170 @@ @Deprecated @VisibleForTesting -@@ -316,8 +_,8 @@ - this.serverLevelData.setClearWeatherTime(clearTime); - this.serverLevelData.setRainTime(weatherTime); - this.serverLevelData.setThunderTime(weatherTime); -- this.serverLevelData.setRaining(isRaining); -- this.serverLevelData.setThundering(isThundering); -+ this.serverLevelData.setRaining(isRaining, org.bukkit.event.weather.WeatherChangeEvent.Cause.COMMAND); // Paper - Add cause to Weather/ThunderChangeEvents -+ this.serverLevelData.setThundering(isThundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.COMMAND); // Paper - Add cause to Weather/ThunderChangeEvents - } - - @Override -@@ -349,12 +_,25 @@ - - int i = this.getGameRules().get(GameRules.PLAYERS_SLEEPING_PERCENTAGE); - if (this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) { -+ // Paper start - create time skip event - move up calculations -+ final long newDayTime = this.levelData.getDayTime() + 24000L; -+ org.bukkit.event.world.TimeSkipEvent event = new org.bukkit.event.world.TimeSkipEvent( -+ this.getWorld(), -+ org.bukkit.event.world.TimeSkipEvent.SkipReason.NIGHT_SKIP, -+ (newDayTime - newDayTime % 24000L) - this.getDayTime() -+ ); -+ // Paper end - create time skip event - move up calculations - if (this.getGameRules().get(GameRules.ADVANCE_TIME)) { -- long l = this.levelData.getDayTime() + 24000L; -- this.setDayTime(l - l % 24000L); -+ // Paper start - call time skip event if gamerule is enabled -+ // long l = this.levelData.getDayTime() + 24000L; // Paper - diff on change to above - newDayTime -+ // this.setDayTime(l - l % 24000L); // Paper - diff on change to above - event param +@@ -354,11 +_,24 @@ + int percentage = this.getGameRules().get(GameRules.PLAYERS_SLEEPING_PERCENTAGE); + if (this.sleepStatus.areEnoughSleeping(percentage) && this.sleepStatus.areEnoughDeepSleeping(percentage, this.players)) { + Optional> defaultClock = this.dimensionType().defaultClock(); ++ org.bukkit.event.world.TimeSkipEvent event = null; // Paper - time skip event + if (this.getGameRules().get(GameRules.ADVANCE_TIME) && defaultClock.isPresent()) { +- this.server.clockManager().moveToTimeMarker(defaultClock.get(), ClockTimeMarkers.WAKE_UP_FROM_SLEEP); ++ // Paper start - time skip event ++ long currentTime = this.server.clockManager().getTotalTicks(defaultClock.get()); ++ long delta = this.server.clockManager().getTotalTicksToTimeMarker(defaultClock.get(), ClockTimeMarkers.WAKE_UP_FROM_SLEEP).orElse(0L) - currentTime; ++ event = new org.bukkit.event.world.TimeSkipEvent( ++ this.getWorld(), ++ org.bukkit.event.world.TimeSkipEvent.SkipReason.NIGHT_SKIP, ++ delta ++ ); ++ + if (event.callEvent()) { -+ this.setDayTime(this.getDayTime() + event.getSkipAmount()); ++ this.server.clockManager().setTotalTicks(defaultClock.get(), currentTime + event.getSkipAmount()); // TODO - snapshot - per world time + } -+ // Paper end - call time skip event if gamerule is enabled ++ // Paper end - time skip event } - this.wakeUpAllPlayers(); -+ if (!event.isCancelled()) this.wakeUpAllPlayers(); // Paper - only wake up players if time skip event is not cancelled ++ if (event == null || !event.isCancelled()) this.wakeUpAllPlayers(); // Paper - time skip event - only wake up players if time skip event is not cancelled if (this.getGameRules().get(GameRules.ADVANCE_WEATHER) && this.isRaining()) { this.resetWeatherCycle(); } -@@ -369,9 +_,9 @@ - if (!this.isDebug() && runsNormally) { - long l = this.getGameTime(); - profilerFiller.push("blockTicks"); -- this.blockTicks.tick(l, 65536, this::tickBlock); -+ this.blockTicks.tick(l, paperConfig().environment.maxBlockTicks, this::tickBlock); // Paper - configurable max block ticks - profilerFiller.popPush("fluidTicks"); -- this.fluidTicks.tick(l, 65536, this::tickFluid); -+ this.fluidTicks.tick(l, paperConfig().environment.maxFluidTicks, this::tickFluid); // Paper - configurable max fluid ticks - profilerFiller.pop(); +@@ -373,9 +_,9 @@ + if (!this.isDebug() && runs) { + long tick = this.getGameTime(); + profiler.push("blockTicks"); +- this.blockTicks.tick(tick, 65536, this::tickBlock); ++ this.blockTicks.tick(tick, paperConfig().environment.maxBlockTicks, this::tickBlock); // Paper - configurable max block ticks + profiler.popPush("fluidTicks"); +- this.fluidTicks.tick(tick, 65536, this::tickFluid); ++ this.fluidTicks.tick(tick, paperConfig().environment.maxFluidTicks, this::tickFluid); // Paper - configurable max fluid ticks + profiler.pop(); } -@@ -389,7 +_,7 @@ +@@ -393,7 +_,7 @@ this.handlingTick = false; - profilerFiller.pop(); -- boolean hasActiveTickets = this.chunkSource.hasActiveTickets(); -+ boolean hasActiveTickets = !paperConfig().unsupportedSettings.disableWorldTickingWhenEmpty || this.chunkSource.hasActiveTickets(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players // Paper - restore this - if (hasActiveTickets) { + profiler.pop(); +- boolean isActive = this.chunkSource.hasActiveTickets(); ++ boolean isActive = !paperConfig().unsupportedSettings.disableWorldTickingWhenEmpty || this.chunkSource.hasActiveTickets(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players // Paper - restore this + if (isActive) { this.resetEmptyTime(); } -@@ -498,11 +_,13 @@ - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("iceandsnow"); +@@ -468,7 +_,7 @@ + long time = this.levelData.getGameTime() + 1L; + this.serverLevelData.setGameTime(time); + Profiler.get().push("scheduledFunctions"); +- this.server.getScheduledEvents().tick(this.server, time); ++ this.scheduledEvents.tick(this.server, time); // Paper - per-level scheduledEvents + Profiler.get().pop(); + } + } +@@ -491,11 +_,13 @@ + ProfilerFiller profiler = Profiler.get(); + profiler.push("iceandsnow"); + if (!this.paperConfig().environment.disableIceAndSnow) { // Paper - Option to disable ice and snow - for (int i = 0; i < randomTickSpeed; i++) { + for (int i = 0; i < tickSpeed; i++) { if (this.random.nextInt(48) == 0) { - this.tickPrecipitation(this.getBlockRandomPos(minBlockX, 0, minBlockZ, 15)); + this.tickPrecipitation(this.getBlockRandomPos(minX, 0, minZ, 15)); } } + } // Paper - Option to disable ice and snow - profilerFiller.popPush("tickBlocks"); - if (randomTickSpeed > 0) { -@@ -545,12 +_,12 @@ - int minBlockZ = pos.getMinBlockZ(); - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("thunder"); -- if (isRaining && this.isThundering() && this.random.nextInt(100000) == 0) { -+ if (!this.paperConfig().environment.disableThunder && isRaining && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - Option to disable thunder - BlockPos blockPos = this.findLightningTargetAround(this.getBlockRandomPos(minBlockX, 0, minBlockZ, 15)); - if (this.isRainingAt(blockPos)) { - DifficultyInstance currentDifficultyAt = this.getCurrentDifficultyAt(blockPos); - boolean flag = this.getGameRules().get(GameRules.SPAWN_MOBS) -- && this.random.nextDouble() < currentDifficultyAt.getEffectiveDifficulty() * 0.01 -+ && this.random.nextDouble() < currentDifficultyAt.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01) // Paper - Configurable spawn chances for skeleton horses - && !this.getBlockState(blockPos.below()).is(BlockTags.LIGHTNING_RODS); - if (flag) { - SkeletonHorse skeletonHorse = EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT); -@@ -558,7 +_,7 @@ - skeletonHorse.setTrap(true); - skeletonHorse.setAge(0); - skeletonHorse.setPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()); -- this.addFreshEntity(skeletonHorse); -+ this.addFreshEntity(skeletonHorse, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit + profiler.popPush("tickBlocks"); + if (tickSpeed > 0) { +@@ -536,12 +_,12 @@ + int minZ = chunkPos.getMinBlockZ(); + ProfilerFiller profiler = Profiler.get(); + profiler.push("thunder"); +- if (raining && this.isThundering() && this.random.nextInt(100000) == 0) { ++ if (!this.paperConfig().environment.disableThunder && raining && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - Option to disable thunder + BlockPos pos = this.findLightningTargetAround(this.getBlockRandomPos(minX, 0, minZ, 15)); + if (this.isRainingAt(pos)) { + DifficultyInstance difficulty = this.getCurrentDifficultyAt(pos); + boolean isTrap = this.getGameRules().get(GameRules.SPAWN_MOBS) +- && this.random.nextDouble() < difficulty.getEffectiveDifficulty() * 0.01 ++ && this.random.nextDouble() < difficulty.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01) // Paper - Configurable spawn chances for skeleton horses + && !this.getBlockState(pos.below()).is(BlockTags.LIGHTNING_RODS); + if (isTrap) { + SkeletonHorse horse = EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT); +@@ -549,7 +_,7 @@ + horse.setTrap(true); + horse.setAge(0); + horse.setPos(pos.getX(), pos.getY(), pos.getZ()); +- this.addFreshEntity(horse); ++ this.addFreshEntity(horse, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit } } -@@ -566,7 +_,7 @@ - if (lightningBolt != null) { - lightningBolt.snapTo(Vec3.atBottomCenterOf(blockPos)); - lightningBolt.setVisualOnly(flag); -- this.addFreshEntity(lightningBolt); -+ this.strikeLightning(lightningBolt, org.bukkit.event.weather.LightningStrikeEvent.Cause.WEATHER); // CraftBukkit +@@ -557,7 +_,7 @@ + if (bolt != null) { + bolt.snapTo(Vec3.atBottomCenterOf(pos)); + bolt.setVisualOnly(isTrap); +- this.addFreshEntity(bolt); ++ this.strikeLightning(bolt, org.bukkit.event.weather.LightningStrikeEvent.Cause.WEATHER); // CraftBukkit } } } -@@ -580,7 +_,7 @@ - BlockPos blockPos = heightmapPos.below(); - Biome biome = this.getBiome(heightmapPos).value(); - if (biome.shouldFreeze(this, blockPos)) { -- this.setBlockAndUpdate(blockPos, Blocks.ICE.defaultBlockState()); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockPos, Blocks.ICE.defaultBlockState(), Block.UPDATE_ALL, null); // CraftBukkit +@@ -571,7 +_,7 @@ + BlockPos belowPos = topPos.below(); + Biome biome = this.getBiome(topPos).value(); + if (biome.shouldFreeze(this, belowPos)) { +- this.setBlockAndUpdate(belowPos, Blocks.ICE.defaultBlockState()); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, belowPos, Blocks.ICE.defaultBlockState(), Block.UPDATE_ALL, null); // CraftBukkit } if (this.isRaining()) { -@@ -592,10 +_,10 @@ - if (layersValue < Math.min(i, 8)) { - BlockState blockState1 = blockState.setValue(SnowLayerBlock.LAYERS, layersValue + 1); - Block.pushEntitiesUp(blockState, blockState1, this, heightmapPos); -- this.setBlockAndUpdate(heightmapPos, blockState1); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, heightmapPos, blockState1, Block.UPDATE_ALL, null); // CraftBukkit +@@ -583,10 +_,10 @@ + if (currentLayers < Math.min(maxHeight, 8)) { + BlockState newState = state.setValue(SnowLayerBlock.LAYERS, currentLayers + 1); + Block.pushEntitiesUp(state, newState, this, topPos); +- this.setBlockAndUpdate(topPos, newState); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, topPos, newState, Block.UPDATE_ALL, null); // CraftBukkit } } else { -- this.setBlockAndUpdate(heightmapPos, Blocks.SNOW.defaultBlockState()); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, heightmapPos, Blocks.SNOW.defaultBlockState(), Block.UPDATE_ALL, null); // CraftBukkit +- this.setBlockAndUpdate(topPos, Blocks.SNOW.defaultBlockState()); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, topPos, Blocks.SNOW.defaultBlockState(), Block.UPDATE_ALL, null); // CraftBukkit } } -@@ -620,6 +_,12 @@ +@@ -611,16 +_,24 @@ } - protected BlockPos findLightningTargetAround(BlockPos pos) { + protected BlockPos findLightningTargetAround(final BlockPos pos) { + // Paper start - Add methods to find targets for lightning strikes + return this.findLightningTargetAround(pos, false); + } + -+ public BlockPos findLightningTargetAround(BlockPos pos, boolean returnNullWhenNoTarget) { ++ @org.jetbrains.annotations.Contract("_, false -> !null") ++ public @Nullable BlockPos findLightningTargetAround(final BlockPos pos, boolean returnNullWhenNoTarget) { + // Paper end - Add methods to find targets for lightning strikes - BlockPos heightmapPos = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos); - Optional optional = this.findLightningRod(heightmapPos); - if (optional.isPresent()) { -@@ -627,11 +_,12 @@ + BlockPos center = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos); + Optional lightningRodTarget = this.findLightningRod(center); + if (lightningRodTarget.isPresent()) { + return lightningRodTarget.get(); } else { - AABB aabb = AABB.encapsulatingFullBlocks(heightmapPos, heightmapPos.atY(this.getMaxY() + 1)).inflate(3.0); - List entitiesOfClass = this.getEntitiesOfClass( -- LivingEntity.class, aabb, entity -> entity.isAlive() && this.canSeeSky(entity.blockPosition()) -+ LivingEntity.class, aabb, entity -> entity.isAlive() && this.canSeeSky(entity.blockPosition()) && !entity.isSpectator() // Paper - Fix lightning being able to hit spectators (MC-262422) - ); - if (!entitiesOfClass.isEmpty()) { - return entitiesOfClass.get(this.random.nextInt(entitiesOfClass.size())).blockPosition(); + AABB search = AABB.encapsulatingFullBlocks(center, center.atY(this.getMaxY() + 1)).inflate(3.0); +- List entities = this.getEntitiesOfClass(LivingEntity.class, search, input -> input.isAlive() && this.canSeeSky(input.blockPosition())); ++ List entities = this.getEntitiesOfClass(LivingEntity.class, search, input -> input.isAlive() && this.canSeeSky(input.blockPosition()) && !input.isSpectator()); // Paper - Fix lightning being able to hit spectators (MC-262422) + if (!entities.isEmpty()) { + return entities.get(this.random.nextInt(entities.size())).blockPosition(); } else { + if (returnNullWhenNoTarget) return null; // Paper - Add methods to find targets for lightning strikes - if (heightmapPos.getY() == this.getMinY() - 1) { - heightmapPos = heightmapPos.above(2); + if (center.getY() == this.getMinY() - 1) { + center = center.above(2); } -@@ -740,8 +_,8 @@ - this.serverLevelData.setThunderTime(thunderTime); - this.serverLevelData.setRainTime(rainTime); - this.serverLevelData.setClearWeatherTime(clearWeatherTime); -- this.serverLevelData.setThundering(isThundering); -- this.serverLevelData.setRaining(isRaining1); -+ this.serverLevelData.setThundering(isThundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.NATURAL); // Paper - Add cause to Weather/ThunderChangeEvents -+ this.serverLevelData.setRaining(isRaining1, org.bukkit.event.weather.WeatherChangeEvent.Cause.NATURAL); // Paper - Add cause to Weather/ThunderChangeEvents +@@ -739,8 +_,8 @@ + weatherData.setThunderTime(thunderTime); + weatherData.setRainTime(rainTime); + weatherData.setClearWeatherTime(clearWeatherTime); +- weatherData.setThundering(thundering); +- weatherData.setRaining(raining); ++ weatherData.setThundering(thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.NATURAL); // Paper - Add cause to Weather/ThunderChangeEvents ++ weatherData.setRaining(raining, org.bukkit.event.weather.WeatherChangeEvent.Cause.NATURAL); // Paper - Add cause to Weather/ThunderChangeEvents } this.oThunderLevel = this.thunderLevel; -@@ -762,6 +_,7 @@ +@@ -761,6 +_,7 @@ this.rainLevel = Mth.clamp(this.rainLevel, 0.0F, 1.0F); } @@ -409,7 +433,7 @@ if (this.oRainLevel != this.rainLevel) { this.server .getPlayerList() -@@ -784,14 +_,47 @@ +@@ -783,15 +_,48 @@ this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel)); this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel)); } @@ -420,11 +444,11 @@ + } + } + -+ if (isRaining != this.isRaining()) { ++ if (wasRaining != this.isRaining()) { + // Only send weather packets to those affected + for (ServerPlayer player : this.players) { + if (player.level() == this) { -+ player.setPlayerWeather((!isRaining ? org.bukkit.WeatherType.DOWNFALL : org.bukkit.WeatherType.CLEAR), false); ++ player.setPlayerWeather((!wasRaining ? org.bukkit.WeatherType.DOWNFALL : org.bukkit.WeatherType.CLEAR), false); + } + } + } @@ -438,24 +462,25 @@ @VisibleForTesting public void resetWeatherCycle() { -- this.serverLevelData.setRainTime(0); -- this.serverLevelData.setRaining(false); -- this.serverLevelData.setThunderTime(0); -- this.serverLevelData.setThundering(false); + WeatherData weatherData = this.getWeatherData(); +- weatherData.setRainTime(0); +- weatherData.setRaining(false); +- weatherData.setThunderTime(0); +- weatherData.setThundering(false); + // CraftBukkit start -+ this.serverLevelData.setRaining(false, org.bukkit.event.weather.WeatherChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents ++ weatherData.setRaining(false, org.bukkit.event.weather.WeatherChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents + // If we stop due to everyone sleeping we should reset the weather duration to some other random value. + // Not that everyone ever manages to get the whole server to sleep at the same time.... -+ if (!this.serverLevelData.isRaining()) { -+ this.serverLevelData.setRainTime(0); ++ if (!weatherData.isRaining()) { ++ weatherData.setRainTime(0); + } + // CraftBukkit end -+ this.serverLevelData.setThundering(false, org.bukkit.event.weather.ThunderChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents ++ weatherData.setThundering(false, org.bukkit.event.weather.ThunderChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents + // CraftBukkit start + // If we stop due to everyone sleeping we should reset the weather duration to some other random value. + // Not that everyone ever manages to get the whole server to sleep at the same time.... -+ if (!this.serverLevelData.isThundering()) { -+ this.serverLevelData.setThunderTime(0); ++ if (!weatherData.isThundering()) { ++ weatherData.setThunderTime(0); + } + // CraftBukkit end } @@ -477,7 +502,7 @@ + } + // Paper end - log detailed entity tick information + - public void tickNonPassenger(Entity entity) { + public void tickNonPassenger(final Entity entity) { + // Paper start - log detailed entity tick information + ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot tick an entity off-main"); + try { @@ -486,17 +511,17 @@ + } + // Paper end - log detailed entity tick information entity.setOldPosAndRot(); - ProfilerFiller profilerFiller = Profiler.get(); + ProfilerFiller profiler = Profiler.get(); entity.tickCount++; + entity.totalEntityAge++; // Paper - age-like counter for all entities - profilerFiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString()); - profilerFiller.incrementCounter("tickNonPassenger"); + profiler.push(entity.typeHolder()::getRegisteredName); + profiler.incrementCounter("tickNonPassenger"); entity.tick(); + entity.postTick(); // CraftBukkit - profilerFiller.pop(); + profiler.pop(); - for (Entity entity1 : entity.getPassengers()) { - this.tickPassenger(entity, entity1); + for (Entity passenger : entity.getPassengers()) { + this.tickPassenger(entity, passenger); } + // Paper start - log detailed entity tick information + } finally { @@ -507,75 +532,63 @@ + // Paper end - log detailed entity tick information } - private void tickPassenger(Entity ridingEntity, Entity passengerEntity) { + private void tickPassenger(final Entity vehicle, final Entity entity) { @@ -833,10 +_,12 @@ - } else if (passengerEntity instanceof Player || this.entityTickList.contains(passengerEntity)) { - passengerEntity.setOldPosAndRot(); - passengerEntity.tickCount++; -+ passengerEntity.totalEntityAge++; // Paper - age-like counter for all entities - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(passengerEntity.getType()).toString()); - profilerFiller.incrementCounter("tickPassenger"); - passengerEntity.rideTick(); -+ passengerEntity.postTick(); // CraftBukkit - profilerFiller.pop(); - - for (Entity entity : passengerEntity.getPassengers()) { + } else if (entity instanceof Player || this.entityTickList.contains(entity)) { + entity.setOldPosAndRot(); + entity.tickCount++; ++ entity.totalEntityAge++; // Paper - age-like counter for all entities + ProfilerFiller profiler = Profiler.get(); + profiler.push(entity.typeHolder()::getRegisteredName); + profiler.incrementCounter("tickPassenger"); + entity.rideTick(); ++ entity.postTick(); // CraftBukkit + profiler.pop(); + + for (Entity passenger : entity.getPassengers()) { @@ -867,6 +_,7 @@ - public void save(@Nullable ProgressListener progress, boolean flush, boolean skipSave) { + public void save(final @Nullable ProgressListener progressListener, final boolean flush, final boolean noSave) { ServerChunkCache chunkSource = this.getChunkSource(); - if (!skipSave) { -+ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(this.getWorld())); // CraftBukkit - if (progress != null) { - progress.progressStartNoAbort(Component.translatable("menu.savingLevel")); - } -@@ -883,11 +_,18 @@ - this.entityManager.autoSave(); + if (!noSave) { ++ new org.bukkit.event.world.WorldSaveEvent(this.getWorld()).callEvent(); // CraftBukkit + if (progressListener != null) { + progressListener.progressStartNoAbort(Component.translatable("menu.savingLevel")); } - } -+ -+ // CraftBukkit start - moved from MinecraftServer#saveAllChunks -+ ServerLevel serverLevel1 = this; -+ -+ this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save(this.registryAccess())); -+ this.levelStorageAccess.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData()); -+ // CraftBukkit end - } - - private void saveLevelData(boolean join) { - if (this.dragonFight != null) { -- this.server.getWorldData().setEndDragonFightData(this.dragonFight.saveData()); -+ this.serverLevelData.setEndDragonFightData(this.dragonFight.saveData()); // CraftBukkit - } +@@ -887,6 +_,7 @@ - DimensionDataStorage dataStorage = this.getChunkSource().getDataStorage(); -@@ -951,18 +_,40 @@ + private void saveLevelData(final boolean sync) { + SavedDataStorage savedDataStorage = this.getChunkSource().getDataStorage(); ++ savedDataStorage.computeIfAbsent(io.papermc.paper.world.saveddata.PaperWorldPDC.TYPE).setFrom((org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer) this.getWorld().getPersistentDataContainer()); // Paper + if (sync) { + savedDataStorage.saveAndJoin(); + } else { +@@ -949,18 +_,40 @@ @Override - public boolean addFreshEntity(Entity entity) { + public boolean addFreshEntity(final Entity entity) { - return this.addEntity(entity); + // CraftBukkit start + return this.addFreshEntity(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); + } + + @Override -+ public boolean addFreshEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.@Nullable SpawnReason reason) { ++ public boolean addFreshEntity(final Entity entity, final org.bukkit.event.entity.CreatureSpawnEvent.@Nullable SpawnReason reason) { + return this.addEntity(entity, reason); + // CraftBukkit end } - public boolean addWithUUID(Entity entity) { + public boolean addWithUUID(final Entity entity) { - return this.addEntity(entity); + // CraftBukkit start + return this.addWithUUID(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); + } + -+ public boolean addWithUUID(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.@Nullable SpawnReason reason) { ++ public boolean addWithUUID(final Entity entity, final org.bukkit.event.entity.CreatureSpawnEvent.@Nullable SpawnReason reason) { + return this.addEntity(entity, reason); + // CraftBukkit end } - public void addDuringTeleport(Entity entity) { + public void addDuringTeleport(final Entity entity) { + // CraftBukkit start + // SPIGOT-6415: Don't call spawn event for entities which travel trough worlds, + // since it is only an implementation detail, that a new entity is created when @@ -583,23 +596,23 @@ + this.addDuringTeleport(entity, null); + } + -+ public void addDuringTeleport(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.@Nullable SpawnReason reason) { ++ public void addDuringTeleport(final Entity entity, final org.bukkit.event.entity.CreatureSpawnEvent.@Nullable SpawnReason reason) { + // CraftBukkit end - if (entity instanceof ServerPlayer serverPlayer) { - this.addPlayer(serverPlayer); + if (entity instanceof ServerPlayer player) { + this.addPlayer(player); } else { - this.addEntity(entity); + this.addEntity(entity, reason); // CraftBukkit } } -@@ -985,41 +_,120 @@ +@@ -983,42 +_,121 @@ this.entityManager.addNewEntity(player); } -- private boolean addEntity(Entity entity) { +- private boolean addEntity(final Entity entity) { + // CraftBukkit start -+ private boolean addEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.@Nullable SpawnReason spawnReason) { ++ private boolean addEntity(final Entity entity, final org.bukkit.event.entity.CreatureSpawnEvent.@Nullable SpawnReason spawnReason) { + org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot + entity.generation = false; // Paper - Don't fire sync event during generation; Reset flag if it was added during a ServerLevel generation process + // Paper start - extra debug info @@ -610,14 +623,14 @@ + // Paper end - extra debug info + if (entity.spawnReason == null) entity.spawnReason = spawnReason; // Paper - Entity#getEntitySpawnReason if (entity.isRemoved()) { -- LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityType.getKey(entity.getType())); -+ // LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityType.getKey(entity.getType())); // CraftBukkit - remove warning +- LOGGER.warn("Tried to add entity {} but it was marked as removed already", entity.typeHolder().getRegisteredName()); ++ // LOGGER.warn("Tried to add entity {} but it was marked as removed already", entity.typeHolder().getRegisteredName()); // CraftBukkit - remove warning return false; } else { -+ if (entity instanceof net.minecraft.world.entity.item.ItemEntity itemEntity && itemEntity.getItem().isEmpty()) return false; // Paper - Prevent empty items from being added ++ if (entity instanceof net.minecraft.world.entity.item.ItemEntity item && item.getItem().isEmpty()) return false; // Paper - Prevent empty items from being added + // Paper start - capture all item additions to the world -+ if (captureDrops != null && entity instanceof net.minecraft.world.entity.item.ItemEntity) { -+ captureDrops.add((net.minecraft.world.entity.item.ItemEntity) entity); ++ if (captureDrops != null && entity instanceof net.minecraft.world.entity.item.ItemEntity item) { ++ captureDrops.add(item); + return true; + } + // Paper end - capture all item additions to the world @@ -631,12 +644,12 @@ } } - public boolean tryAddFreshEntityWithPassengers(Entity entity) { + public boolean tryAddFreshEntityWithPassengers(final Entity entity) { + // CraftBukkit start + return this.tryAddFreshEntityWithPassengers(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); + } + -+ public boolean tryAddFreshEntityWithPassengers(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { ++ public boolean tryAddFreshEntityWithPassengers(final Entity entity, final org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { + // CraftBukkit end if (entity.getSelfAndPassengers().map(Entity::getUUID).anyMatch(this.entityManager::isLoaded)) { return false; @@ -647,9 +660,9 @@ } } - public void unload(LevelChunk chunk) { + public void unload(final LevelChunk levelChunk) { + // Spigot start -+ for (net.minecraft.world.level.block.entity.BlockEntity blockEntity : chunk.getBlockEntities().values()) { ++ for (net.minecraft.world.level.block.entity.BlockEntity blockEntity : levelChunk.getBlockEntities().values()) { + if (blockEntity instanceof net.minecraft.world.Container) { + // Paper start - this area looks like it can load chunks, change the behavior + // chests for example can apply physics to the world @@ -661,12 +674,12 @@ + } + } + // Spigot end - chunk.clearAllBlockEntities(); - chunk.unregisterTickContainerFromLevel(this); - this.debugSynchronizers.dropChunk(chunk.getPos()); + levelChunk.clearAllBlockEntities(); + levelChunk.unregisterTickContainerFromLevel(this); + this.debugSynchronizers.dropChunk(levelChunk.getPos()); } - public void removePlayerImmediately(ServerPlayer player, Entity.RemovalReason reason) { + public void removePlayerImmediately(final ServerPlayer player, final Entity.RemovalReason reason) { - player.remove(reason); - } + player.remove(reason, null); // CraftBukkit - add Bukkit remove cause @@ -689,10 +702,10 @@ + // CraftBukkit end @Override - public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) { + public void destroyBlockProgress(final int id, final BlockPos blockPos, final int progress) { + // CraftBukkit start + Player breakerPlayer = null; -+ Entity entity = this.getEntity(breakerId); ++ Entity entity = this.getEntity(id); + if (entity instanceof Player) breakerPlayer = (Player) entity; + // CraftBukkit end + @@ -701,25 +714,26 @@ + // Hence, do not call the event. + if (entity != null) { + float progressFloat = Mth.clamp(progress, 0, 10) / 10.0f; -+ org.bukkit.craftbukkit.block.CraftBlock bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(this, pos); ++ org.bukkit.craftbukkit.block.CraftBlock bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(this, blockPos); + new io.papermc.paper.event.block.BlockBreakProgressUpdateEvent(bukkitBlock, progressFloat, entity.getBukkitEntity()) + .callEvent(); + } + // Paper end - Add BlockBreakProgressUpdateEvent - for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) { - if (serverPlayer.level() == this && serverPlayer.getId() != breakerId) { - double d = pos.getX() - serverPlayer.getX(); - double d1 = pos.getY() - serverPlayer.getY(); - double d2 = pos.getZ() - serverPlayer.getZ(); -+ // CraftBukkit start -+ if (breakerPlayer != null && !serverPlayer.getBukkitEntity().canSee(breakerPlayer.getBukkitEntity())) { -+ continue; -+ } -+ // CraftBukkit end - if (d * d + d1 * d1 + d2 * d2 < 1024.0) { - serverPlayer.connection.send(new ClientboundBlockDestructionPacket(breakerId, pos, progress)); + for (ServerPlayer player : this.server.getPlayerList().getPlayers()) { + if (player.level() == this && player.getId() != id) { + double xd = blockPos.getX() - player.getX(); + double yd = blockPos.getY() - player.getY(); + double zd = blockPos.getZ() - player.getZ(); + if (xd * xd + yd * yd + zd * zd < 1024.0) { ++ // CraftBukkit start ++ if (breakerPlayer != null && !player.getBukkitEntity().canSee(breakerPlayer.getBukkitEntity())) { ++ continue; ++ } ++ // CraftBukkit end + player.connection.send(new ClientboundBlockDestructionPacket(id, blockPos, progress)); } -@@ -1094,7 +_,7 @@ + } +@@ -1106,7 +_,7 @@ pos.getX(), pos.getY(), pos.getZ(), @@ -728,33 +742,33 @@ this.dimension(), new ClientboundLevelEventPacket(type, pos, data, false) ); -@@ -1106,6 +_,11 @@ +@@ -1118,6 +_,11 @@ @Override - public void gameEvent(Holder gameEvent, Vec3 pos, GameEvent.Context context) { + public void gameEvent(final Holder gameEvent, final Vec3 position, final GameEvent.Context context) { + // Paper start - Prevent GameEvents being fired from unloaded chunks -+ if (this.getChunkIfLoadedImmediately((Mth.floor(pos.x) >> 4), (Mth.floor(pos.z) >> 4)) == null) { ++ if (this.getChunkIfLoadedImmediately((Mth.floor(position.x) >> 4), (Mth.floor(position.z) >> 4)) == null) { + return; + } + // Paper end - Prevent GameEvents being fired from unloaded chunks - this.gameEventDispatcher.post(gameEvent, pos, context); + this.gameEventDispatcher.post(gameEvent, position, context); } -@@ -1118,17 +_,28 @@ +@@ -1130,17 +_,28 @@ this.getChunkSource().blockChanged(pos); this.pathTypesByPosCache.invalidate(pos); + if (this.paperConfig().misc.updatePathfindingOnBlockUpdate) { // Paper - option to disable pathfinding updates - VoxelShape collisionShape = oldState.getCollisionShape(this, pos); - VoxelShape collisionShape1 = newState.getCollisionShape(this, pos); - if (Shapes.joinIsNotEmpty(collisionShape, collisionShape1, BooleanOp.NOT_SAME)) { - List list = new ObjectArrayList<>(); + VoxelShape oldShape = old.getCollisionShape(this, pos); + VoxelShape newShape = current.getCollisionShape(this, pos); + if (Shapes.joinIsNotEmpty(oldShape, newShape, BooleanOp.NOT_SAME)) { + List navigationsToUpdate = new ObjectArrayList<>(); + try { // Paper - catch CME see below why - for (Mob mob : this.navigatingMobs) { - PathNavigation navigation = mob.getNavigation(); - if (navigation.shouldRecomputePath(pos)) { - list.add(navigation); + for (Mob navigatingMob : this.navigatingMobs) { + PathNavigation pathNavigation = navigatingMob.getNavigation(); + if (pathNavigation.shouldRecomputePath(pos)) { + navigationsToUpdate.add(pathNavigation); } } + // Paper start - catch CME see below why @@ -762,14 +776,14 @@ + // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register + // In this case we just run the update again across all the iterators as the chunk will then be loaded + // As this is a relative edge case it is much faster than copying navigators (on either read or write) -+ this.sendBlockUpdated(pos, oldState, newState, flags); ++ this.sendBlockUpdated(pos, old, current, updateFlags); + return; + } + // Paper end - catch CME see below why try { this.isUpdatingNavigations = true; -@@ -1140,15 +_,23 @@ +@@ -1152,15 +_,23 @@ this.isUpdatingNavigations = false; } } @@ -777,107 +791,109 @@ } @Override - public void updateNeighborsAt(BlockPos pos, Block block) { + public void updateNeighborsAt(final BlockPos pos, final Block sourceBlock) { + // CraftBukkit start + if (this.populating) { + return; + } + // CraftBukkit end + if (captureBlockStates) { return; } // Paper - Cancel all physics during placement - this.updateNeighborsAt(pos, block, ExperimentalRedstoneUtils.initialOrientation(this, null, null)); + this.updateNeighborsAt(pos, sourceBlock, ExperimentalRedstoneUtils.initialOrientation(this, null, null)); } @Override - public void updateNeighborsAt(BlockPos pos, Block block, @Nullable Orientation orientation) { + public void updateNeighborsAt(final BlockPos pos, final Block sourceBlock, final @Nullable Orientation orientation) { + if (captureBlockStates) { return; } // Paper - Cancel all physics during placement - this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, block, null, orientation); + this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, sourceBlock, null, orientation); } -@@ -1198,6 +_,44 @@ - WeightedList blockParticles, - Holder explosionSound +@@ -1214,6 +_,44 @@ + final WeightedList blockParticles, + final Holder explosionSound ) { + // CraftBukkit start -+ this.explode0(source, damageSource, damageCalculator, x, y, z, radius, fire, explosionInteraction, smallExplosionParticles, largeExplosionParticles, blockParticles, explosionSound); ++ this.explode0(source, damageSource, damageCalculator, x, y, z, r, fire, interactionType, smallExplosionParticles, largeExplosionParticles, blockParticles, explosionSound); + } + + public ServerExplosion explode0( -+ @Nullable Entity source, -+ @Nullable DamageSource damageSource, -+ @Nullable ExplosionDamageCalculator damageCalculator, -+ double x, -+ double y, -+ double z, -+ float radius, -+ boolean fire, -+ Level.ExplosionInteraction explosionInteraction, -+ ParticleOptions smallExplosionParticles, -+ ParticleOptions largeExplosionParticles, -+ WeightedList blockParticles, -+ Holder explosionSound ++ final @Nullable Entity source, ++ final @Nullable DamageSource damageSource, ++ final @Nullable ExplosionDamageCalculator damageCalculator, ++ final double x, ++ final double y, ++ final double z, ++ final float r, ++ final boolean fire, ++ final Level.ExplosionInteraction interactionType, ++ final ParticleOptions smallExplosionParticles, ++ final ParticleOptions largeExplosionParticles, ++ final WeightedList blockParticles, ++ final Holder explosionSound + ) { -+ return this.explode0(source, damageSource, damageCalculator, x, y, z, radius, fire, explosionInteraction, smallExplosionParticles, largeExplosionParticles, blockParticles, explosionSound, null); ++ return this.explode0(source, damageSource, damageCalculator, x, y, z, r, fire, interactionType, smallExplosionParticles, largeExplosionParticles, blockParticles, explosionSound, null); + } + public ServerExplosion explode0( -+ @Nullable Entity source, -+ @Nullable DamageSource damageSource, -+ @Nullable ExplosionDamageCalculator damageCalculator, -+ double x, -+ double y, -+ double z, -+ float radius, -+ boolean fire, -+ Level.ExplosionInteraction explosionInteraction, -+ ParticleOptions smallExplosionParticles, -+ ParticleOptions largeExplosionParticles, -+ WeightedList blockParticles, -+ Holder explosionSound, ++ final @Nullable Entity source, ++ final @Nullable DamageSource damageSource, ++ final @Nullable ExplosionDamageCalculator damageCalculator, ++ final double x, ++ final double y, ++ final double z, ++ final float r, ++ final boolean fire, ++ final Level.ExplosionInteraction interactionType, ++ final ParticleOptions smallExplosionParticles, ++ final ParticleOptions largeExplosionParticles, ++ final WeightedList blockParticles, ++ final Holder explosionSound, + java.util.function.@Nullable Consumer configurator + ) { + // CraftBukkit end - Explosion.BlockInteraction blockInteraction = switch (explosionInteraction) { + Explosion.BlockInteraction blockInteraction = switch (interactionType) { case NONE -> Explosion.BlockInteraction.KEEP; case BLOCK -> this.getDestroyType(GameRules.BLOCK_EXPLOSION_DROP_DECAY); -@@ -1206,10 +_,17 @@ +@@ -1222,10 +_,17 @@ : Explosion.BlockInteraction.KEEP; case TNT -> this.getDestroyType(GameRules.TNT_EXPLOSION_DROP_DECAY); case TRIGGER -> Explosion.BlockInteraction.TRIGGER_BLOCK; + case STANDARD -> Explosion.BlockInteraction.DESTROY; // CraftBukkit - handle custom explosion type }; - Vec3 vec3 = new Vec3(x, y, z); - ServerExplosion serverExplosion = new ServerExplosion(this, source, damageSource, damageCalculator, vec3, radius, fire, blockInteraction); -+ if (configurator != null) configurator.accept(serverExplosion);// Paper - Allow explosions to damage source - int i = serverExplosion.explode(); + Vec3 center = new Vec3(x, y, z); + ServerExplosion explosion = new ServerExplosion(this, source, damageSource, damageCalculator, center, r, fire, blockInteraction); ++ if (configurator != null) configurator.accept(explosion); // Paper - Allow explosions to damage source + int blockCount = explosion.explode(); + // CraftBukkit start -+ if (serverExplosion.wasCanceled) { -+ return serverExplosion; ++ if (explosion.wasCanceled) { ++ return explosion; + } + // CraftBukkit end - ParticleOptions particleOptions = serverExplosion.isSmall() ? smallExplosionParticles : largeExplosionParticles; + ParticleOptions explosionParticle = explosion.isSmall() ? smallExplosionParticles : largeExplosionParticles; - for (ServerPlayer serverPlayer : this.players) { -@@ -1218,6 +_,8 @@ - serverPlayer.connection.send(new ClientboundExplodePacket(vec3, radius, i, optional, particleOptions, explosionSound, blockParticles)); + for (ServerPlayer player : this.players) { +@@ -1234,6 +_,8 @@ + player.connection.send(new ClientboundExplodePacket(center, r, blockCount, playerKnockback, explosionParticle, explosionSound, blockParticles)); } } + -+ return serverExplosion; // CraftBukkit ++ return explosion; // CraftBukkit } - private Explosion.BlockInteraction getDestroyType(GameRule decayGameRule) { -@@ -1287,19 +_,55 @@ - public int sendParticles( - T options, double x, double y, double z, int count, double xDist, double yDist, double zDist, double speed + private Explosion.BlockInteraction getDestroyType(final GameRule gameRule) { +@@ -1311,7 +_,7 @@ + final double zDist, + final double speed ) { -- return this.sendParticles(options, false, false, x, y, z, count, xDist, yDist, zDist, speed); -+ return this.sendParticlesSource(null, options, false, false, x, y, z, count, xDist, yDist, zDist, speed); // CraftBukkit - visibility api support +- return this.sendParticles(particle, false, false, x, y, z, count, xDist, yDist, zDist, speed); ++ return this.sendParticlesSource(null, particle, false, false, x, y, z, count, xDist, yDist, zDist, speed); // CraftBukkit - visibility api support } public int sendParticles( - T options, boolean overrideLimiter, boolean alwaysShow, double x, double y, double z, int count, double xDist, double yDist, double zDist, double speed +@@ -1327,13 +_,49 @@ + final double zDist, + final double speed ) { + // Paper start - visibility api support -+ return this.sendParticlesSource(null, options, overrideLimiter, alwaysShow, x, y, z, count, xDist, yDist, zDist, speed); ++ return this.sendParticlesSource(null, particle, overrideLimiter, alwaysShow, x, y, z, count, xDist, yDist, zDist, speed); + } + public int sendParticlesSource( + @Nullable Entity sender, @@ -898,7 +914,7 @@ + public int sendParticlesSource( + List receivers, + @Nullable Entity sender, -+ T options, ++ T particle, + boolean overrideLimiter, + boolean alwaysShow, + double x, @@ -911,41 +927,41 @@ + double speed + ) { + // Paper end - visibility api support - ClientboundLevelParticlesPacket clientboundLevelParticlesPacket = new ClientboundLevelParticlesPacket( - options, overrideLimiter, alwaysShow, x, y, z, (float)xDist, (float)yDist, (float)zDist, (float)speed, count + ClientboundLevelParticlesPacket packet = new ClientboundLevelParticlesPacket( + particle, overrideLimiter, alwaysShow, x, y, z, (float)xDist, (float)yDist, (float)zDist, (float)speed, count ); - int i = 0; - -- for (int i1 = 0; i1 < this.players.size(); i1++) { -- ServerPlayer serverPlayer = this.players.get(i1); -+ for (int i1 = 0; i1 < receivers.size(); i1++) { // Paper - particle API -+ ServerPlayer serverPlayer = receivers.get(i1); // Paper - particle API -+ if (sender != null && !serverPlayer.getBukkitEntity().canSee(sender.getBukkitEntity())) continue; // CraftBukkit - if (this.sendParticles(serverPlayer, overrideLimiter, x, y, z, clientboundLevelParticlesPacket)) { - i++; + int result = 0; + +- for (int i = 0; i < this.players.size(); i++) { +- ServerPlayer player = this.players.get(i); ++ for (int i = 0; i < receivers.size(); i++) { // Paper - particle API ++ ServerPlayer player = receivers.get(i); // Paper - particle API ++ if (sender != null && !player.getBukkitEntity().canSee(sender.getBukkitEntity())) continue; // CraftBukkit + if (this.sendParticles(player, overrideLimiter, x, y, z, packet)) { + result++; } -@@ -1383,7 +_,7 @@ - } - - public @Nullable BlockPos findNearestMapStructure(TagKey structureTag, BlockPos pos, int radius, boolean skipKnownStructures) { -- if (!this.server.getWorldData().worldGenOptions().generateStructures()) { -+ if (!this.serverLevelData.worldGenOptions().generateStructures()) { // CraftBukkit +@@ -1421,7 +_,7 @@ + public @Nullable BlockPos findNearestMapStructure( + final TagKey structureTag, final BlockPos origin, final int maxSearchRadius, final boolean createReference + ) { +- if (!this.server.getWorldGenSettings().options().generateStructures()) { ++ if (!this.worldGenSettings.options().generateStructures()) { // CraftBukkit return null; } else { - Optional> optional = this.registryAccess().lookupOrThrow(Registries.STRUCTURE).get(structureTag); -@@ -1435,10 +_,36 @@ + Optional> tag = this.registryAccess().lookupOrThrow(Registries.STRUCTURE).get(structureTag); +@@ -1479,10 +_,36 @@ @Override - public @Nullable MapItemSavedData getMapData(MapId mapId) { -- return this.getServer().overworld().getDataStorage().get(MapItemSavedData.type(mapId)); + public @Nullable MapItemSavedData getMapData(final MapId id) { +- return this.getServer().getDataStorage().get(MapItemSavedData.type(id)); + // Paper start - Call missing map initialize event and set id -+ final DimensionDataStorage storage = this.getServer().overworld().getDataStorage(); ++ final SavedDataStorage storage = this.getServer().getDataStorage(); + -+ final Optional cacheEntry = storage.cache.get(MapItemSavedData.type(mapId)); ++ final Optional cacheEntry = storage.cache.get(MapItemSavedData.type(id)); + if (cacheEntry == null) { // Cache did not contain, try to load and may init -+ final MapItemSavedData mapData = storage.get(MapItemSavedData.type(mapId)); // get populates the cache ++ final MapItemSavedData mapData = storage.get(MapItemSavedData.type(id)); // get populates the cache + if (mapData != null) { // map was read, init it and return -+ mapData.id = mapId; ++ mapData.id = id; + new org.bukkit.event.server.MapInitializeEvent(mapData.mapView).callEvent(); + return mapData; + } @@ -954,7 +970,7 @@ + } + // Cache entry exists, update it with the id ref and return. + if (cacheEntry.orElse(null) instanceof final MapItemSavedData mapItemSavedData) { -+ mapItemSavedData.id = mapId; ++ mapItemSavedData.id = id; + return mapItemSavedData; + } + @@ -962,64 +978,75 @@ + // Paper end - Call missing map initialize event and set id } - public void setMapData(MapId mapId, MapItemSavedData data) { + public void setMapData(final MapId id, final MapItemSavedData data) { + // CraftBukkit start -+ data.id = mapId; ++ data.id = id; + org.bukkit.event.server.MapInitializeEvent event = new org.bukkit.event.server.MapInitializeEvent(data.mapView); + event.callEvent(); + // CraftBukkit end - this.getServer().overworld().getDataStorage().set(MapItemSavedData.type(mapId), data); + this.getServer().getDataStorage().set(MapItemSavedData.type(id), data); } -@@ -1448,7 +_,19 @@ +@@ -1492,7 +_,21 @@ @Override - public void setRespawnData(LevelData.RespawnData respawnData) { + public void setRespawnData(final LevelData.RespawnData respawnData) { - this.getServer().setRespawnData(respawnData); + // Paper start -+ if (!this.serverLevelData.getRespawnData().positionEquals(respawnData)) { ++ final LevelData.RespawnData previousRespawnData = this.serverLevelData.getRespawnData(); ++ this.serverLevelData.setSpawn(respawnData); ++ if (!previousRespawnData.positionEquals(respawnData)) { + org.bukkit.Location previousLocation = this.getWorld().getSpawnLocation(); -+ this.serverLevelData.setSpawn(respawnData); + this.server.getPlayerList().broadcastAll(new net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket(respawnData), this.dimension()); + this.server.updateEffectiveRespawnData(); + new org.bukkit.event.world.SpawnChangeEvent(this.getWorld(), previousLocation).callEvent(); + } -+ if (this.server.overworld().serverLevelData.respawnDimension != this.dimension()) { -+ this.server.overworld().serverLevelData.respawnDimension = this.dimension(); ++ final net.minecraft.world.level.storage.PrimaryLevelData overworldData = (net.minecraft.world.level.storage.PrimaryLevelData) this.server.getWorldData(); ++ if (overworldData.respawnDimension != this.dimension()) { ++ overworldData.respawnDimension = this.dimension(); + this.server.updateEffectiveRespawnData(); + } + // Paper end } @Override -@@ -1485,6 +_,11 @@ - this.debugSynchronizers.dropPoi(blockPos); +@@ -1529,6 +_,11 @@ + this.debugSynchronizers.dropPoi(immutable); })); - optional1.ifPresent(holder -> this.getServer().execute(() -> { + newType.ifPresent(poiType -> this.getServer().execute(() -> { + // Paper start - Remove stale POIs -+ if (optional.isEmpty() && this.getPoiManager().exists(blockPos, ignored -> true)) { -+ this.getPoiManager().remove(blockPos); ++ if (oldType.isEmpty() && this.getPoiManager().exists(pos, _ -> true)) { ++ this.getPoiManager().remove(pos); + } + // Paper end - Remove stale POIs - PoiRecord poiRecord = this.getPoiManager().add(blockPos, (Holder)holder); - if (poiRecord != null) { - this.debugSynchronizers.registerPoi(poiRecord); -@@ -1638,12 +_,12 @@ + PoiRecord record = this.getPoiManager().add(immutable, (Holder)poiType); + if (record != null) { + this.debugSynchronizers.registerPoi(record); +@@ -1677,12 +_,12 @@ } public boolean isFlat() { - return this.server.getWorldData().isFlatWorld(); -+ return this.serverLevelData.isFlatWorld(); // CraftBukkit ++ return this.worldGenSettings.dimensions().get(this.getTypeKey()).map(levelStem -> levelStem.generator() instanceof net.minecraft.world.level.levelgen.FlatLevelSource).orElse(false); // Paper } @Override public long getSeed() { -- return this.server.getWorldData().worldGenOptions().seed(); -+ return this.serverLevelData.worldGenOptions().seed(); // CraftBukkit +- return this.server.getWorldGenSettings().options().seed(); ++ return this.worldGenSettings.options().seed(); // CraftBukkit + } + + public @Nullable EnderDragonFight getDragonFight() { +@@ -1690,7 +_,7 @@ + } + + public WeatherData getWeatherData() { +- return this.server.getWeatherData(); ++ return this.weatherData; // Paper - per-level WeatherData } - public @Nullable EndDragonFight getDragonFight() { -@@ -1693,6 +_,7 @@ + @Override +@@ -1737,6 +_,7 @@ @Override public LevelEntityGetter getEntities() { @@ -1027,10 +1054,15 @@ return this.entityManager.getEntityGetter(); } -@@ -1808,6 +_,28 @@ - return this.serverLevelData.getGameRules(); +@@ -1841,8 +_,30 @@ } + public GameRules getGameRules() { +- return this.server.getGameRules(); +- } ++ return this.gameRules; // Paper - per-level GameRules ++ } ++ + // Paper start - respect global sound events gamerule + public List getPlayersForGlobalSoundGamerule() { + return this.getGameRules().get(GameRules.GLOBAL_SOUND_EVENTS) ? ((ServerLevel) this).getServer().getPlayerList().players : ((ServerLevel) this).players(); @@ -1052,28 +1084,27 @@ + } + } + // Paper end - notify observers even if grow failed -+ + @Override - public CrashReportCategory fillReportDetails(CrashReport report) { - CrashReportCategory crashReportCategory = super.fillReportDetails(report); -@@ -1852,6 +_,7 @@ - if (entity instanceof WaypointTransmitter waypointTransmitter && waypointTransmitter.isTransmittingWaypoint()) { - ServerLevel.this.getWaypointManager().trackWaypoint(waypointTransmitter); + public CrashReportCategory fillReportDetails(final CrashReport report) { +@@ -1905,6 +_,7 @@ + if (entity instanceof WaypointTransmitter waypoint && waypoint.isTransmittingWaypoint()) { + ServerLevel.this.getWaypointManager().trackWaypoint(waypoint); } + entity.setOldPosAndRot(); // Paper - update old pos / rot for new entities as it will default to Vec3.ZERO } @Override -@@ -1865,17 +_,24 @@ +@@ -1918,17 +_,24 @@ @Override - public void onTickingStart(Entity entity) { + public void onTickingStart(final Entity entity) { + if (entity instanceof net.minecraft.world.entity.Marker && !paperConfig().entities.markers.tick) return; // Paper - Configurable marker ticking ServerLevel.this.entityTickList.add(entity); } @Override - public void onTickingEnd(Entity entity) { + public void onTickingEnd(final Entity entity) { ServerLevel.this.entityTickList.remove(entity); + // Paper start - Reset pearls when they stop being ticked + if (ServerLevel.this.paperConfig().fixes.disableUnloadedChunkEnderpearlExploit && ServerLevel.this.paperConfig().misc.legacyEnderPearlBehavior && entity instanceof net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownEnderpearl pearl) { @@ -1083,23 +1114,23 @@ } @Override - public void onTrackingStart(Entity entity) { + public void onTrackingStart(final Entity entity) { - ServerLevel.this.getChunkSource().addEntity(entity); + org.spigotmc.AsyncCatcher.catchOp("entity register"); // Spigot + // ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true - if (entity instanceof ServerPlayer serverPlayer) { - ServerLevel.this.players.add(serverPlayer); - if (serverPlayer.isReceivingWaypoints()) { -@@ -1890,7 +_,7 @@ + if (entity instanceof ServerPlayer player) { + ServerLevel.this.players.add(player); + if (player.isReceivingWaypoints()) { +@@ -1943,7 +_,7 @@ } if (entity instanceof Mob mob) { - if (ServerLevel.this.isUpdatingNavigations) { + if (false && ServerLevel.this.isUpdatingNavigations) { // Paper - Remove unnecessary onTrackingStart during navigation warning - String string = "onTrackingStart called during navigation iteration"; + String message = "onTrackingStart called during navigation iteration"; Util.logAndPauseIfInIde( "onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration") -@@ -1907,10 +_,52 @@ +@@ -1960,10 +_,52 @@ } entity.updateDynamicGameEventListener(DynamicGameEventListener::add); @@ -1119,7 +1150,7 @@ } @Override - public void onTrackingEnd(Entity entity) { + public void onTrackingEnd(final Entity entity) { + org.spigotmc.AsyncCatcher.catchOp("entity unregister"); // Spigot + // Spigot start // TODO I don't think this is needed anymore + if (entity instanceof Player player) { @@ -1150,18 +1181,18 @@ + } + // Spigot end ServerLevel.this.getChunkSource().removeEntity(entity); - if (entity instanceof ServerPlayer serverPlayer) { - ServerLevel.this.players.remove(serverPlayer); -@@ -1919,7 +_,7 @@ + if (entity instanceof ServerPlayer player) { + ServerLevel.this.players.remove(player); +@@ -1972,7 +_,7 @@ } if (entity instanceof Mob mob) { - if (ServerLevel.this.isUpdatingNavigations) { + if (false && ServerLevel.this.isUpdatingNavigations) { // Paper - Remove unnecessary onTrackingStart during navigation warning - String string = "onTrackingStart called during navigation iteration"; + String message = "onTrackingStart called during navigation iteration"; Util.logAndPauseIfInIde( "onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration") -@@ -1937,6 +_,15 @@ +@@ -1990,6 +_,15 @@ entity.updateDynamicGameEventListener(DynamicGameEventListener::remove); ServerLevel.this.debugSynchronizers.dropEntity(entity); @@ -1177,7 +1208,7 @@ } @Override -@@ -1944,4 +_,24 @@ +@@ -1997,4 +_,24 @@ entity.updateDynamicGameEventListener(DynamicGameEventListener::move); } } diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch index 9fdbd77f61e2..353b83ed6014 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -251,7 +_,8 @@ +@@ -253,7 +_,8 @@ private int levitationStartTime; private boolean disconnected; private int requestedViewDistance = 2; @@ -10,7 +10,7 @@ private @Nullable Vec3 startingToFallPosition; private @Nullable Vec3 enteredNetherPosition; private @Nullable Vec3 enteredLavaOnVehiclePosition; -@@ -295,6 +_,13 @@ +@@ -308,6 +_,13 @@ } } @@ -22,9 +22,9 @@ + // Paper end - Sync offhand slot in menus + @Override - public void sendSlotChange(AbstractContainerMenu container, int slot, ItemStack stack) { - ServerPlayer.this.connection.send(new ClientboundContainerSetSlotPacket(container.containerId, container.incrementStateId(), slot, stack)); -@@ -330,6 +_,32 @@ + public void sendSlotChange(final AbstractContainerMenu container, final int slotIndex, final ItemStack itemStack) { + ServerPlayer.this.connection.send(new ClientboundContainerSetSlotPacket(container.containerId, container.incrementStateId(), slotIndex, itemStack)); +@@ -347,6 +_,32 @@ } } @@ -55,10 +55,10 @@ + // Paper end - Add PlayerInventorySlotChangeEvent + @Override - public void dataChanged(AbstractContainerMenu containerMenu, int dataSlotIndex, int value) { + public void dataChanged(final AbstractContainerMenu container, final int id, final int value) { } -@@ -356,10 +_,43 @@ - public void sendSystemMessage(Component message) { +@@ -377,10 +_,43 @@ + public void sendSystemMessage(final Component message) { ServerPlayer.this.sendSystemMessage(message); } + @@ -99,10 +99,10 @@ + public com.destroystokyo.paper.event.entity.@Nullable PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent + public org.bukkit.event.player.PlayerQuitEvent.@Nullable QuitReason quitReason = null; // Paper - Add API for quit reason; there are a lot of changes to do if we change all methods leading to the event - public ServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile, ClientInformation clientInformation) { + public ServerPlayer(final MinecraftServer server, final ServerLevel level, final GameProfile gameProfile, final ClientInformation clientInformation) { super(level, gameProfile); -@@ -370,8 +_,14 @@ - this.recipeBook = new ServerRecipeBook((recipe, output) -> server.getRecipeManager().listDisplaysForRecipe(recipe, output)); +@@ -391,8 +_,14 @@ + this.recipeBook = new ServerRecipeBook((id, output) -> server.getRecipeManager().listDisplaysForRecipe(id, output)); this.stats = server.getPlayerList().getPlayerStats(this); this.advancements = server.getPlayerList().getPlayerAdvancements(this); - this.updateOptions(clientInformation); @@ -117,15 +117,15 @@ } @Override -@@ -389,6 +_,7 @@ +@@ -410,6 +_,7 @@ this.seenCredits = input.getBooleanOr("seenCredits", false); input.read("recipeBook", ServerRecipeBook.Packed.CODEC) - .ifPresent(packed -> this.recipeBook.loadUntrusted(packed, key -> this.server.getRecipeManager().byKey(key).isPresent())); + .ifPresent(p -> this.recipeBook.loadUntrusted(p, id -> this.server.getRecipeManager().byKey(id).isPresent())); + this.getBukkitEntity().readExtraData(input); // CraftBukkit if (this.isSleeping()) { this.stopSleeping(); } -@@ -396,6 +_,19 @@ +@@ -417,6 +_,19 @@ this.respawnConfig = input.read("respawn", ServerPlayer.RespawnConfig.CODEC).orElse(null); this.spawnExtraParticlesOnFall = input.getBooleanOr("spawn_extra_particles_on_fall", false); this.raidOmenPosition = input.read("raid_omen_position", BlockPos.CODEC).orElse(null); @@ -145,14 +145,14 @@ this.gameMode .setGameModeForPlayer(this.calculateGameModeForNewPlayer(readPlayerMode(input, "playerGameType")), readPlayerMode(input, "previousPlayerGameType")); this.setShoulderEntityLeft(input.read("ShoulderEntityLeft", CompoundTag.CODEC).orElseGet(CompoundTag::new)); -@@ -423,12 +_,24 @@ +@@ -444,12 +_,24 @@ if (!this.getShoulderEntityRight().isEmpty()) { output.store("ShoulderEntityRight", CompoundTag.CODEC, this.getShoulderEntityRight()); } + this.getBukkitEntity().setExtraData(output); // CraftBukkit } - private void saveParentVehicle(ValueOutput output) { + private void saveParentVehicle(final ValueOutput playerOutput) { Entity rootVehicle = this.getRootVehicle(); Entity vehicle = this.getVehicle(); - if (vehicle != null && rootVehicle != this && rootVehicle.hasExactlyOnePlayerPassenger()) { @@ -168,40 +168,40 @@ + } + if (persistVehicle && vehicle != null && rootVehicle != this && rootVehicle.hasExactlyOnePlayerPassenger() && !rootVehicle.isRemoved()) { // Paper - Ensure valid vehicle status + // CraftBukkit end - ValueOutput valueOutput = output.child("RootVehicle"); - valueOutput.store("Attach", UUIDUtil.CODEC, vehicle.getUUID()); - rootVehicle.save(valueOutput.child("Entity")); -@@ -440,7 +_,7 @@ - if (!optional.isEmpty()) { + ValueOutput vehicleWrapper = playerOutput.child("RootVehicle"); + vehicleWrapper.store("Attach", UUIDUtil.CODEC, vehicle.getUUID()); + rootVehicle.save(vehicleWrapper.child("Entity")); +@@ -461,7 +_,7 @@ + if (!rootTag.isEmpty()) { ServerLevel serverLevel = this.level(); - Entity entity = EntityType.loadEntityRecursive( -- optional.get().childOrEmpty("Entity"), serverLevel, EntitySpawnReason.LOAD, entity2 -> !serverLevel.addWithUUID(entity2) ? null : entity2 -+ optional.get().childOrEmpty("Entity"), serverLevel, EntitySpawnReason.LOAD, entity2 -> !serverLevel.addWithUUID(entity2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.MOUNT) ? null : entity2 // Paper - Entity#getEntitySpawnReason + Entity vehicle = EntityType.loadEntityRecursive( +- rootTag.get().childOrEmpty("Entity"), serverLevel, EntitySpawnReason.LOAD, e -> !serverLevel.addWithUUID(e) ? null : e ++ rootTag.get().childOrEmpty("Entity"), serverLevel, EntitySpawnReason.LOAD, e -> !serverLevel.addWithUUID(e, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.MOUNT) ? null : e // Paper - Entity#getEntitySpawnReason ); - if (entity != null) { - UUID uuid = optional.get().read("Attach", UUIDUtil.CODEC).orElse(null); -@@ -457,10 +_,10 @@ + if (vehicle != null) { + UUID attachTo = rootTag.get().read("Attach", UUIDUtil.CODEC).orElse(null); +@@ -478,10 +_,10 @@ if (!this.isPassenger()) { LOGGER.warn("Couldn't reattach entity to player"); -- entity.discard(); -+ entity.discard(null); // CraftBukkit - add Bukkit remove cause +- vehicle.discard(); ++ vehicle.discard(null); // CraftBukkit - add Bukkit remove cause - for (Entity entity1x : entity.getIndirectPassengers()) { -- entity1x.discard(); -+ entity1x.discard(null); // CraftBukkit - add Bukkit remove cause + for (Entity entityx : vehicle.getIndirectPassengers()) { +- entityx.discard(); ++ entityx.discard(null); // CraftBukkit - add Bukkit remove cause } } } -@@ -472,6 +_,7 @@ - ValueOutput.ValueOutputList valueOutputList = output.childrenList("ender_pearls"); +@@ -493,6 +_,7 @@ + ValueOutput.ValueOutputList pearlsOutput = playerOutput.childrenList("ender_pearls"); - for (ThrownEnderpearl thrownEnderpearl : this.enderPearls) { -+ if (thrownEnderpearl.level().paperConfig().misc.legacyEnderPearlBehavior) continue; // Paper - Allow using old ender pearl behavior - if (thrownEnderpearl.isRemoved()) { + for (ThrownEnderpearl enderPearl : this.enderPearls) { ++ if (enderPearl.level().paperConfig().misc.legacyEnderPearlBehavior) continue; // Paper - Allow using old ender pearl behavior + if (enderPearl.isRemoved()) { LOGGER.warn("Trying to save removed ender pearl, skipping"); } else { -@@ -504,6 +_,16 @@ +@@ -527,6 +_,16 @@ } } @@ -215,10 +215,10 @@ + } + // CraftBukkit end + - public void setExperiencePoints(int experiencePoints) { - float f = this.getXpNeededForNextLevel(); - float f1 = (f - 1.0F) / f; -@@ -568,6 +_,11 @@ + public void setExperiencePoints(final int amount) { + float limit = this.getXpNeededForNextLevel(); + float max = (limit - 1.0F) / limit; +@@ -591,6 +_,11 @@ @Override public void tick() { @@ -230,7 +230,7 @@ this.connection.tickClientLoadTimeout(); this.gameMode.tick(); this.wardenSpawnTracker.tick(); -@@ -575,9 +_,18 @@ +@@ -598,9 +_,18 @@ this.invulnerableTime--; } @@ -252,7 +252,7 @@ this.containerMenu = this.inventoryMenu; } -@@ -636,10 +_,10 @@ +@@ -659,10 +_,10 @@ public void doTick() { try { @@ -265,7 +265,7 @@ this.containerMenu = this.inventoryMenu; } -@@ -669,7 +_,7 @@ +@@ -692,7 +_,7 @@ if (this.getHealth() != this.lastSentHealth || this.lastSentFood != this.foodData.getFoodLevel() || this.foodData.getSaturationLevel() == 0.0F != this.lastFoodSaturationZero) { @@ -274,7 +274,7 @@ this.lastSentHealth = this.getHealth(); this.lastSentFood = this.foodData.getFoodLevel(); this.lastFoodSaturationZero = this.foodData.getSaturationLevel() == 0.0F; -@@ -700,6 +_,12 @@ +@@ -723,6 +_,12 @@ this.updateScoreForCriteria(ObjectiveCriteria.EXPERIENCE, Mth.ceil((float)this.lastRecordedExperience)); } @@ -287,7 +287,7 @@ if (this.experienceLevel != this.lastRecordedLevel) { this.lastRecordedLevel = this.experienceLevel; this.updateScoreForCriteria(ObjectiveCriteria.LEVEL, Mth.ceil((float)this.lastRecordedLevel)); -@@ -713,6 +_,21 @@ +@@ -736,6 +_,21 @@ if (this.tickCount % 20 == 0) { CriteriaTriggers.LOCATION.trigger(this); } @@ -307,9 +307,9 @@ + } + // CraftBukkit end } catch (Throwable var4) { - CrashReport crashReport = CrashReport.forThrowable(var4, "Ticking player"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Player being ticked"); -@@ -737,7 +_,7 @@ + CrashReport report = CrashReport.forThrowable(var4, "Ticking player"); + CrashReportCategory category = report.addCategory("Player being ticked"); +@@ -760,7 +_,7 @@ if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.level().getGameRules().get(GameRules.NATURAL_HEALTH_REGENERATION)) { if (this.tickCount % 20 == 0) { if (this.getHealth() < this.getMaxHealth()) { @@ -317,8 +317,8 @@ + this.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit - added regain reason of "REGEN" for filtering purposes. } - float saturationLevel = this.foodData.getSaturationLevel(); -@@ -757,6 +_,7 @@ + float saturation = this.foodData.getSaturationLevel(); +@@ -780,6 +_,7 @@ this.playShoulderEntityAmbientSound(this.getShoulderEntityLeft()); this.playShoulderEntityAmbientSound(this.getShoulderEntityRight()); if (this.fallDistance > 0.5 || this.isInWater() || this.getAbilities().flying || this.isSleeping() || this.isInPowderSnow) { @@ -326,7 +326,7 @@ this.removeEntitiesOnShoulder(); } } -@@ -801,35 +_,64 @@ +@@ -824,33 +_,62 @@ @Override public void removeEntitiesOnShoulder() { if (this.timeEntitySatOnShoulder + 20L < this.level().getGameTime()) { @@ -361,29 +361,27 @@ } + // Paper end - release entity api -- private void respawnEntityOnShoulder(CompoundTag tag) { -+ private boolean respawnEntityOnShoulder(CompoundTag tag) { // CraftBukkit - void -> boolean +- private void respawnEntityOnShoulder(final CompoundTag tag) { ++ private boolean respawnEntityOnShoulder(final CompoundTag tag) { // CraftBukkit - void -> boolean + // Paper start - release entity api - return entity - overload + return this.respawnEntityOnShoulder0(tag) != null; + } + @Nullable -+ private Entity respawnEntityOnShoulder0(CompoundTag tag) { // CraftBukkit void->boolean ++ private Entity respawnEntityOnShoulder0(final CompoundTag tag) { // CraftBukkit void->boolean + // Paper end - release entity api - return entity - overload - ServerLevel scopedCollector = this.level(); - if (scopedCollector instanceof ServerLevel) { - ServerLevel serverLevel = scopedCollector; + ServerLevel reporter = this.level(); + if (reporter instanceof ServerLevel) { + ServerLevel serverLevel = reporter; if (!tag.isEmpty()) { - try (ProblemReporter.ScopedCollector scopedCollectorx = new ProblemReporter.ScopedCollector(this.problemPath(), LOGGER)) { + try (ProblemReporter.ScopedCollector reporterx = new ProblemReporter.ScopedCollector(this.problemPath(), LOGGER)) { - EntityType.create( + return EntityType.create( // Paper - release entity api - TagValueInput.create(scopedCollectorx.forChild(() -> ".shoulder"), serverLevel.registryAccess(), tag), - serverLevel, - EntitySpawnReason.LOAD + TagValueInput.create(reporterx.forChild(() -> ".shoulder"), serverLevel.registryAccess(), tag), serverLevel, EntitySpawnReason.LOAD ) - .ifPresent(entity -> { + .map(entity -> { // Paper - release entity api - if (entity instanceof TamableAnimal tamableAnimal) { - tamableAnimal.setOwner(this); + if (entity instanceof TamableAnimal tamed) { + tamed.setOwner(this); } entity.setPos(this.getX(), this.getY() + 0.7F, this.getZ()); @@ -398,20 +396,20 @@ } @Override -@@ -866,15 +_,36 @@ +@@ -887,15 +_,36 @@ } - private void updateScoreForCriteria(ObjectiveCriteria criteria, int points) { -- this.level().getScoreboard().forAllObjectives(criteria, this, score -> score.set(points)); + private void updateScoreForCriteria(final ObjectiveCriteria criteria, final int value) { +- this.level().getScoreboard().forAllObjectives(criteria, this, score -> score.set(value)); - } - - @Override -- public void die(DamageSource damageSource) { +- public void die(final DamageSource source) { - this.gameEvent(GameEvent.ENTITY_DIE); -- boolean flag = this.level().getGameRules().get(GameRules.SHOW_DEATH_MESSAGES); -- if (flag) { +- boolean showDeathMessage = this.level().getGameRules().get(GameRules.SHOW_DEATH_MESSAGES); +- if (showDeathMessage) { - Component deathMessage = this.getCombatTracker().getDeathMessage(); -+ this.level().getCraftServer().getScoreboardManager().forAllObjectives(criteria, this, scoreAccess -> scoreAccess.set(points)); // CraftBukkit - Use our scores instead ++ this.level().getCraftServer().getScoreboardManager().forAllObjectives(criteria, this, score -> score.set(value)); // CraftBukkit - Use our scores instead + } + + // Paper start - PlayerDeathEvent#getItemsToKeep @@ -444,7 +442,7 @@ this.connection .send( new ClientboundPlayerCombatKillPacket(this.getId(), deathMessage), -@@ -891,6 +_,65 @@ +@@ -912,6 +_,64 @@ } ) ); @@ -454,9 +452,9 @@ + } + // Paper end - Expand PlayerDeathEvent API + @Override -+ public void die(DamageSource damageSource) { ++ public void die(final DamageSource source) { + // this.gameEvent(GameEvent.ENTITY_DIE); // Paper - move below event cancellation check -+ boolean flag = this.level().getGameRules().get(GameRules.SHOW_DEATH_MESSAGES); final boolean showDeathMessage = flag; // Paper - OBFHELPER ++ boolean showDeathMessage = this.level().getGameRules().get(GameRules.SHOW_DEATH_MESSAGES); + // CraftBukkit start - fire PlayerDeathEvent + if (this.isRemoved()) { + return; @@ -472,7 +470,7 @@ + } + if (!this.isSpectator() && this.shouldDropLoot(this.level())) { // Paper - fix player loottables running when mob loot gamerule is false + // SPIGOT-5071: manually add player loot tables (SPIGOT-5195 - ignores keepInventory rule) -+ this.dropFromLootTable(this.level(), damageSource, this.lastHurtByPlayerMemoryTime > 0); ++ this.dropFromLootTable(this.level(), source, this.lastHurtByPlayerMemoryTime > 0); + // Paper - Restore vanilla drops behaviour; custom death loot is a noop on server player, remove. + loot.addAll(this.drops); + this.drops.clear(); // SPIGOT-5188: make sure to clear @@ -480,9 +478,8 @@ + + Component defaultMessage = this.getCombatTracker().getDeathMessage(); + -+ String deathmessage = defaultMessage.getString(); + this.keepLevel = keepInventory; // SPIGOT-2222: pre-set keepLevel -+ org.bukkit.event.entity.PlayerDeathEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerDeathEvent(this, damageSource, loot, io.papermc.paper.adventure.PaperAdventure.asAdventure(defaultMessage), showDeathMessage, keepInventory); // Paper - Adventure; Expand PlayerDeathEvent API ++ org.bukkit.event.entity.PlayerDeathEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerDeathEvent(this, source, loot, io.papermc.paper.adventure.PaperAdventure.asAdventure(defaultMessage), showDeathMessage, keepInventory); // Paper - Adventure; Expand PlayerDeathEvent API + // Paper start - cancellable death event + if (event.isCancelled()) { + // make compatible with plugins that might have already set the health in an event listener @@ -510,7 +507,7 @@ Team team = this.getTeam(); if (team == null || team.getDeathMessageVisibility() == Team.Visibility.ALWAYS) { this.server.getPlayerList().broadcastSystemMessage(deathMessage, false); -@@ -900,7 +_,7 @@ +@@ -921,7 +_,7 @@ this.server.getPlayerList().broadcastSystemToAllExceptTeam(this, deathMessage); } } else { @@ -519,14 +516,14 @@ } this.removeEntitiesOnShoulder(); -@@ -908,11 +_,35 @@ +@@ -929,11 +_,35 @@ this.tellNeutralMobsThatIDied(); } - if (!this.isSpectator()) { -- this.dropAllDeathLoot(this.level(), damageSource); +- this.dropAllDeathLoot(this.level(), source); + // SPIGOT-5478 must be called manually now -+ if (event.shouldDropExperience()) this.dropExperience(this.level(), damageSource.getEntity()); // Paper - tie to event ++ if (event.shouldDropExperience()) this.dropExperience(this.level(), source.getEntity()); // Paper - tie to event + // we clean the player's inventory after the EntityDeathEvent is called so plugins can get the exact state of the inventory. + if (!event.getKeepInventory()) { + // Paper start - PlayerDeathEvent#getItemsToKeep @@ -555,50 +552,50 @@ + // CraftBukkit end + + this.level().getCraftServer().getScoreboardManager().forAllObjectives(ObjectiveCriteria.DEATH_COUNT, this, ScoreAccess::increment); // CraftBukkit - Get our scores instead - LivingEntity killCredit = this.getKillCredit(); - if (killCredit != null) { - this.awardStat(Stats.ENTITY_KILLED_BY.get(killCredit.getType())); -@@ -946,10 +_,10 @@ - if (entity != this) { - super.awardKillScore(entity, damageSource); + LivingEntity killer = this.getKillCredit(); + if (killer != null) { + this.awardStat(Stats.ENTITY_KILLED_BY.get(killer.getType())); +@@ -967,10 +_,10 @@ + if (victim != this) { + super.awardKillScore(victim, killingBlow); Scoreboard scoreboard = this.level().getScoreboard(); - scoreboard.forAllObjectives(ObjectiveCriteria.KILL_COUNT_ALL, this, ScoreAccess::increment); + this.level().getCraftServer().getScoreboardManager().forAllObjectives(ObjectiveCriteria.KILL_COUNT_ALL, this, ScoreAccess::increment); // CraftBukkit - Get our scores instead - if (entity instanceof Player) { + if (victim instanceof Player) { this.awardStat(Stats.PLAYER_KILLS); - scoreboard.forAllObjectives(ObjectiveCriteria.KILL_COUNT_PLAYERS, this, ScoreAccess::increment); + this.level().getCraftServer().getScoreboardManager().forAllObjectives(ObjectiveCriteria.KILL_COUNT_PLAYERS, this, ScoreAccess::increment); // CraftBukkit - Get our scores instead } else { this.awardStat(Stats.MOB_KILLS); } -@@ -961,12 +_,11 @@ - } - - private void handleTeamKill(ScoreHolder scoreHolder, ScoreHolder teamMember, ObjectiveCriteria[] criteria) { -- Scoreboard scoreboard = this.level().getScoreboard(); -- PlayerTeam playersTeam = scoreboard.getPlayersTeam(teamMember.getScoreboardName()); -+ PlayerTeam playersTeam = this.getBukkitEntity().getScoreboard().getHandle().getPlayersTeam(teamMember.getScoreboardName()); // CraftBukkit - if (playersTeam != null) { - int id = playersTeam.getColor().getId(); - if (id >= 0 && id < criteria.length) { -- scoreboard.forAllObjectives(criteria[id], scoreHolder, ScoreAccess::increment); -+ this.level().getCraftServer().getScoreboardManager().forAllObjectives(criteria[id], scoreHolder, ScoreAccess::increment); // CraftBukkit - Get our scores instead +@@ -983,11 +_,11 @@ + + private void handleTeamKill(final ScoreHolder source, final ScoreHolder target, final ObjectiveCriteria[] criteriaByTeam) { + Scoreboard scoreboard = this.level().getScoreboard(); +- PlayerTeam ownTeam = scoreboard.getPlayersTeam(target.getScoreboardName()); ++ PlayerTeam ownTeam = this.getBukkitEntity().getScoreboard().getHandle().getPlayersTeam(target.getScoreboardName()); // CraftBukkit + if (ownTeam != null) { + int color = ownTeam.getColor().getId(); + if (color >= 0 && color < criteriaByTeam.length) { +- scoreboard.forAllObjectives(criteriaByTeam[color], source, ScoreAccess::increment); ++ this.level().getCraftServer().getScoreboardManager().forAllObjectives(criteriaByTeam[color], source, ScoreAccess::increment); // CraftBukkit - Get our scores instead } } } -@@ -977,9 +_,20 @@ +@@ -998,9 +_,20 @@ return false; } else { - Entity entity = damageSource.getEntity(); -- return !(entity instanceof Player player && !this.canHarmPlayer(player)) + Entity entity = source.getEntity(); +- return !(entity instanceof Player playerx && !this.canHarmPlayer(playerx)) +- && !(entity instanceof AbstractArrow arrow && arrow.getOwner() instanceof Player player && !this.canHarmPlayer(player)) +- && super.hurtServer(level, source, damage); + if (!( // Paper - split the if statement. If below statement is false, hurtServer would not have been evaluated. Return false. -+ !(entity instanceof Player player && !this.canHarmPlayer(player)) - && !(entity instanceof AbstractArrow abstractArrow && abstractArrow.getOwner() instanceof Player player1 && !this.canHarmPlayer(player1)) -- && super.hurtServer(level, damageSource, amount); ++ !(entity instanceof Player playerx && !this.canHarmPlayer(playerx)) ++ && !(entity instanceof AbstractArrow arrow && arrow.getOwner() instanceof Player player && !this.canHarmPlayer(player)) + )) return false; // Paper - split the if statement. If below statement is false, hurtServer would not have been evaluated. Return false. + // Paper start - cancellable death events + this.queueHealthUpdatePacket = true; -+ boolean damaged = super.hurtServer(level, damageSource, amount); ++ boolean damaged = super.hurtServer(level, source, damage); + this.queueHealthUpdatePacket = false; + if (this.queuedHealthUpdatePacket != null) { + this.connection.send(this.queuedHealthUpdatePacket); @@ -609,39 +606,47 @@ } } -@@ -992,22 +_,92 @@ +@@ -1013,24 +_,98 @@ return this.level().isPvpAllowed(); } -- public TeleportTransition findRespawnPositionAndUseSpawnBlock(boolean useCharge, TeleportTransition.PostTeleportTransition postTeleportTransition) { +- public TeleportTransition findRespawnPositionAndUseSpawnBlock( +- final boolean consumeSpawnBlock, final TeleportTransition.PostTeleportTransition postTeleportTransition +- ) { + // Paper start + public record RespawnResult(TeleportTransition transition, boolean isBedSpawn, boolean isAnchorSpawn) { + } + -+ public @Nullable TeleportTransition findRespawnPositionAndUseSpawnBlock(boolean useCharge, TeleportTransition.PostTeleportTransition postTeleportTransition, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason respawnReason) { -+ RespawnResult result = this.findRespawnPositionAndUseSpawnBlock0(useCharge, postTeleportTransition, respawnReason); ++ public @Nullable TeleportTransition findRespawnPositionAndUseSpawnBlock( ++ final boolean consumeSpawnBlock, final TeleportTransition.PostTeleportTransition postTeleportTransition, ++ final org.bukkit.event.player.PlayerRespawnEvent.RespawnReason respawnReason ++ ) { ++ RespawnResult result = this.findRespawnPositionAndUseSpawnBlock0(consumeSpawnBlock, postTeleportTransition, respawnReason); + return result == null ? null : result.transition(); + } + -+ public @Nullable RespawnResult findRespawnPositionAndUseSpawnBlock0(boolean useCharge, TeleportTransition.PostTeleportTransition postTeleportTransition, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason respawnReason) { ++ public @Nullable RespawnResult findRespawnPositionAndUseSpawnBlock0( ++ final boolean consumeSpawnBlock, final TeleportTransition.PostTeleportTransition postTeleportTransition, ++ final org.bukkit.event.player.PlayerRespawnEvent.RespawnReason respawnReason ++ ) { + TeleportTransition teleportTransition; + boolean isBedSpawn = false; + boolean isAnchorSpawn = false; + Runnable consumeAnchorCharge = null; + // Paper end ServerPlayer.RespawnConfig respawnConfig = this.getRespawnConfig(); - ServerLevel level = this.server.getLevel(ServerPlayer.RespawnConfig.getDimensionOrDefault(respawnConfig)); - if (level != null && respawnConfig != null) { - Optional optional = findRespawnAndUseSpawnBlock(level, respawnConfig, useCharge); - if (optional.isPresent()) { - ServerPlayer.RespawnPosAngle respawnPosAngle = optional.get(); + ServerLevel respawnLevel = this.server.getLevel(ServerPlayer.RespawnConfig.getDimensionOrDefault(respawnConfig)); + if (respawnLevel != null && respawnConfig != null) { + Optional respawn = findRespawnAndUseSpawnBlock(respawnLevel, respawnConfig, consumeSpawnBlock); + if (respawn.isPresent()) { + ServerPlayer.RespawnPosAngle respawnPosAngle = respawn.get(); - return new TeleportTransition( + // CraftBukkit start + isBedSpawn = respawnPosAngle.isBedSpawn(); + isAnchorSpawn = respawnPosAngle.isAnchorSpawn(); + consumeAnchorCharge = respawnPosAngle.consumeAnchorCharge(); + teleportTransition = new TeleportTransition( - level, respawnPosAngle.position(), Vec3.ZERO, respawnPosAngle.yaw(), respawnPosAngle.pitch(), postTeleportTransition + respawnLevel, respawnPosAngle.position(), Vec3.ZERO, respawnPosAngle.yaw(), respawnPosAngle.pitch(), postTeleportTransition ); + // CraftBukkit end } else { @@ -707,60 +712,62 @@ } public boolean isReceivingWaypoints() { -@@ -1042,14 +_,16 @@ - && (flag || blockState.getValue(RespawnAnchorBlock.CHARGE) > 0) - && RespawnAnchorBlock.canSetSpawn(level, blockPos)) { - Optional optional = RespawnAnchorBlock.findStandUpPosition(EntityType.PLAYER, level, blockPos); +@@ -1064,15 +_,17 @@ + if (block instanceof RespawnAnchorBlock && (forced || blockState.getValue(RespawnAnchorBlock.CHARGE) > 0) && RespawnAnchorBlock.canSetSpawn(level, pos) + ) + { + Runnable consumeAnchorCharge = null; // Paper - Fix SPIGOT-5989 (don't use charge until after respawn event) - if (!flag && useCharge && optional.isPresent()) { -- level.setBlock(blockPos, blockState.setValue(RespawnAnchorBlock.CHARGE, blockState.getValue(RespawnAnchorBlock.CHARGE) - 1), Block.UPDATE_ALL); -+ consumeAnchorCharge = () -> level.setBlock(blockPos, blockState.setValue(RespawnAnchorBlock.CHARGE, blockState.getValue(RespawnAnchorBlock.CHARGE) - 1), Block.UPDATE_ALL); // Paper - Fix SPIGOT-5989 (don't use charge until after respawn event) + Optional standUpPosition = RespawnAnchorBlock.findStandUpPosition(EntityType.PLAYER, level, pos); + if (!forced && consumeSpawnBlock && standUpPosition.isPresent()) { +- level.setBlock(pos, blockState.setValue(RespawnAnchorBlock.CHARGE, blockState.getValue(RespawnAnchorBlock.CHARGE) - 1), Block.UPDATE_ALL); ++ consumeAnchorCharge = () -> level.setBlock(pos, blockState.setValue(RespawnAnchorBlock.CHARGE, blockState.getValue(RespawnAnchorBlock.CHARGE) - 1), Block.UPDATE_ALL); // Paper - Fix SPIGOT-5989 (don't use charge until after respawn event) } + final Runnable finalConsumeAnchorCharge = consumeAnchorCharge; // Paper - Fix SPIGOT-5989 -- return optional.map(pos -> ServerPlayer.RespawnPosAngle.of(pos, blockPos, 0.0F)); -+ return optional.map(pos -> ServerPlayer.RespawnPosAngle.of(pos, blockPos, 0.0F, false, true, finalConsumeAnchorCharge)); // Paper - Fix SPIGOT-5989 (don't use charge until after respawn event) - } else if (block instanceof BedBlock && level.environmentAttributes().getValue(EnvironmentAttributes.BED_RULE, blockPos).canSetSpawn(level)) { - return BedBlock.findStandUpPosition(EntityType.PLAYER, level, blockPos, blockState.getValue(BedBlock.FACING), yaw) -- .map(pos -> ServerPlayer.RespawnPosAngle.of(pos, blockPos, 0.0F)); -+ .map(pos -> ServerPlayer.RespawnPosAngle.of(pos, blockPos, 0.0F, true, false, null)); // Paper - Fix SPIGOT-5989 - } else if (!flag) { +- return standUpPosition.map(p -> ServerPlayer.RespawnPosAngle.of(p, pos, 0.0F)); ++ return standUpPosition.map(p -> ServerPlayer.RespawnPosAngle.of(p, pos, 0.0F, false, true, finalConsumeAnchorCharge)); // Paper - Fix SPIGOT-5989 (don't use charge until after respawn event) + } else if (block instanceof BedBlock && level.environmentAttributes().getValue(EnvironmentAttributes.BED_RULE, pos).canSetSpawn(level)) { + return BedBlock.findStandUpPosition(EntityType.PLAYER, level, pos, blockState.getValue(BedBlock.FACING), yaw) +- .map(p -> ServerPlayer.RespawnPosAngle.of(p, pos, 0.0F)); ++ .map(p -> ServerPlayer.RespawnPosAngle.of(p, pos, 0.0F, true, false, null)); // Paper - Fix SPIGOT-5989 + } else if (!forced) { return Optional.empty(); } else { -@@ -1057,7 +_,7 @@ - BlockState blockState1 = level.getBlockState(blockPos.above()); - boolean isPossibleToRespawnInThis1 = blockState1.getBlock().isPossibleToRespawnInThis(blockState1); - return isPossibleToRespawnInThis && isPossibleToRespawnInThis1 -- ? Optional.of(new ServerPlayer.RespawnPosAngle(new Vec3(blockPos.getX() + 0.5, blockPos.getY() + 0.1, blockPos.getZ() + 0.5), yaw, pitch)) -+ ? Optional.of(new ServerPlayer.RespawnPosAngle(new Vec3(blockPos.getX() + 0.5, blockPos.getY() + 0.1, blockPos.getZ() + 0.5), yaw, pitch, false, false, null)) // Paper - Fix SPIGOT-5989 +@@ -1080,7 +_,7 @@ + BlockState topState = level.getBlockState(pos.above()); + boolean freeTop = topState.getBlock().isPossibleToRespawnInThis(topState); + return freeBottom && freeTop +- ? Optional.of(new ServerPlayer.RespawnPosAngle(new Vec3(pos.getX() + 0.5, pos.getY() + 0.1, pos.getZ() + 0.5), yaw, pitch)) ++ ? Optional.of(new ServerPlayer.RespawnPosAngle(new Vec3(pos.getX() + 0.5, pos.getY() + 0.1, pos.getZ() + 0.5), yaw, pitch, false, false, null)) // Paper - Fix SPIGOT-5989 : Optional.empty(); } } -@@ -1074,6 +_,7 @@ +@@ -1096,7 +_,8 @@ + } @Override - public @Nullable ServerPlayer teleport(TeleportTransition teleportTransition) { +- public @Nullable ServerPlayer teleport(final TeleportTransition transition) { ++ public @Nullable ServerPlayer teleport(TeleportTransition transition) { // Paper - make non-final + if (this.isSleeping()) return null; // CraftBukkit - SPIGOT-3154 if (this.isRemoved()) { return null; } else { -@@ -1083,13 +_,48 @@ - - ServerLevel level = teleportTransition.newLevel(); - ServerLevel serverLevel = this.level(); -- ResourceKey resourceKey = serverLevel.dimension(); +@@ -1107,12 +_,46 @@ + ServerLevel newLevel = transition.newLevel(); + ServerLevel oldLevel = this.level(); + ResourceKey lastDimension = oldLevel.dimension(); + // CraftBukkit start -+ ResourceKey resourceKey = serverLevel.getTypeKey(); ++ ResourceKey oldLevelTypeKey = oldLevel.getTypeKey(); + + org.bukkit.Location enter = this.getBukkitEntity().getLocation(); -+ PositionMoveRotation absolutePosition = PositionMoveRotation.calculateAbsolute(PositionMoveRotation.of(this), PositionMoveRotation.of(teleportTransition), teleportTransition.relatives()); -+ org.bukkit.Location exit = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(absolutePosition.position(), level, absolutePosition.yRot(), absolutePosition.xRot()); ++ PositionMoveRotation absolutePosition = PositionMoveRotation.calculateAbsolute(PositionMoveRotation.of(this), PositionMoveRotation.of(transition), transition.relatives()); ++ org.bukkit.Location exit = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(absolutePosition.position(), newLevel, absolutePosition.yRot(), absolutePosition.xRot()); + final org.bukkit.event.player.PlayerTeleportEvent tpEvent; + // Paper start - gateway-specific teleport event + if (this.portalProcess != null && this.portalProcess.isSamePortal(((net.minecraft.world.level.block.EndGatewayBlock) net.minecraft.world.level.block.Blocks.END_GATEWAY)) && this.level().getBlockEntity(this.portalProcess.getEntryPosition()) instanceof net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity theEndGatewayBlockEntity) { + tpEvent = new com.destroystokyo.paper.event.player.PlayerTeleportEndGatewayEvent(this.getBukkitEntity(), enter, exit.clone(), new org.bukkit.craftbukkit.block.CraftEndGateway(this.level().getWorld(), theEndGatewayBlockEntity)); + } else { -+ tpEvent = new org.bukkit.event.player.PlayerTeleportEvent(this.getBukkitEntity(), enter, exit.clone(), teleportTransition.cause()); ++ tpEvent = new org.bukkit.event.player.PlayerTeleportEvent(this.getBukkitEntity(), enter, exit.clone(), transition.cause()); + } + // Paper end - gateway-specific teleport event + org.bukkit.Bukkit.getServer().getPluginManager().callEvent(tpEvent); @@ -769,61 +776,58 @@ + return null; + } + if (!newExit.equals(exit)) { -+ level = ((org.bukkit.craftbukkit.CraftWorld) newExit.getWorld()).getHandle(); -+ teleportTransition = new TeleportTransition( -+ level, ++ newLevel = ((org.bukkit.craftbukkit.CraftWorld) newExit.getWorld()).getHandle(); ++ transition = new TeleportTransition( ++ newLevel, + org.bukkit.craftbukkit.util.CraftLocation.toVec3(newExit), + Vec3.ZERO, + newExit.getYaw(), + newExit.getPitch(), -+ teleportTransition.missingRespawnBlock(), -+ teleportTransition.asPassenger(), ++ transition.missingRespawnBlock(), ++ transition.asPassenger(), + Set.of(), -+ teleportTransition.postTeleportTransition(), -+ teleportTransition.cause()); ++ transition.postTeleportTransition(), ++ transition.cause()); + } + // CraftBukkit end - if (!teleportTransition.asPassenger()) { + if (!transition.asPassenger()) { this.removeVehicle(); } -- if (level.dimension() == resourceKey) { -- this.connection.teleport(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives()); -+ // CraftBukkit start -+ if (level.dimension() == serverLevel.dimension()) { -+ this.connection.internalTeleport(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives()); -+ // CraftBukkit end + if (newLevel.dimension() == lastDimension) { +- this.connection.teleport(PositionMoveRotation.of(transition), transition.relatives()); ++ this.connection.internalTeleport(PositionMoveRotation.of(transition), transition.relatives()); // CraftBukkit this.connection.resetPosition(); - teleportTransition.postTeleportTransition().onTransition(this); + transition.postTeleportTransition().onTransition(this); return this; -@@ -1100,18 +_,19 @@ +@@ -1123,18 +_,19 @@ this.connection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked())); PlayerList playerList = this.server.getPlayerList(); playerList.sendPlayerPermissionLevel(this); + this.portalProcess = null; // SPIGOT-7785: there is no need to carry this over as it contains the old world/location and we might run into trouble if there is a portal in the same spot in both worlds - serverLevel.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); + oldLevel.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); this.unsetRemoved(); - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("moving"); -- if (resourceKey == Level.OVERWORLD && level.dimension() == Level.NETHER) { -+ if (resourceKey == net.minecraft.world.level.dimension.LevelStem.OVERWORLD && level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER) { // CraftBukkit - empty to fall through to null to event + ProfilerFiller profiler = Profiler.get(); + profiler.push("moving"); +- if (lastDimension == Level.OVERWORLD && newLevel.dimension() == Level.NETHER) { ++ if (oldLevelTypeKey == net.minecraft.world.level.dimension.LevelStem.OVERWORLD && newLevel.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER) { // CraftBukkit - empty to fall through to null to event this.enteredNetherPosition = this.position(); } - profilerFiller.pop(); - profilerFiller.push("placing"); - this.setServerLevel(level); -- this.connection.teleport(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives()); -+ this.connection.internalTeleport(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives()); // CraftBukkit - use internal teleport without event + profiler.pop(); + profiler.push("placing"); + this.setServerLevel(newLevel); +- this.connection.teleport(PositionMoveRotation.of(transition), transition.relatives()); ++ this.connection.internalTeleport(PositionMoveRotation.of(transition), transition.relatives()); // CraftBukkit - use internal teleport without event this.connection.resetPosition(); - level.addDuringTeleport(this); - profilerFiller.pop(); -@@ -1126,6 +_,15 @@ + newLevel.addDuringTeleport(this); + profiler.pop(); +@@ -1149,6 +_,15 @@ this.lastSentHealth = -1.0F; this.lastSentFood = -1; - this.teleportSpectators(teleportTransition, serverLevel); + this.teleportSpectators(transition, oldLevel); + // CraftBukkit start -+ org.bukkit.event.player.PlayerChangedWorldEvent changeEvent = new org.bukkit.event.player.PlayerChangedWorldEvent(this.getBukkitEntity(), serverLevel.getWorld()); ++ org.bukkit.event.player.PlayerChangedWorldEvent changeEvent = new org.bukkit.event.player.PlayerChangedWorldEvent(this.getBukkitEntity(), oldLevel.getWorld()); + this.level().getCraftServer().getPluginManager().callEvent(changeEvent); + // CraftBukkit end + // Paper start - Reset shield blocking on dimension change @@ -834,24 +838,24 @@ return this; } } -@@ -1140,12 +_,26 @@ - public void triggerDimensionChangeTriggers(ServerLevel level) { - ResourceKey resourceKey = level.dimension(); - ResourceKey resourceKey1 = this.level().dimension(); -- CriteriaTriggers.CHANGED_DIMENSION.trigger(this, resourceKey, resourceKey1); -- if (resourceKey == Level.NETHER && resourceKey1 == Level.OVERWORLD && this.enteredNetherPosition != null) { +@@ -1163,12 +_,26 @@ + public void triggerDimensionChangeTriggers(final ServerLevel oldLevel) { + ResourceKey oldKey = oldLevel.dimension(); + ResourceKey newKey = this.level().dimension(); +- CriteriaTriggers.CHANGED_DIMENSION.trigger(this, oldKey, newKey); +- if (oldKey == Level.NETHER && newKey == Level.OVERWORLD && this.enteredNetherPosition != null) { + // CraftBukkit start -+ ResourceKey maindimensionkey = org.bukkit.craftbukkit.util.CraftDimensionUtil.getMainDimensionKey(level); ++ ResourceKey maindimensionkey = org.bukkit.craftbukkit.util.CraftDimensionUtil.getMainDimensionKey(oldLevel); + ResourceKey maindimensionkey1 = org.bukkit.craftbukkit.util.CraftDimensionUtil.getMainDimensionKey(this.level()); + // Paper start - Add option for strict advancement dimension checks + if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.strictAdvancementDimensionCheck) { -+ maindimensionkey = resourceKey; -+ maindimensionkey1 = resourceKey1; ++ maindimensionkey = oldKey; ++ maindimensionkey1 = newKey; + } + // Paper end - Add option for strict advancement dimension checks + CriteriaTriggers.CHANGED_DIMENSION.trigger(this, maindimensionkey, maindimensionkey1); -+ if (maindimensionkey != resourceKey || maindimensionkey1 != resourceKey1) { -+ CriteriaTriggers.CHANGED_DIMENSION.trigger(this, resourceKey, resourceKey1); ++ if (maindimensionkey != oldKey || maindimensionkey1 != newKey) { ++ CriteriaTriggers.CHANGED_DIMENSION.trigger(this, oldKey, newKey); + } + + if (maindimensionkey == Level.NETHER && maindimensionkey1 == Level.OVERWORLD && this.enteredNetherPosition != null) { @@ -859,43 +863,42 @@ CriteriaTriggers.NETHER_TRAVEL.trigger(this, this.enteredNetherPosition); } -- if (resourceKey1 != Level.NETHER) { +- if (newKey != Level.NETHER) { + if (maindimensionkey1 != Level.NETHER) { // CraftBukkit this.enteredNetherPosition = null; } } -@@ -1161,11 +_,12 @@ +@@ -1184,10 +_,12 @@ this.containerMenu.broadcastChanges(); } - @Override -- public Either startSleepInBed(BlockPos bedPos) { -- Direction direction = this.level().getBlockState(bedPos).getValue(HorizontalDirectionalBlock.FACING); +- public Either startSleepInBed(final BlockPos pos) { +- Direction direction = this.level().getBlockState(pos).getValue(HorizontalDirectionalBlock.FACING); - if (!this.isSleeping() && this.isAlive()) { -- BedRule bedRule = this.level().environmentAttributes().getValue(EnvironmentAttributes.BED_RULE, bedPos); + // CraftBukkit start - moved bed result checks from below into separate method -+ private Either getBedResult(BlockPos bedPos, Direction direction) { -+ BedRule bedRule = this.level().environmentAttributes().getValue(EnvironmentAttributes.BED_RULE, bedPos); ++ private Either getBedResult(final BlockPos pos, final Direction direction) { ++ BedRule bedRule = this.level().environmentAttributes().getValue(EnvironmentAttributes.BED_RULE, pos); + if (bedRule.explodes()) { + return Either.left(Player.BedSleepingProblem.EXPLOSION); + } else if (!this.isSleeping() && this.isAlive()) { - boolean canSleep = bedRule.canSleep(this.level()); - boolean canSetSpawn = bedRule.canSetSpawn(this.level()); - if (!canSetSpawn && !canSleep) { -@@ -1177,7 +_,7 @@ + BedRule rule = this.level().environmentAttributes().getValue(EnvironmentAttributes.BED_RULE, pos); + boolean canSleep = rule.canSleep(this.level()); + boolean canSetSpawn = rule.canSetSpawn(this.level()); +@@ -1200,7 +_,7 @@ } else { if (canSetSpawn) { this.setRespawnPosition( -- new ServerPlayer.RespawnConfig(LevelData.RespawnData.of(this.level().dimension(), bedPos, this.getYRot(), this.getXRot()), false), true -+ new ServerPlayer.RespawnConfig(LevelData.RespawnData.of(this.level().dimension(), bedPos, this.getYRot(), this.getXRot()), false), true, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.BED // Paper - Add PlayerSetSpawnEvent +- new ServerPlayer.RespawnConfig(LevelData.RespawnData.of(this.level().dimension(), pos, this.getYRot(), this.getXRot()), false), true ++ new ServerPlayer.RespawnConfig(LevelData.RespawnData.of(this.level().dimension(), pos, this.getYRot(), this.getXRot()), false), true, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.BED // Paper - Add PlayerSetSpawnEvent ); } -@@ -1199,7 +_,37 @@ +@@ -1229,7 +_,37 @@ } } -- Either either = super.startSleepInBed(bedPos).ifRight(unit -> { +- Either result = super.startSleepInBed(pos).ifRight(unit -> { + // CraftBukkit start + return Either.right(Unit.INSTANCE); + } @@ -906,9 +909,9 @@ + } + + @Override -+ public Either startSleepInBed(BlockPos bedPos, boolean force) { -+ Direction direction = this.level().getBlockState(bedPos).getValue(HorizontalDirectionalBlock.FACING); -+ Either bedResult = this.getBedResult(bedPos, direction); ++ public Either startSleepInBed(BlockPos pos, boolean force) { ++ Direction direction = this.level().getBlockState(pos).getValue(HorizontalDirectionalBlock.FACING); ++ Either bedResult = this.getBedResult(pos, direction); + + if (bedResult.left().orElse(null) == net.minecraft.world.entity.player.Player.BedSleepingProblem.OTHER_PROBLEM) { + return bedResult; // return immediately if the result is not bypassable by plugins @@ -918,22 +921,22 @@ + bedResult = Either.right(Unit.INSTANCE); + } + -+ bedResult = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBedEnterEvent(this, bedPos, bedResult); ++ bedResult = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBedEnterEvent(this, pos, bedResult); + if (bedResult.left().isPresent()) { + return bedResult; + } + + { + { -+ Either either = super.startSleepInBed(bedPos, force).ifRight(unit -> { ++ Either result = super.startSleepInBed(pos, force).ifRight(unit -> { + // CraftBukkit end this.awardStat(Stats.SLEEP_IN_BED); CriteriaTriggers.SLEPT_IN_BED.trigger(this); }); -@@ -1209,10 +_,7 @@ +@@ -1239,10 +_,7 @@ this.level().updateSleepingPlayerList(); - return either; + return result; - } } - } else { @@ -941,10 +944,10 @@ } } -@@ -1238,21 +_,29 @@ +@@ -1270,19 +_,30 @@ @Override - public void stopSleepInBed(boolean wakeImmediately, boolean updateLevelForSleepingPlayers) { + public void stopSleepInBed(final boolean forcefulWakeUp, final boolean updateLevelList) { + if (!this.isSleeping()) return; // CraftBukkit - Can't leave bed if not in one! + // CraftBukkit start - fire PlayerBedLeaveEvent + org.bukkit.block.Block bed = org.bukkit.craftbukkit.block.CraftBlock.at(this.level(), this.getSleepingPos().orElse(this.blockPosition())); @@ -957,7 +960,7 @@ this.level().getChunkSource().sendToTrackingPlayersAndSelf(this, new ClientboundAnimatePacket(this, ClientboundAnimatePacket.WAKE_UP)); } - super.stopSleepInBed(wakeImmediately, updateLevelForSleepingPlayers); + super.stopSleepInBed(forcefulWakeUp, updateLevelList); if (this.connection != null) { - this.connection.teleport(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); + this.connection.teleport(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot(), org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.EXIT_BED); // CraftBukkit @@ -965,16 +968,16 @@ } @Override - public boolean isInvulnerableTo(ServerLevel level, DamageSource damageSource) { -- return super.isInvulnerableTo(level, damageSource) -+ return (super.isInvulnerableTo(level, damageSource) // Paper - disable player cramming; - || this.isChangingDimension() && !damageSource.is(DamageTypes.ENDER_PEARL) -- || !this.connection.hasClientLoaded(); -+ || !this.connection.hasClientLoaded()) || (!this.level().paperConfig().collisions.allowPlayerCrammingDamage && damageSource.is(DamageTypes.CRAMMING)); // Paper - disable player cramming; + public boolean isInvulnerableTo(final ServerLevel level, final DamageSource source) { +- return super.isInvulnerableTo(level, source) || this.isChangingDimension() && !source.is(DamageTypes.ENDER_PEARL) || !this.connection.hasClientLoaded(); ++ // Paper start - disable player cramming ++ return (super.isInvulnerableTo(level, source) || this.isChangingDimension() && !source.is(DamageTypes.ENDER_PEARL) || !this.connection.hasClientLoaded()) ++ || (!this.level().paperConfig().collisions.allowPlayerCrammingDamage && source.is(DamageTypes.CRAMMING)); ++ // Paper end - disable player cramming } @Override -@@ -1300,8 +_,9 @@ +@@ -1330,8 +_,9 @@ this.connection.send(new ClientboundShowDialogPacket(dialog)); } @@ -985,8 +988,8 @@ } @Override -@@ -1309,12 +_,39 @@ - if (menu == null) { +@@ -1339,12 +_,39 @@ + if (provider == null) { return OptionalInt.empty(); } else { - if (this.containerMenu != this.inventoryMenu) { @@ -995,25 +998,25 @@ } this.nextContainerCounter(); - AbstractContainerMenu abstractContainerMenu = menu.createMenu(this.containerCounter, this.getInventory(), this); + AbstractContainerMenu menu = provider.createMenu(this.containerCounter, this.getInventory(), this); + Component title = null; // Paper - Add titleOverride to InventoryOpenEvent + // CraftBukkit start - Inventory open hook -+ if (abstractContainerMenu != null) { -+ abstractContainerMenu.setTitle(menu.getDisplayName()); ++ if (menu != null) { ++ menu.setTitle(provider.getDisplayName()); + + boolean cancelled = false; + // Paper start - Add titleOverride to InventoryOpenEvent -+ final com.mojang.datafixers.util.Pair result = org.bukkit.craftbukkit.event.CraftEventFactory.callInventoryOpenEventWithTitle(this, abstractContainerMenu, cancelled); -+ abstractContainerMenu = result.getSecond(); ++ final com.mojang.datafixers.util.Pair result = org.bukkit.craftbukkit.event.CraftEventFactory.callInventoryOpenEventWithTitle(this, menu, cancelled); ++ menu = result.getSecond(); + title = io.papermc.paper.adventure.PaperAdventure.asVanilla(result.getFirst()); + // Paper end - Add titleOverride to InventoryOpenEvent -+ if (abstractContainerMenu == null && !cancelled) { // Let pre-cancelled events fall through ++ if (menu == null && !cancelled) { // Let pre-cancelled events fall through + // SPIGOT-5263 - close chest if cancelled -+ if (menu instanceof Container container) { ++ if (provider instanceof Container container) { + container.stopOpen(this); -+ } else if (menu instanceof net.minecraft.world.level.block.ChestBlock.DoubleInventory doubleInventory) { ++ } else if (provider instanceof org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest.Provider doubleChestProvider) { + // SPIGOT-5355 - double chests too :( -+ doubleInventory.container.stopOpen(this); ++ doubleChestProvider.container.stopOpen(this); + // Paper start - Fix InventoryOpenEvent cancellation + } else if (!this.enderChestInventory.isActiveChest(null)) { + this.enderChestInventory.stopOpen(this); @@ -1023,38 +1026,37 @@ + } + } + // CraftBukkit end - if (abstractContainerMenu == null) { + if (menu == null) { if (this.isSpectator()) { - this.displayClientMessage(Component.translatable("container.spectatorCantOpen").withStyle(ChatFormatting.RED), true); -@@ -1322,10 +_,14 @@ + this.sendOverlayMessage(Component.translatable("container.spectatorCantOpen").withStyle(ChatFormatting.RED)); +@@ -1352,9 +_,13 @@ return OptionalInt.empty(); } else { +- this.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), provider.getDisplayName())); + // CraftBukkit start -+ this.containerMenu = abstractContainerMenu; // Moved up ++ this.containerMenu = menu; // Moved up + if (!this.isImmobile()) - this.connection -- .send(new ClientboundOpenScreenPacket(abstractContainerMenu.containerId, abstractContainerMenu.getType(), menu.getDisplayName())); -+ .send(new net.minecraft.network.protocol.game.ClientboundOpenScreenPacket(abstractContainerMenu.containerId, abstractContainerMenu.getType(), java.util.Objects.requireNonNullElseGet(title, abstractContainerMenu::getTitle))); // Paper - Add titleOverride to InventoryOpenEven ++ this.connection.send(new net.minecraft.network.protocol.game.ClientboundOpenScreenPacket(menu.containerId, menu.getType(), java.util.Objects.requireNonNullElseGet(title, menu::getTitle))); // Paper - Add titleOverride to InventoryOpenEven + // CraftBukkit end - this.initMenu(abstractContainerMenu); -- this.containerMenu = abstractContainerMenu; + this.initMenu(menu); +- this.containerMenu = menu; + // CraftBukkit - moved up return OptionalInt.of(this.containerCounter); } } -@@ -1338,27 +_,49 @@ +@@ -1374,27 +_,49 @@ @Override - public void openHorseInventory(AbstractHorse horse, Container inventory) { + public void openHorseInventory(final AbstractHorse horse, final Container container) { + // CraftBukkit start - Inventory open hook + this.nextContainerCounter(); // Moved up from below -+ AbstractContainerMenu container = new HorseInventoryMenu(this.containerCounter, this.getInventory(), inventory, horse, horse.getInventoryColumns()); -+ container.setTitle(horse.getDisplayName()); -+ container = org.bukkit.craftbukkit.event.CraftEventFactory.callInventoryOpenEvent(this, container); ++ AbstractContainerMenu menu = new HorseInventoryMenu(this.containerCounter, this.getInventory(), container, horse, horse.getInventoryColumns()); ++ menu.setTitle(horse.getDisplayName()); ++ menu = org.bukkit.craftbukkit.event.CraftEventFactory.callInventoryOpenEvent(this, menu); + -+ if (container == null) { -+ inventory.stopOpen(this); ++ if (menu == null) { ++ container.stopOpen(this); + return; + } + // CraftBukkit end @@ -1067,21 +1069,21 @@ + // this.nextContainerCounter(); // CraftBukkit - moved up int inventoryColumns = horse.getInventoryColumns(); this.connection.send(new ClientboundMountScreenOpenPacket(this.containerCounter, inventoryColumns, horse.getId())); -- this.containerMenu = new HorseInventoryMenu(this.containerCounter, this.getInventory(), inventory, horse, inventoryColumns); -+ this.containerMenu = container; // CraftBukkit +- this.containerMenu = new HorseInventoryMenu(this.containerCounter, this.getInventory(), container, horse, inventoryColumns); ++ this.containerMenu = menu; // CraftBukkit this.initMenu(this.containerMenu); } @Override - public void openNautilusInventory(AbstractNautilus nautilus, Container inventory) { + public void openNautilusInventory(final AbstractNautilus nautilus, final Container container) { + // Paper start - Inventory open hook + this.nextContainerCounter(); // moved up from below -+ AbstractContainerMenu menu = new NautilusInventoryMenu(this.containerCounter, this.getInventory(), inventory, nautilus, nautilus.getInventoryColumns()); ++ AbstractContainerMenu menu = new NautilusInventoryMenu(this.containerCounter, this.getInventory(), container, nautilus, nautilus.getInventoryColumns()); + menu.setTitle(nautilus.getDisplayName()); + menu = org.bukkit.craftbukkit.event.CraftEventFactory.callInventoryOpenEvent(this, menu); + + if (menu == null) { -+ inventory.stopOpen(this); ++ container.stopOpen(this); + return; + } + // Paper end @@ -1093,12 +1095,12 @@ + // this.nextContainerCounter(); Paper - Move Up int inventoryColumns = nautilus.getInventoryColumns(); this.connection.send(new ClientboundMountScreenOpenPacket(this.containerCounter, inventoryColumns, nautilus.getId())); -- this.containerMenu = new NautilusInventoryMenu(this.containerCounter, this.getInventory(), inventory, nautilus, inventoryColumns); +- this.containerMenu = new NautilusInventoryMenu(this.containerCounter, this.getInventory(), container, nautilus, inventoryColumns); + this.containerMenu = menu; // Paper - Moved up declaration this.initMenu(this.containerMenu); } -@@ -1380,10 +_,30 @@ +@@ -1416,10 +_,30 @@ @Override public void closeContainer() { @@ -1129,63 +1131,63 @@ @Override public void doCloseContainer() { this.containerMenu.removed(this); -@@ -1406,19 +_,19 @@ - int rounded = Math.round((float)Math.sqrt(dx * dx + dy * dy + dz * dz) * 100.0F); - if (rounded > 0) { - this.awardStat(Stats.SWIM_ONE_CM, rounded); -- this.causeFoodExhaustion(0.01F * rounded * 0.01F); -+ this.causeFoodExhaustion(this.level().spigotConfig.swimMultiplier * (float) rounded * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.SWIM); // CraftBukkit - EntityExhaustionEvent // Spigot +@@ -1442,19 +_,19 @@ + int distance = Math.round((float)Math.sqrt(dx * dx + dy * dy + dz * dz) * 100.0F); + if (distance > 0) { + this.awardStat(Stats.SWIM_ONE_CM, distance); +- this.causeFoodExhaustion(0.01F * distance * 0.01F); ++ this.causeFoodExhaustion(this.level().spigotConfig.swimMultiplier * distance * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.WALK_ON_WATER); // CraftBukkit - EntityExhaustionEvent // Spigot } } else if (this.isEyeInFluid(FluidTags.WATER)) { - int rounded = Math.round((float)Math.sqrt(dx * dx + dy * dy + dz * dz) * 100.0F); - if (rounded > 0) { - this.awardStat(Stats.WALK_UNDER_WATER_ONE_CM, rounded); -- this.causeFoodExhaustion(0.01F * rounded * 0.01F); -+ this.causeFoodExhaustion(this.level().spigotConfig.swimMultiplier * (float) rounded * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.WALK_UNDERWATER); // CraftBukkit - EntityExhaustionEvent // Spigot + int distance = Math.round((float)Math.sqrt(dx * dx + dy * dy + dz * dz) * 100.0F); + if (distance > 0) { + this.awardStat(Stats.WALK_UNDER_WATER_ONE_CM, distance); +- this.causeFoodExhaustion(0.01F * distance * 0.01F); ++ this.causeFoodExhaustion(this.level().spigotConfig.swimMultiplier * distance * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.WALK_ON_WATER); // CraftBukkit - EntityExhaustionEvent // Spigot } } else if (this.isInWater()) { - int rounded = Math.round((float)Math.sqrt(dx * dx + dz * dz) * 100.0F); - if (rounded > 0) { - this.awardStat(Stats.WALK_ON_WATER_ONE_CM, rounded); -- this.causeFoodExhaustion(0.01F * rounded * 0.01F); -+ this.causeFoodExhaustion(this.level().spigotConfig.swimMultiplier * (float) rounded * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.WALK_ON_WATER); // CraftBukkit - EntityExhaustionEvent // Spigot + int horizontalDistance = Math.round((float)Math.sqrt(dx * dx + dz * dz) * 100.0F); + if (horizontalDistance > 0) { + this.awardStat(Stats.WALK_ON_WATER_ONE_CM, horizontalDistance); +- this.causeFoodExhaustion(0.01F * horizontalDistance * 0.01F); ++ this.causeFoodExhaustion(this.level().spigotConfig.swimMultiplier * horizontalDistance * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.WALK_ON_WATER); // CraftBukkit - EntityExhaustionEvent // Spigot } } else if (this.onClimbable()) { if (dy > 0.0) { -@@ -1429,13 +_,13 @@ - if (rounded > 0) { +@@ -1465,13 +_,13 @@ + if (horizontalDistance > 0) { if (this.isSprinting()) { - this.awardStat(Stats.SPRINT_ONE_CM, rounded); -- this.causeFoodExhaustion(0.1F * rounded * 0.01F); -+ this.causeFoodExhaustion(this.level().spigotConfig.sprintMultiplier * (float) rounded * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.SPRINT); // CraftBukkit - EntityExhaustionEvent // Spigot + this.awardStat(Stats.SPRINT_ONE_CM, horizontalDistance); +- this.causeFoodExhaustion(0.1F * horizontalDistance * 0.01F); ++ this.causeFoodExhaustion(this.level().spigotConfig.sprintMultiplier * horizontalDistance * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.WALK); // CraftBukkit - EntityExhaustionEvent // Spigot } else if (this.isCrouching()) { - this.awardStat(Stats.CROUCH_ONE_CM, rounded); -- this.causeFoodExhaustion(0.0F * rounded * 0.01F); -+ this.causeFoodExhaustion(this.level().spigotConfig.otherMultiplier * (float) rounded * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.CROUCH); // CraftBukkit - EntityExhaustionEvent // Spigot + this.awardStat(Stats.CROUCH_ONE_CM, horizontalDistance); +- this.causeFoodExhaustion(0.0F * horizontalDistance * 0.01F); ++ this.causeFoodExhaustion(this.level().spigotConfig.otherMultiplier * horizontalDistance * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.CROUCH); // CraftBukkit - EntityExhaustionEvent // Spigot } else { - this.awardStat(Stats.WALK_ONE_CM, rounded); -- this.causeFoodExhaustion(0.0F * rounded * 0.01F); -+ this.causeFoodExhaustion(this.level().spigotConfig.otherMultiplier * (float) rounded * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.WALK); // CraftBukkit - EntityExhaustionEvent // Spigot + this.awardStat(Stats.WALK_ONE_CM, horizontalDistance); +- this.causeFoodExhaustion(0.0F * horizontalDistance * 0.01F); ++ this.causeFoodExhaustion(this.level().spigotConfig.otherMultiplier * horizontalDistance * 0.01F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.WALK); // CraftBukkit - EntityExhaustionEvent // Spigot } } } else if (this.isFallFlying()) { -@@ -1479,13 +_,13 @@ +@@ -1515,13 +_,13 @@ @Override - public void awardStat(Stat stat, int amount) { - this.stats.increment(this, stat, amount); -- this.level().getScoreboard().forAllObjectives(stat, this, score -> score.add(amount)); -+ this.level().getCraftServer().getScoreboardManager().forAllObjectives(stat, this, scoreAccess -> scoreAccess.add(amount)); // CraftBukkit - Get our scores instead + public void awardStat(final Stat stat, final int count) { + this.stats.increment(this, stat, count); +- this.level().getScoreboard().forAllObjectives(stat, this, score -> score.add(count)); ++ this.level().getCraftServer().getScoreboardManager().forAllObjectives(stat, this, score -> score.add(count)); // CraftBukkit - Get our scores instead } @Override - public void resetStat(Stat stat) { + public void resetStat(final Stat stat) { this.stats.setValue(this, stat, 0); - this.level().getScoreboard().forAllObjectives(stat, this, ScoreAccess::reset); + this.level().getCraftServer().getScoreboardManager().forAllObjectives(stat, this, ScoreAccess::reset); // CraftBukkit - Get our scores instead } @Override -@@ -1516,9 +_,9 @@ +@@ -1552,9 +_,9 @@ super.jumpFromGround(); this.awardStat(Stats.JUMP); if (this.isSprinting()) { @@ -1197,7 +1199,7 @@ } } -@@ -1533,6 +_,13 @@ +@@ -1569,6 +_,13 @@ public void disconnect() { this.disconnected = true; this.ejectPassengers(); @@ -1211,7 +1213,7 @@ if (this.isSleeping()) { this.stopSleepInBed(true, false); } -@@ -1544,6 +_,7 @@ +@@ -1580,6 +_,7 @@ public void resetSentInfo() { this.lastSentHealth = -1.0E8F; @@ -1219,38 +1221,38 @@ } @Override -@@ -1578,18 +_,18 @@ +@@ -1609,18 +_,18 @@ this.onUpdateAbilities(); - this.getAttributes().assignBaseValues(that.getAttributes()); - if (keepEverything) { -- this.getAttributes().assignPermanentModifiers(that.getAttributes()); -+ // this.getAttributes().assignPermanentModifiers(that.getAttributes()); // CraftBukkit - this.setHealth(that.getHealth()); - this.foodData = that.foodData; - - for (MobEffectInstance mobEffectInstance : that.getActiveEffects()) { -- this.addEffect(new MobEffectInstance(mobEffectInstance)); -+ // this.addEffect(new MobEffectInstance(mobEffectInstance)); // CraftBukkit + this.getAttributes().assignBaseValues(oldPlayer.getAttributes()); + if (restoreAll) { +- this.getAttributes().assignPermanentModifiers(oldPlayer.getAttributes()); ++ // this.getAttributes().assignPermanentModifiers(oldPlayer.getAttributes()); // CraftBukkit + this.setHealth(oldPlayer.getHealth()); + this.foodData = oldPlayer.foodData; + + for (MobEffectInstance effect : oldPlayer.getActiveEffects()) { +- this.addEffect(new MobEffectInstance(effect)); ++ // this.addEffect(new MobEffectInstance(effect)); // CraftBukkit } - this.transferInventoryXpAndScore(that); - this.portalProcess = that.portalProcess; + this.transferInventoryXpAndScore(oldPlayer); + this.portalProcess = oldPlayer.portalProcess; } else { - this.setHealth(this.getMaxHealth()); + // this.setHealth(this.getMaxHealth()); // CraftBukkit - if (this.level().getGameRules().get(GameRules.KEEP_INVENTORY) || that.isSpectator()) { - this.transferInventoryXpAndScore(that); + if (this.level().getGameRules().get(GameRules.KEEP_INVENTORY) || oldPlayer.isSpectator()) { + this.transferInventoryXpAndScore(oldPlayer); } -@@ -1601,7 +_,7 @@ +@@ -1632,7 +_,7 @@ this.lastSentExp = -1; this.lastSentHealth = -1.0F; this.lastSentFood = -1; -- this.recipeBook.copyOverData(that.recipeBook); -+ // this.recipeBook.copyOverData(that.recipeBook); // CraftBukkit - this.seenCredits = that.seenCredits; - this.enteredNetherPosition = that.enteredNetherPosition; - this.chunkTrackingView = that.chunkTrackingView; -@@ -1653,9 +_,22 @@ +- this.recipeBook.copyOverData(oldPlayer.recipeBook); ++ // this.recipeBook.copyOverData(oldPlayer.recipeBook); // CraftBukkit + this.seenCredits = oldPlayer.seenCredits; + this.enteredNetherPosition = oldPlayer.enteredNetherPosition; + this.chunkTrackingView = oldPlayer.chunkTrackingView; +@@ -1684,9 +_,22 @@ CriteriaTriggers.EFFECTS_CHANGED.trigger(this, null); } @@ -1262,58 +1264,57 @@ + // Paper end - use dismount cause + @Override - public void teleportTo(double x, double y, double z) { + public void teleportTo(final double x, final double y, final double z) { - this.connection.teleport(new PositionMoveRotation(new Vec3(x, y, z), Vec3.ZERO, 0.0F, 0.0F), Relative.union(Relative.DELTA, Relative.ROTATION)); + // Paper start - pass cause + this.teleportTo(x, y, z, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN); + } + -+ public void teleportTo(double x, double y, double z, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { ++ public void teleportTo(final double x, final double y, final double z, final org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { + this.connection.teleport(new PositionMoveRotation(new Vec3(x, y, z), Vec3.ZERO, 0.0F, 0.0F), Relative.union(Relative.DELTA, Relative.ROTATION), cause); + // Paper end - pass cause } @Override -@@ -1664,7 +_,7 @@ - } - - @Override -- public boolean teleportTo(ServerLevel level, double x, double y, double z, Set relativeMovements, float yaw, float pitch, boolean setCamera) { -+ public boolean teleportTo(ServerLevel level, double x, double y, double z, Set relativeMovements, float yaw, float pitch, boolean setCamera, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { // CraftBukkit +@@ -1704,6 +_,7 @@ + final float newYRot, + final float newXRot, + final boolean resetCamera ++ , final org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause // CraftBukkit + ) { if (this.isSleeping()) { this.stopSleepInBed(true, true); - } -@@ -1673,7 +_,7 @@ +@@ -1713,7 +_,7 @@ this.setCamera(this); } -- boolean flag = super.teleportTo(level, x, y, z, relativeMovements, yaw, pitch, setCamera); -+ boolean flag = super.teleportTo(level, x, y, z, relativeMovements, yaw, pitch, setCamera, cause); // CraftBukkit - if (flag) { - this.setYHeadRot(relativeMovements.contains(Relative.Y_ROT) ? this.getYHeadRot() + yaw : yaw); +- boolean success = super.teleportTo(level, x, y, z, relatives, newYRot, newXRot, resetCamera); ++ boolean success = super.teleportTo(level, x, y, z, relatives, newYRot, newXRot, resetCamera, cause); // CraftBukkit + if (success) { + this.setYHeadRot(relatives.contains(Relative.Y_ROT) ? this.getYHeadRot() + newYRot : newYRot); this.connection.resetFlyingTicks(); -@@ -1712,9 +_,18 @@ +@@ -1752,9 +_,18 @@ } - public boolean setGameMode(GameType gameMode) { + public boolean setGameMode(final GameType mode) { + // Paper start - Expand PlayerGameModeChangeEvent -+ org.bukkit.event.player.PlayerGameModeChangeEvent event = this.setGameMode(gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.UNKNOWN, null); ++ org.bukkit.event.player.PlayerGameModeChangeEvent event = this.setGameMode(mode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.UNKNOWN, null); + return event != null && !event.isCancelled(); + } -+ public org.bukkit.event.player.@Nullable PlayerGameModeChangeEvent setGameMode(GameType gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause cause, net.kyori.adventure.text.@Nullable Component message) { - boolean isSpectator = this.isSpectator(); -- if (!this.gameMode.changeGameModeForPlayer(gameMode)) { ++ public org.bukkit.event.player.@Nullable PlayerGameModeChangeEvent setGameMode(final GameType mode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause cause, net.kyori.adventure.text.@Nullable Component message) { + boolean wasSpectator = this.isSpectator(); +- if (!this.gameMode.changeGameModeForPlayer(mode)) { - return false; -+ org.bukkit.event.player.PlayerGameModeChangeEvent event = this.gameMode.changeGameModeForPlayer(gameMode, cause, message); ++ org.bukkit.event.player.PlayerGameModeChangeEvent event = this.gameMode.changeGameModeForPlayer(mode, cause, message); + if (event == null) { + return null; + } else if (event.isCancelled()) { + return event; // need to return the event for the cancel message + // Paper end - Expand PlayerGameModeChangeEvent } else { - this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, gameMode.getId())); - if (gameMode == GameType.SPECTATOR) { -@@ -1731,7 +_,7 @@ + this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, mode.getId())); + if (mode == GameType.SPECTATOR) { +@@ -1771,7 +_,7 @@ this.onUpdateAbilities(); this.updateEffectVisibility(); @@ -1322,90 +1323,90 @@ } } -@@ -1786,8 +_,13 @@ +@@ -1840,8 +_,13 @@ } - public void sendChatMessage(OutgoingChatMessage message, boolean filtered, ChatType.Bound boundChatType) { + public void sendChatMessage(final OutgoingChatMessage message, final boolean filtered, final ChatType.Bound chatType) { + // Paper start -+ this.sendChatMessage(message, filtered, boundChatType, null); ++ this.sendChatMessage(message, filtered, chatType, null); + } -+ public void sendChatMessage(OutgoingChatMessage message, boolean filtered, ChatType.Bound boundChatType, @Nullable Component unsigned) { ++ public void sendChatMessage(final OutgoingChatMessage message, final boolean filtered, final ChatType.Bound chatType, @Nullable Component unsigned) { + // Paper end if (this.acceptsChatMessages()) { -- message.sendToPlayer(this, filtered, boundChatType); -+ message.sendToPlayer(this, filtered, boundChatType, unsigned); // Paper +- message.sendToPlayer(this, filtered, chatType); ++ message.sendToPlayer(this, filtered, chatType, unsigned); // Paper } } -@@ -1798,7 +_,42 @@ +@@ -1852,7 +_,42 @@ } - public void updateOptions(ClientInformation clientInformation) { + public void updateOptions(final ClientInformation information) { + // Paper start - settings event + new com.destroystokyo.paper.event.player.PlayerClientOptionsChangeEvent(this.getBukkitEntity(), Util.make(new java.util.IdentityHashMap<>(), map -> { -+ map.put(com.destroystokyo.paper.ClientOption.LOCALE, clientInformation.language()); -+ map.put(com.destroystokyo.paper.ClientOption.VIEW_DISTANCE, clientInformation.viewDistance()); -+ map.put(com.destroystokyo.paper.ClientOption.CHAT_VISIBILITY, com.destroystokyo.paper.ClientOption.ChatVisibility.valueOf(clientInformation.chatVisibility().name())); -+ map.put(com.destroystokyo.paper.ClientOption.CHAT_COLORS_ENABLED, clientInformation.chatColors()); -+ map.put(com.destroystokyo.paper.ClientOption.SKIN_PARTS, new com.destroystokyo.paper.PaperSkinParts(clientInformation.modelCustomisation())); -+ map.put(com.destroystokyo.paper.ClientOption.MAIN_HAND, clientInformation.mainHand() == net.minecraft.world.entity.HumanoidArm.LEFT ? org.bukkit.inventory.MainHand.LEFT : org.bukkit.inventory.MainHand.RIGHT); -+ map.put(com.destroystokyo.paper.ClientOption.TEXT_FILTERING_ENABLED, clientInformation.textFilteringEnabled()); -+ map.put(com.destroystokyo.paper.ClientOption.ALLOW_SERVER_LISTINGS, clientInformation.allowsListing()); -+ map.put(com.destroystokyo.paper.ClientOption.PARTICLE_VISIBILITY, com.destroystokyo.paper.ClientOption.ParticleVisibility.valueOf(clientInformation.particleStatus().name())); ++ map.put(com.destroystokyo.paper.ClientOption.LOCALE, information.language()); ++ map.put(com.destroystokyo.paper.ClientOption.VIEW_DISTANCE, information.viewDistance()); ++ map.put(com.destroystokyo.paper.ClientOption.CHAT_VISIBILITY, com.destroystokyo.paper.ClientOption.ChatVisibility.valueOf(information.chatVisibility().name())); ++ map.put(com.destroystokyo.paper.ClientOption.CHAT_COLORS_ENABLED, information.chatColors()); ++ map.put(com.destroystokyo.paper.ClientOption.SKIN_PARTS, new com.destroystokyo.paper.PaperSkinParts(information.modelCustomisation())); ++ map.put(com.destroystokyo.paper.ClientOption.MAIN_HAND, information.mainHand() == net.minecraft.world.entity.HumanoidArm.LEFT ? org.bukkit.inventory.MainHand.LEFT : org.bukkit.inventory.MainHand.RIGHT); ++ map.put(com.destroystokyo.paper.ClientOption.TEXT_FILTERING_ENABLED, information.textFilteringEnabled()); ++ map.put(com.destroystokyo.paper.ClientOption.ALLOW_SERVER_LISTINGS, information.allowsListing()); ++ map.put(com.destroystokyo.paper.ClientOption.PARTICLE_VISIBILITY, com.destroystokyo.paper.ClientOption.ParticleVisibility.valueOf(information.particleStatus().name())); + })).callEvent(); + // Paper end - settings event + // CraftBukkit start -+ if (this.getMainArm() != clientInformation.mainHand()) { ++ if (this.getMainArm() != information.mainHand()) { + org.bukkit.event.player.PlayerChangedMainHandEvent event = new org.bukkit.event.player.PlayerChangedMainHandEvent( + this.getBukkitEntity(), -+ clientInformation.mainHand() == net.minecraft.world.entity.HumanoidArm.LEFT ? org.bukkit.inventory.MainHand.LEFT : org.bukkit.inventory.MainHand.RIGHT ++ information.mainHand() == net.minecraft.world.entity.HumanoidArm.LEFT ? org.bukkit.inventory.MainHand.LEFT : org.bukkit.inventory.MainHand.RIGHT + ); -+ this.server.server.getPluginManager().callEvent(event); ++ event.callEvent(); + } -+ if (this.language == null || !this.language.equals(clientInformation.language())) { // Paper ++ if (this.language == null || !this.language.equals(information.language())) { // Paper + org.bukkit.event.player.PlayerLocaleChangeEvent event = new org.bukkit.event.player.PlayerLocaleChangeEvent( + this.getBukkitEntity(), -+ clientInformation.language() ++ information.language() + ); -+ this.server.server.getPluginManager().callEvent(event); ++ event.callEvent(); + } + // CraftBukkit end + // Paper start - don't call options events on login -+ this.updateOptionsNoEvents(clientInformation); ++ this.updateOptionsNoEvents(information); + } -+ public void updateOptionsNoEvents(ClientInformation clientInformation) { ++ public void updateOptionsNoEvents(final ClientInformation information) { + // Paper end - this.language = clientInformation.language(); + this.language = information.language(); + this.adventure$locale = java.util.Objects.requireNonNullElse(net.kyori.adventure.translation.Translator.parseLocale(this.language), java.util.Locale.US); // Paper - this.requestedViewDistance = clientInformation.viewDistance(); - this.chatVisibility = clientInformation.chatVisibility(); - this.canChatColor = clientInformation.chatColors(); -@@ -1883,8 +_,23 @@ - Entity camera = this.getCamera(); - this.camera = (Entity)(entityToSpectate == null ? this : entityToSpectate); - if (camera != this.camera) { + this.requestedViewDistance = information.viewDistance(); + this.chatVisibility = information.chatVisibility(); + this.canChatColor = information.chatColors(); +@@ -1937,8 +_,23 @@ + Entity oldCamera = this.getCamera(); + this.camera = (Entity)(newCamera == null ? this : newCamera); + if (oldCamera != this.camera) { + // Paper start - Add PlayerStartSpectatingEntityEvent and PlayerStopSpectatingEntity + if (this.camera == this) { + com.destroystokyo.paper.event.player.PlayerStopSpectatingEntityEvent playerStopSpectatingEntityEvent = new com.destroystokyo.paper.event.player.PlayerStopSpectatingEntityEvent(this.getBukkitEntity(), camera.getBukkitEntity()); + if (!playerStopSpectatingEntityEvent.callEvent()) { -+ this.camera = camera; // rollback camera entity again ++ this.camera = oldCamera; // rollback camera entity again + return; + } + } else { -+ com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent playerStartSpectatingEntityEvent = new com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent(this.getBukkitEntity(), camera.getBukkitEntity(), entityToSpectate.getBukkitEntity()); ++ com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent playerStartSpectatingEntityEvent = new com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent(this.getBukkitEntity(), camera.getBukkitEntity(), newCamera.getBukkitEntity()); + if (!playerStartSpectatingEntityEvent.callEvent()) { -+ this.camera = camera; // rollback camera entity again ++ this.camera = oldCamera; // rollback camera entity again + return; + } + } + // Paper end - Add PlayerStartSpectatingEntityEvent and PlayerStopSpectatingEntity - if (this.camera.level() instanceof ServerLevel serverLevel) { -- this.teleportTo(serverLevel, this.camera.getX(), this.camera.getY(), this.camera.getZ(), Set.of(), this.getYRot(), this.getXRot(), false); -+ this.teleportTo(serverLevel, this.camera.getX(), this.camera.getY(), this.camera.getZ(), Set.of(), this.getYRot(), this.getXRot(), false, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.SPECTATE); // CraftBukkit + if (this.camera.level() instanceof ServerLevel level) { +- this.teleportTo(level, this.camera.getX(), this.camera.getY(), this.camera.getZ(), Set.of(), this.getYRot(), this.getXRot(), false); ++ this.teleportTo(level, this.camera.getX(), this.camera.getY(), this.camera.getZ(), Set.of(), this.getYRot(), this.getXRot(), false, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.SPECTATE); // CraftBukkit } - if (entityToSpectate != null) { -@@ -1917,11 +_,11 @@ + if (newCamera != null) { +@@ -1962,11 +_,11 @@ } public @Nullable Component getTabListDisplayName() { @@ -1419,21 +1420,21 @@ } @Override -@@ -1951,11 +_,60 @@ +@@ -1996,11 +_,60 @@ } - public void setRespawnPosition(ServerPlayer.@Nullable RespawnConfig respawnConfig, boolean displayInChat) { -- if (displayInChat && respawnConfig != null && !respawnConfig.isSamePosition(this.respawnConfig)) { + public void setRespawnPosition(final ServerPlayer.@Nullable RespawnConfig respawnConfig, final boolean showMessage) { +- if (showMessage && respawnConfig != null && !respawnConfig.isSamePosition(this.respawnConfig)) { - this.sendSystemMessage(SPAWN_SET_MESSAGE); + // Paper start - Add PlayerSetSpawnEvent -+ this.setRespawnPosition(respawnConfig, displayInChat, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.UNKNOWN); ++ this.setRespawnPosition(respawnConfig, showMessage, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.UNKNOWN); + } + -+ public boolean setRespawnPosition(ServerPlayer.@Nullable RespawnConfig respawnConfig, boolean displayInChat, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause cause) { ++ public boolean setRespawnPosition(ServerPlayer.@Nullable RespawnConfig respawnConfig, final boolean showMessage, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause cause) { + org.bukkit.Location spawnLoc = null; + boolean actuallyDisplayInChat = false; + if (respawnConfig != null) { -+ actuallyDisplayInChat = displayInChat && !respawnConfig.isSamePosition(this.respawnConfig); ++ actuallyDisplayInChat = showMessage && !respawnConfig.isSamePosition(this.respawnConfig); + spawnLoc = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(respawnConfig.respawnData().pos(), this.server.getLevel(respawnConfig.respawnData().dimension())); + spawnLoc.setYaw(respawnConfig.respawnData().yaw()); + spawnLoc.setPitch(respawnConfig.respawnData().pitch()); @@ -1465,7 +1466,7 @@ + respawnConfig = new ServerPlayer.RespawnConfig( + net.minecraft.world.level.storage.LevelData.RespawnData.of( + ((org.bukkit.craftbukkit.CraftWorld) event.getLocation().getWorld()).getHandle().dimension(), -+ org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(event.getLocation()), ++ org.bukkit.craftbukkit.util.CraftLocation.toBlockPos(event.getLocation()), + event.getLocation().getYaw(), + event.getLocation().getPitch() + ), @@ -1482,90 +1483,90 @@ } public SectionPos getLastSectionPos() { -@@ -1975,16 +_,23 @@ +@@ -2020,16 +_,23 @@ } @Override -- public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean traceItem) { -- ItemEntity itemEntity = super.drop(droppedItem, dropAround, traceItem); -+ public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean traceItem, boolean callEvent, java.util.function.@Nullable Consumer entityOperation) { // Paper - Extend dropItem API -+ ItemEntity itemEntity = super.drop(droppedItem, dropAround, traceItem, callEvent, entityOperation); // Paper - Extend dropItem API -+ ItemStack itemStack = itemEntity != null ? itemEntity.getItem() : ItemStack.EMPTY; // Paper - move up - if (traceItem) { -- ItemStack itemStack = itemEntity != null ? itemEntity.getItem() : ItemStack.EMPTY; - if (!itemStack.isEmpty()) { -- this.awardStat(Stats.ITEM_DROPPED.get(itemStack.getItem()), droppedItem.getCount()); -+ this.awardStat(Stats.ITEM_DROPPED.get(itemStack.getItem()), itemStack.getCount()); // Paper - use size from dropped item +- public ItemEntity drop(final ItemStack itemStack, final boolean randomly, final boolean thrownFromHand) { +- ItemEntity entity = super.drop(itemStack, randomly, thrownFromHand); ++ public ItemEntity drop(final ItemStack itemStack, final boolean randomly, final boolean thrownFromHand, final boolean callEvent, final java.util.function.@Nullable Consumer entityOperation) { // Paper - Extend dropItem API ++ ItemEntity entity = super.drop(itemStack, randomly, thrownFromHand, callEvent, entityOperation); // Paper - Extend dropItem API ++ ItemStack droppedItemStack = entity != null ? entity.getItem() : ItemStack.EMPTY; // Paper - move up + if (thrownFromHand) { +- ItemStack droppedItemStack = entity != null ? entity.getItem() : ItemStack.EMPTY; + if (!droppedItemStack.isEmpty()) { +- this.awardStat(Stats.ITEM_DROPPED.get(droppedItemStack.getItem()), itemStack.getCount()); ++ this.awardStat(Stats.ITEM_DROPPED.get(droppedItemStack.getItem()), droppedItemStack.getCount()); // Paper - use size from dropped item this.awardStat(Stats.DROP); } } - + // Paper start - remove player from map on drop -+ if (itemStack.is(net.minecraft.world.item.Items.FILLED_MAP)) { -+ final MapItemSavedData mapData = MapItem.getSavedData(itemStack, this.level()); ++ if (droppedItemStack.is(net.minecraft.world.item.Items.FILLED_MAP)) { ++ final MapItemSavedData mapData = MapItem.getSavedData(droppedItemStack, this.level()); + if (mapData != null) { -+ mapData.tickCarriedBy(this, itemStack); ++ mapData.tickCarriedBy(this, droppedItemStack, null); + } + } + // Paper end - remove player from map on drop - return itemEntity; + return entity; } -@@ -2036,7 +_,7 @@ - super.updateUsingItem(usingItem); +@@ -2081,7 +_,7 @@ + super.updateUsingItem(useItem); } -- public void drop(boolean dropStack) { -+ public boolean drop(boolean dropStack) { // Paper - add back success return +- public void drop(final boolean all) { ++ public boolean drop(final boolean all) { // Paper - add back success return Inventory inventory = this.getInventory(); - ItemStack itemStack = inventory.removeFromSelected(dropStack); + ItemStack removed = inventory.removeFromSelected(all); this.containerMenu -@@ -2046,7 +_,7 @@ +@@ -2091,7 +_,7 @@ this.stopUsingItem(); } -- this.drop(itemStack, false, true); -+ return this.drop(itemStack, false, true) != null; // Paper - add back success return +- this.drop(removed, false, true); ++ return this.drop(removed, false, true) != null; // Paper - add back success return } @Override -@@ -2109,9 +_,9 @@ +@@ -2154,9 +_,9 @@ } @Override - public void removeVehicle() { + public void removeVehicle(final boolean suppressCancellation) { // Paper - Force entity dismount during teleportation - Entity vehicle = this.getVehicle(); + Entity oldVehicle = this.getVehicle(); - super.removeVehicle(); + super.removeVehicle(suppressCancellation); // Paper - Force entity dismount during teleportation - if (vehicle instanceof LivingEntity livingEntity) { - for (MobEffectInstance mobEffectInstance : livingEntity.getActiveEffects()) { - this.connection.send(new ClientboundRemoveMobEffectPacket(vehicle.getId(), mobEffectInstance.getEffect())); -@@ -2233,7 +_,7 @@ + if (oldVehicle instanceof LivingEntity livingEntity) { + for (MobEffectInstance effect : livingEntity.getActiveEffects()) { + this.connection.send(new ClientboundRemoveMobEffectPacket(oldVehicle.getId(), effect.getEffect())); +@@ -2278,7 +_,7 @@ } - public static long placeEnderPearlTicket(ServerLevel level, ChunkPos pos) { -- level.getChunkSource().addTicketWithRadius(TicketType.ENDER_PEARL, pos, 2); -+ if (!level.paperConfig().misc.legacyEnderPearlBehavior) level.getChunkSource().addTicketWithRadius(TicketType.ENDER_PEARL, pos, 2); // Paper - Allow using old ender pearl behavior + public static long placeEnderPearlTicket(final ServerLevel level, final ChunkPos chunk) { +- level.getChunkSource().addTicketWithRadius(TicketType.ENDER_PEARL, chunk, 2); ++ if (!level.paperConfig().misc.legacyEnderPearlBehavior) level.getChunkSource().addTicketWithRadius(TicketType.ENDER_PEARL, chunk, 2); // Paper - Allow using old ender pearl behavior return TicketType.ENDER_PEARL.timeout(); } -@@ -2263,9 +_,11 @@ +@@ -2308,9 +_,11 @@ } } - public record RespawnPosAngle(Vec3 position, float yaw, float pitch) { -- public static ServerPlayer.RespawnPosAngle of(Vec3 position, BlockPos towardsPos, float pitch) { -- return new ServerPlayer.RespawnPosAngle(position, calculateLookAtYaw(position, towardsPos), pitch); +- public static ServerPlayer.RespawnPosAngle of(final Vec3 position, final BlockPos lookAtBlockPos, final float pitch) { +- return new ServerPlayer.RespawnPosAngle(position, calculateLookAtYaw(position, lookAtBlockPos), pitch); + // CraftBukkit start + public record RespawnPosAngle(Vec3 position, float yaw, float pitch, boolean isBedSpawn, boolean isAnchorSpawn, @Nullable Runnable consumeAnchorCharge) { -+ public static ServerPlayer.RespawnPosAngle of(Vec3 position, BlockPos towardsPos, float pitch, boolean isBedSpawn, boolean isAnchorSpawn, @Nullable Runnable consumeAnchorCharge) { -+ return new ServerPlayer.RespawnPosAngle(position, calculateLookAtYaw(position, towardsPos), pitch, isBedSpawn, isAnchorSpawn, consumeAnchorCharge); ++ public static ServerPlayer.RespawnPosAngle of(final Vec3 position, final BlockPos lookAtBlockPos, final float pitch, final boolean isBedSpawn, final boolean isAnchorSpawn, final @Nullable Runnable consumeAnchorCharge) { ++ return new ServerPlayer.RespawnPosAngle(position, calculateLookAtYaw(position, lookAtBlockPos), pitch, isBedSpawn, isAnchorSpawn, consumeAnchorCharge); + // CraftBukkit end } - private static float calculateLookAtYaw(Vec3 position, BlockPos towardsPos) { -@@ -2285,4 +_,135 @@ + private static float calculateLookAtYaw(final Vec3 position, final BlockPos lookAtBlockPos) { +@@ -2330,4 +_,135 @@ ); public static final ServerPlayer.SavedPosition EMPTY = new ServerPlayer.SavedPosition(Optional.empty(), Optional.empty(), Optional.empty()); } @@ -1577,10 +1578,10 @@ + public long getPlayerTime() { + if (this.relativeTime) { + // Adds timeOffset to the current server time. -+ return this.level().getDayTime() + this.timeOffset; ++ return this.level().getDefaultClockTime() + this.timeOffset; + } else { + // Adds timeOffset to the beginning of this day. -+ return this.level().getDayTime() - (this.level().getDayTime() % 24000) + this.timeOffset; ++ return this.level().getDefaultClockTime() - (this.level().getDefaultClockTime() % net.minecraft.SharedConstants.TICKS_PER_GAME_DAY) + this.timeOffset; + } + } + @@ -1642,7 +1643,7 @@ + + public void resetPlayerWeather() { + this.weatherType = null; -+ this.setPlayerWeather(this.level().getLevelData().isRaining() ? org.bukkit.WeatherType.DOWNFALL : org.bukkit.WeatherType.CLEAR, false); ++ this.setPlayerWeather(this.level().isRaining() ? org.bukkit.WeatherType.DOWNFALL : org.bukkit.WeatherType.CLEAR, false); + } + + @Override diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch index 700dee35c478..e05a0ec589bb 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch @@ -7,19 +7,19 @@ + public boolean captureSentBlockEntities = false; // Paper - Send block entities after destroy prediction + public boolean capturedBlockEntity = false; // Paper - Send block entities after destroy prediction - public ServerPlayerGameMode(ServerPlayer player) { + public ServerPlayerGameMode(final ServerPlayer player) { this.player = player; this.level = player.level(); } + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - public boolean changeGameModeForPlayer(GameType gameModeForPlayer) { + public boolean changeGameModeForPlayer(final GameType gameModeForPlayer) { + // Paper start - Expand PlayerGameModeChangeEvent + org.bukkit.event.player.PlayerGameModeChangeEvent event = this.changeGameModeForPlayer(gameModeForPlayer, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.UNKNOWN, null); + return event != null && !event.isCancelled(); + } + -+ public org.bukkit.event.player.@Nullable PlayerGameModeChangeEvent changeGameModeForPlayer(GameType gameModeForPlayer, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause playerGameModeChangeCause, net.kyori.adventure.text.@Nullable Component cancelMessage) { ++ public org.bukkit.event.player.@Nullable PlayerGameModeChangeEvent changeGameModeForPlayer(final GameType gameModeForPlayer, final org.bukkit.event.player.PlayerGameModeChangeEvent.Cause playerGameModeChangeCause, final net.kyori.adventure.text.@Nullable Component cancelMessage) { + // Paper end - Expand PlayerGameModeChangeEvent if (gameModeForPlayer == this.gameModeForPlayer) { - return false; @@ -69,7 +69,7 @@ + if (blockState == null || blockState.isAir()) { // Paper - Don't allow digging into unloaded chunks this.hasDelayedDestroy = false; } else { - float f = this.incrementDestroyProgress(blockState, this.delayedDestroyPos, this.delayedTickStart); + float destroyProgress = this.incrementDestroyProgress(blockState, this.delayedDestroyPos, this.delayedTickStart); @@ -118,7 +_,13 @@ } } @@ -85,20 +85,20 @@ if (blockState.isAir()) { this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1); this.lastSentState = -1; -@@ -149,6 +_,7 @@ - - public void handleBlockBreakAction(BlockPos pos, ServerboundPlayerActionPacket.Action action, Direction face, int maxBuildHeight, int sequence) { +@@ -151,6 +_,7 @@ + final BlockPos pos, final ServerboundPlayerActionPacket.Action action, final Direction direction, final int maxY, final int sequence + ) { if (!this.player.isWithinBlockInteractionRange(pos, 1.0)) { + if (true) return; // Paper - Don't allow digging into unloaded chunks; Don't notify if unreasonably far away this.debugLogging(pos, false, sequence, "too far"); - } else if (pos.getY() > maxBuildHeight) { + } else if (pos.getY() > maxY) { this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos))); -@@ -156,16 +_,35 @@ - } else { - if (action == ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK) { +@@ -164,16 +_,35 @@ + } + if (!this.level.mayInteract(this.player, pos)) { + // CraftBukkit start - fire PlayerInteractEvent -+ org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(this.player, org.bukkit.event.block.Action.LEFT_CLICK_BLOCK, pos, face, this.player.getInventory().getSelectedItem(), InteractionHand.MAIN_HAND); ++ org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(this.player, org.bukkit.event.block.Action.LEFT_CLICK_BLOCK, pos, direction, this.player.getInventory().getSelectedItem(), InteractionHand.MAIN_HAND); this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos))); this.debugLogging(pos, false, sequence, "may not interact"); - return; @@ -109,7 +109,7 @@ + } + + // CraftBukkit start -+ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(this.player, org.bukkit.event.block.Action.LEFT_CLICK_BLOCK, pos, face, this.player.getInventory().getSelectedItem(), InteractionHand.MAIN_HAND); ++ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(this.player, org.bukkit.event.block.Action.LEFT_CLICK_BLOCK, pos, direction, this.player.getInventory().getSelectedItem(), InteractionHand.MAIN_HAND); + if (event.isCancelled()) { + this.capturedBlockEntity = true; // Paper - Send block entities after destroy prediction + return; @@ -131,17 +131,17 @@ if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) { this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos))); this.debugLogging(pos, false, sequence, "block action restricted"); -@@ -175,7 +_,7 @@ +@@ -183,7 +_,7 @@ this.destroyProgressStart = this.gameTicks; - float f = 1.0F; + float progress = 1.0F; BlockState blockState = this.level.getBlockState(pos); - if (!blockState.isAir()) { + if (event.useInteractedBlock() != org.bukkit.event.Event.Result.DENY && !blockState.isAir()) { // Paper EnchantmentHelper.onHitBlock( this.level, this.player.getMainHandItem(), -@@ -190,6 +_,23 @@ - f = blockState.getDestroyProgress(this.player, this.player.level(), pos); +@@ -198,6 +_,23 @@ + progress = blockState.getDestroyProgress(this.player, this.player.level(), pos); } + // CraftBukkit start @@ -150,21 +150,21 @@ + return; + } + -+ org.bukkit.event.block.BlockDamageEvent blockEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDamageEvent(this.player, pos, face, this.player.getInventory().getSelectedItem(), f >= 1.0f); // Paper - Add BlockFace to BlockDamageEvent ++ org.bukkit.event.block.BlockDamageEvent blockEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDamageEvent(this.player, pos, direction, this.player.getInventory().getSelectedItem(), progress >= 1.0f); // Paper - Add BlockFace to BlockDamageEvent + + if (blockEvent.isCancelled()) { + return; + } + + if (blockEvent.getInstaBreak()) { -+ f = 2.0f; ++ progress = 2.0f; + } + // CraftBukkit end + - if (!blockState.isAir() && f >= 1.0F) { + if (!blockState.isAir() && progress >= 1.0F) { this.destroyAndAck(pos, sequence, "insta mine"); } else { -@@ -230,14 +_,22 @@ +@@ -238,14 +_,22 @@ this.debugLogging(pos, true, sequence, "stopped destroying"); } else if (action == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) { this.isDestroyingBlock = false; @@ -173,8 +173,8 @@ + // Paper start - Don't allow digging into unloaded chunks + if (!Objects.equals(this.destroyPos, pos) && !BlockPos.ZERO.equals(this.destroyPos)) { // Paper + ServerPlayerGameMode.LOGGER.debug("Mismatch in destroy block pos: {} {}", this.destroyPos, pos); // CraftBukkit - SPIGOT-5457 sent by client when interact event cancelled -+ BlockState type = this.level.getBlockStateIfLoaded(this.destroyPos); // Don't load unloaded chunks for stale records here -+ if (type != null) { ++ BlockState destroyState = this.level.getBlockStateIfLoaded(this.destroyPos); // Don't load unloaded chunks for stale records here ++ if (destroyState != null) { this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1); this.debugLogging(pos, true, sequence, "aborted mismatched destroying"); + } @@ -189,17 +189,17 @@ } } } -@@ -253,17 +_,64 @@ +@@ -261,17 +_,62 @@ - public boolean destroyBlock(BlockPos pos) { - BlockState blockState = this.level.getBlockState(pos); -- if (!this.player.getMainHandItem().canDestroyBlock(blockState, this.level, pos, this.player)) { + public boolean destroyBlock(final BlockPos pos) { + BlockState state = this.level.getBlockState(pos); +- if (!this.player.getMainHandItem().canDestroyBlock(state, this.level, pos, this.player)) { + // CraftBukkit start - fire BlockBreakEvent + org.bukkit.block.Block bblock = org.bukkit.craftbukkit.block.CraftBlock.at(this.level, pos); + org.bukkit.event.block.BlockBreakEvent event = null; + if (this.player instanceof ServerPlayer) { + // Sword + Creative mode pre-cancel -+ boolean canAttackBlock = !this.player.getMainHandItem().canDestroyBlock(blockState, this.level, pos, this.player); ++ boolean canAttackBlock = !this.player.getMainHandItem().canDestroyBlock(state, this.level, pos, this.player); + event = new org.bukkit.event.block.BlockBreakEvent(bblock, this.player.getBukkitEntity()); + + // Sword + Creative mode pre-cancel @@ -214,9 +214,7 @@ + event.setExpToDrop(block.getExpDrop(updatedBlockState, this.level, pos, itemInHand, true)); + } + -+ this.level.getCraftServer().getPluginManager().callEvent(event); -+ -+ if (event.isCancelled()) { ++ if (!event.callEvent()) { + if (canAttackBlock) { + return false; + } @@ -235,29 +233,29 @@ + } + // CraftBukkit end + -+ if (false && !this.player.getMainHandItem().canDestroyBlock(blockState, this.level, pos, this.player)) { // CraftBukkit - false ++ if (false && !this.player.getMainHandItem().canDestroyBlock(state, this.level, pos, this.player)) { // CraftBukkit - false return false; } else { -+ blockState = this.level.getBlockState(pos); // CraftBukkit - update state from plugins -+ if (blockState.isAir()) return false; // CraftBukkit - A plugin set block to air without cancelling ++ state = this.level.getBlockState(pos); // CraftBukkit - update state from plugins ++ if (state.isAir()) return false; // CraftBukkit - A plugin set block to air without cancelling BlockEntity blockEntity = this.level.getBlockEntity(pos); - Block block = blockState.getBlock(); + Block block = state.getBlock(); - if (block instanceof GameMasterBlock && !this.player.canUseGameMasterBlocks()) { + if (block instanceof GameMasterBlock && !this.player.canUseGameMasterBlocks() && !(block instanceof net.minecraft.world.level.block.CommandBlock && (this.player.isCreative() && this.player.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission - this.level.sendBlockUpdated(pos, blockState, blockState, Block.UPDATE_ALL); + this.level.sendBlockUpdated(pos, state, state, Block.UPDATE_ALL); return false; } else if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) { return false; } else { + // CraftBukkit start -+ org.bukkit.block.BlockState state = bblock.getState(); ++ org.bukkit.block.BlockState bState = bblock.getState(); + this.level.captureDrops = new java.util.ArrayList<>(); + // CraftBukkit end - BlockState blockState1 = block.playerWillDestroy(this.level, pos, blockState, this.player); - boolean flag = this.level.removeBlock(pos, false); + BlockState adjustedState = block.playerWillDestroy(this.level, pos, state, this.player); + boolean changed = this.level.removeBlock(pos, false); if (SharedConstants.DEBUG_BLOCK_BREAK) { -@@ -274,19 +_,44 @@ - block.destroy(this.level, pos, blockState1); +@@ -282,19 +_,44 @@ + block.destroy(this.level, pos, adjustedState); } + ItemStack mainHandStack = null; // Paper - Trigger bee_nest_destroyed trigger in the correct place @@ -266,20 +264,20 @@ - return true; + // return true; // CraftBukkit } else { - ItemStack mainHandItem = this.player.getMainHandItem(); - ItemStack itemStack = mainHandItem.copy(); - boolean hasCorrectToolForDrops = this.player.hasCorrectToolForDrops(blockState1); -+ mainHandStack = itemStack; // Paper - Trigger bee_nest_destroyed trigger in the correct place -+ isCorrectTool = hasCorrectToolForDrops; // Paper - Trigger bee_nest_destroyed trigger in the correct place - mainHandItem.mineBlock(this.level, blockState1, pos, this.player); -- if (flag && hasCorrectToolForDrops) { -- block.playerDestroy(this.level, this.player, pos, blockState1, blockEntity, itemStack); + ItemStack itemStack = this.player.getMainHandItem(); + ItemStack destroyedWith = itemStack.copy(); + boolean canDestroy = this.player.hasCorrectToolForDrops(adjustedState); ++ mainHandStack = destroyedWith; // Paper - Trigger bee_nest_destroyed trigger in the correct place ++ isCorrectTool = canDestroy; // Paper - Trigger bee_nest_destroyed trigger in the correct place + itemStack.mineBlock(this.level, adjustedState, pos, this.player); +- if (changed && canDestroy) { +- block.playerDestroy(this.level, this.player, pos, adjustedState, blockEntity, destroyedWith); - } - - return true; - } -+ if (flag && hasCorrectToolForDrops) { // CraftBukkit - Check if block should drop items // Paper - fix drops not preventing stats/food exhaustion -+ block.playerDestroy(this.level, this.player, pos, blockState1, blockEntity, itemStack, event.isDropItems(), false); // Paper - fix drops not preventing stats/food exhaustion ++ if (changed && canDestroy) { // CraftBukkit - Check if block should drop items // Paper - fix drops not preventing stats/food exhaustion ++ block.playerDestroy(this.level, this.player, pos, adjustedState, blockEntity, destroyedWith, event.isDropItems(), false); // Paper - fix drops not preventing stats/food exhaustion + } + + // return true; // CraftBukkit @@ -288,17 +286,17 @@ + java.util.List itemsToDrop = this.level.captureDrops; // Paper - capture all item additions to the world + this.level.captureDrops = null; // Paper - capture all item additions to the world; Remove this earlier so that we can actually drop stuff + if (event.isDropItems()) { -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, state, this.player, itemsToDrop); // Paper - capture all item additions to the world ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, bState, this.player, itemsToDrop); // Paper - capture all item additions to the world + } + + // Drop event experience -+ if (flag) { -+ blockState.getBlock().popExperience(this.level, pos, event.getExpToDrop(), this.player); // Paper ++ if (changed) { ++ state.getBlock().popExperience(this.level, pos, event.getExpToDrop(), this.player); // Paper + } + // Paper start - Trigger bee_nest_destroyed trigger in the correct place (check impls of block#playerDestroy) + if (mainHandStack != null) { -+ if (flag && isCorrectTool && event.isDropItems() && block instanceof net.minecraft.world.level.block.BeehiveBlock && blockEntity instanceof net.minecraft.world.level.block.entity.BeehiveBlockEntity beehiveBlockEntity) { // simulates the guard on block#playerDestroy above -+ CriteriaTriggers.BEE_NEST_DESTROYED.trigger(player, blockState, mainHandStack, beehiveBlockEntity.getOccupantCount()); ++ if (changed && isCorrectTool && event.isDropItems() && block instanceof net.minecraft.world.level.block.BeehiveBlock && blockEntity instanceof net.minecraft.world.level.block.entity.BeehiveBlockEntity beehiveBlockEntity) { // simulates the guard on block#playerDestroy above ++ CriteriaTriggers.BEE_NEST_DESTROYED.trigger(player, state, mainHandStack, beehiveBlockEntity.getOccupantCount()); + } + } + // Paper end - Trigger bee_nest_destroyed trigger in the correct place @@ -308,15 +306,15 @@ } } } -@@ -299,6 +_,7 @@ +@@ -307,6 +_,7 @@ } else { - int count = stack.getCount(); - int damageValue = stack.getDamageValue(); -+ final ItemStack stackBeforeUse = stack.copy(); // Paper - Store stack before use for interact prediction check - InteractionResult interactionResult = stack.use(level, player, hand); - ItemStack itemStack; - if (interactionResult instanceof InteractionResult.Success success) { -@@ -321,7 +_,14 @@ + int oldCount = itemStack.getCount(); + int oldDamage = itemStack.getDamageValue(); ++ final ItemStack stackBeforeUse = itemStack.copy(); // Paper - Store stack before use for interact prediction check + InteractionResult result = itemStack.use(level, player, hand); + ItemStack resultStack; + if (result instanceof InteractionResult.Success success) { +@@ -332,7 +_,14 @@ } if (!player.isUsingItem()) { @@ -331,8 +329,8 @@ + // Paper end - Optimize sendAllDataToRemote calls } - return interactionResult; -@@ -329,15 +_,53 @@ + return result; +@@ -340,17 +_,55 @@ } } @@ -342,39 +340,41 @@ + public BlockPos interactPosition; + public InteractionHand interactHand; + public ItemStack interactItemStack; - public InteractionResult useItemOn(ServerPlayer player, Level level, ItemStack stack, InteractionHand hand, BlockHitResult hitResult) { - BlockPos blockPos = hitResult.getBlockPos(); - BlockState blockState = level.getBlockState(blockPos); + public InteractionResult useItemOn( + final ServerPlayer player, final Level level, final ItemStack itemStack, final InteractionHand hand, final BlockHitResult hitResult + ) { + BlockPos pos = hitResult.getBlockPos(); + BlockState state = level.getBlockState(pos); + boolean cancelledBlock = false; + boolean cancelledItem = false; // Paper - correctly handle items on cooldown - if (!blockState.getBlock().isEnabled(level.enabledFeatures())) { + if (!state.getBlock().isEnabled(level.enabledFeatures())) { return InteractionResult.FAIL; } else if (this.gameModeForPlayer == GameType.SPECTATOR) { - MenuProvider menuProvider = blockState.getMenuProvider(level, blockPos); + MenuProvider menuProvider = state.getMenuProvider(level, pos); - if (menuProvider != null) { - player.openMenu(menuProvider); -+ cancelledBlock = !(menuProvider instanceof MenuProvider); ++ cancelledBlock = menuProvider == null; + } + -+ if (player.getCooldowns().isOnCooldown(stack)) { ++ if (player.getCooldowns().isOnCooldown(itemStack)) { + cancelledItem = true; // Paper - correctly handle items on cooldown + } -+ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(player, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, blockPos, hitResult.getDirection(), stack, cancelledBlock, cancelledItem, hand, hitResult.getLocation()); // Paper - correctly handle items on cooldown ++ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(player, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, pos, hitResult.getDirection(), itemStack, cancelledBlock, cancelledItem, hand, hitResult.getLocation()); // Paper - correctly handle items on cooldown + this.firedInteract = true; + this.interactResult = event.useItemInHand() == org.bukkit.event.Event.Result.DENY; -+ this.interactPosition = blockPos.immutable(); ++ this.interactPosition = pos.immutable(); + this.interactHand = hand; -+ this.interactItemStack = stack.copy(); ++ this.interactItemStack = itemStack.copy(); + + if (event.useInteractedBlock() == org.bukkit.event.Event.Result.DENY) { + // Block acks will take care of most of it, just handle some special cases here -+ if (blockState.getBlock() instanceof net.minecraft.world.level.block.CakeBlock) { ++ if (state.getBlock() instanceof net.minecraft.world.level.block.CakeBlock) { + player.getBukkitEntity().sendHealthUpdate(); // SPIGOT-1341 - reset health for cake -+ } else if (blockState.is(net.minecraft.world.level.block.Blocks.JIGSAW) || blockState.is(net.minecraft.world.level.block.Blocks.STRUCTURE_BLOCK) || blockState.getBlock() instanceof net.minecraft.world.level.block.CommandBlock) { ++ } else if (state.is(net.minecraft.world.level.block.Blocks.JIGSAW) || state.is(net.minecraft.world.level.block.Blocks.STRUCTURE_BLOCK) || state.getBlock() instanceof net.minecraft.world.level.block.CommandBlock) { + player.connection.send(new net.minecraft.network.protocol.game.ClientboundContainerClosePacket(this.player.containerMenu.containerId)); + } + // Paper start - Fix inventory desync; SPIGOT-2867 -+ if (io.papermc.paper.util.MCUtil.clientPredictsInteraction(this.player, blockState, stack)) { ++ if (io.papermc.paper.util.MCUtil.clientPredictsInteraction(this.player, state, itemStack)) { + this.player.containerMenu.sendAllDataToRemote(); + } else { + this.player.containerMenu.forceHeldSlot(hand); @@ -383,23 +383,23 @@ + this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items + return (event.useItemInHand() != org.bukkit.event.Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS; + } else if (this.gameModeForPlayer == GameType.SPECTATOR) { -+ MenuProvider menuProvider = blockState.getMenuProvider(level, blockPos); ++ MenuProvider menuProvider = state.getMenuProvider(level, pos); + if (menuProvider != null && player.openMenu(menuProvider).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation return InteractionResult.CONSUME; } else { return InteractionResult.PASS; -@@ -362,7 +_,7 @@ +@@ -375,7 +_,7 @@ } } -- if (!stack.isEmpty() && !player.getCooldowns().isOnCooldown(stack)) { -+ if (!stack.isEmpty() && !this.interactResult) { // add !interactResult SPIGOT-764 - UseOnContext useOnContext = new UseOnContext(player, hand, hitResult); - InteractionResult interactionResult1; +- if (!itemStack.isEmpty() && !player.getCooldowns().isOnCooldown(itemStack)) { ++ if (!itemStack.isEmpty() && !this.interactResult) { // Spigot - add !this.interactResult SPIGOT-764 + UseOnContext context = new UseOnContext(player, hand, hitResult); + InteractionResult success; if (player.hasInfiniteMaterials()) { -@@ -379,6 +_,11 @@ +@@ -392,6 +_,11 @@ - return interactionResult1; + return success; } else { + // Paper start - Properly cancel usable items; Cancel only if cancelled + if the interact result is different from default response + if (this.interactResult && this.interactResult != cancelledItem) { diff --git a/paper-server/patches/sources/net/minecraft/server/level/Ticket.java.patch b/paper-server/patches/sources/net/minecraft/server/level/Ticket.java.patch index 8e7b25aff45b..33990c44b165 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/Ticket.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/Ticket.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/server/level/Ticket.java @@ -14,17 +_,36 @@ ExtraCodecs.NON_NEGATIVE_INT.fieldOf("level").forGetter(Ticket::getTicketLevel), - Codec.LONG.optionalFieldOf("ticks_left", 0L).forGetter(ticket -> ticket.ticksLeft) + Codec.LONG.optionalFieldOf("ticks_left", 0L).forGetter(t -> t.ticksLeft) ) -- .apply(instance, Ticket::new) -+ .apply(instance, (type, level, ticks) -> new Ticket(type, level.intValue(), ticks.longValue())) // Paper - add identifier +- .apply(i, Ticket::new) ++ .apply(i, (type, level, ticks) -> new Ticket(type, level.intValue(), ticks.longValue())) // Paper - add identifier ); private final TicketType type; private final int ticketLevel; @@ -19,7 +19,7 @@ + // Paper end - add identifier + - public Ticket(TicketType type, int ticketLevel) { + public Ticket(final TicketType type, final int ticketLevel) { - this(type, ticketLevel, type.timeout()); + // Paper start - add identifier + this(type, ticketLevel, null); @@ -29,7 +29,7 @@ + // Paper end - add identifier } - private Ticket(TicketType type, int ticketLevel, long ticksLeft) { + private Ticket(final TicketType type, final int ticketLevel, final long ticksLeft) { + // Paper start - add identifier + this(type, ticketLevel, ticksLeft, null); + } diff --git a/paper-server/patches/sources/net/minecraft/server/level/TicketType.java.patch b/paper-server/patches/sources/net/minecraft/server/level/TicketType.java.patch index 01cb41ec9b18..418c8ded591f 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/TicketType.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/TicketType.java.patch @@ -29,9 +29,9 @@ + public static final TicketType FUTURE_AWAIT = register("future_await", NO_TIMEOUT, FLAG_LOADING | FLAG_SIMULATION); // Paper + public static final TicketType CHUNK_LOAD = register("chunk_load", NO_TIMEOUT, FLAG_LOADING); // Paper - moonrise - private static TicketType register(String name, long timeout, @TicketType.Flags int flags) { + private static TicketType register(final String name, final long timeout, @TicketType.Flags final int flags) { return Registry.register(BuiltInRegistries.TICKET_TYPE, name, new TicketType(timeout, flags)); -@@ -48,12 +_,20 @@ +@@ -48,12 +_,21 @@ return (this.flags & FLAG_CAN_EXPIRE_IF_UNLOADED) != 0; } @@ -41,10 +41,11 @@ + public long timeout() { + return this == PLUGIN ? PLUGIN_TYPE_TIMEOUT : this.timeout; + } ++ // Paper end - chunk-gc config ++ public boolean hasTimeout() { -- return this.timeout != 0L; -+ return this.timeout() != 0L; -+ // Paper end - chunk-gc config +- return this.timeout != NO_TIMEOUT; ++ return this.timeout() != NO_TIMEOUT; // Paper - chunk-gc config } @Retention(RetentionPolicy.CLASS) diff --git a/paper-server/patches/sources/net/minecraft/server/level/WorldGenRegion.java.patch b/paper-server/patches/sources/net/minecraft/server/level/WorldGenRegion.java.patch index bf4502922504..97ed6a25d732 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/WorldGenRegion.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/WorldGenRegion.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/server/level/WorldGenRegion.java +++ b/net/minecraft/server/level/WorldGenRegion.java -@@ -150,6 +_,28 @@ - return chessboardDistance < this.generatingStep.directDependencies().size(); +@@ -146,6 +_,28 @@ + return distance < this.generatingStep.directDependencies().size(); } + // Paper start - if loaded util @@ -27,27 +27,17 @@ + // Paper end + @Override - public BlockState getBlockState(BlockPos pos) { + public BlockState getBlockState(final BlockPos pos) { return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())).getBlockState(pos); -@@ -196,7 +_,8 @@ - if (blockState.isAir()) { - return false; - } else { -- if (dropBlock) { -+ if (dropBlock) LOGGER.warn("Potential async entity add during worldgen", new Throwable()); // Paper - Fix async entity add due to fungus trees; log when this happens -+ if (false) { // CraftBukkit - SPIGOT-6833: Do not drop during world generation - BlockEntity blockEntity = blockState.hasBlockEntity() ? this.getBlockEntity(pos) : null; - Block.dropResources(blockState, this.level, pos, blockEntity, entity, ItemStack.EMPTY); - } -@@ -239,6 +_,7 @@ +@@ -221,6 +_,7 @@ } } + private boolean hasSetFarWarned = false; // Paper - Buffer OOB setBlock calls @Override - public boolean ensureCanWrite(BlockPos pos) { - int sectionPosX = SectionPos.blockToSectionCoord(pos.getX()); -@@ -256,6 +_,8 @@ + public boolean ensureCanWrite(final BlockPos pos) { + int chunkX = SectionPos.blockToSectionCoord(pos.getX()); +@@ -238,6 +_,8 @@ return true; } else { @@ -55,8 +45,8 @@ + if (!hasSetFarWarned) { Util.logAndPauseIfInIde( "Detected setBlock in a far chunk [" - + sectionPosX -@@ -267,6 +_,12 @@ + + chunkX +@@ -249,6 +_,12 @@ + this.generatingStep.targetStatus() + (this.currentlyGenerating == null ? "" : ", currently generating: " + this.currentlyGenerating.get()) ); @@ -69,16 +59,7 @@ return false; } } -@@ -277,7 +_,7 @@ - return false; - } else { - ChunkAccess chunk = this.getChunk(pos); -- BlockState blockState = chunk.setBlockState(pos, state, flags); -+ BlockState blockState = chunk.setBlockState(pos, state, flags); final BlockState previousBlockState = blockState; // Paper - Clear block entity before setting up a DUMMY block entity - obfhelper - if (blockState != null) { - this.level.updatePOIOnBlockStateChange(pos, blockState, state); - } -@@ -291,6 +_,17 @@ +@@ -273,6 +_,17 @@ chunk.removeBlockEntity(pos); } } else { @@ -89,24 +70,24 @@ + // Otherwise a chest block entity generated by a structure template that is later "updated" to + // be waterlogged would remove its existing block entity (see PaperMC/Paper#10750) + // This logic is *also* found in LevelChunk#setBlockState. -+ if (previousBlockState != null && !java.util.Objects.equals(previousBlockState.getBlock(), state.getBlock())) { ++ if (oldState != null && !java.util.Objects.equals(oldState.getBlock(), blockState.getBlock())) { + chunk.removeBlockEntity(pos); + } + // Paper end - Clear block entity before setting up a DUMMY block entity - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putInt("x", pos.getX()); - compoundTag.putInt("y", pos.getY()); -@@ -316,6 +_,13 @@ + CompoundTag tag = new CompoundTag(); + tag.putInt("x", pos.getX()); + tag.putInt("y", pos.getY()); +@@ -301,6 +_,13 @@ @Override - public boolean addFreshEntity(Entity entity) { + public boolean addFreshEntity(final Entity entity) { + // CraftBukkit start + return this.addFreshEntity(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); + } + + @Override -+ public boolean addFreshEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { ++ public boolean addFreshEntity(final Entity entity, final org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { + // CraftBukkit end - int sectionPosX = SectionPos.blockToSectionCoord(entity.getBlockX()); - int sectionPosZ = SectionPos.blockToSectionCoord(entity.getBlockZ()); - this.getChunk(sectionPosX, sectionPosZ).addEntity(entity); + int xc = SectionPos.blockToSectionCoord(entity.getBlockX()); + int zc = SectionPos.blockToSectionCoord(entity.getBlockZ()); + this.getChunk(xc, zc).addEntity(entity); diff --git a/paper-server/patches/sources/net/minecraft/server/level/progress/LoggingLevelLoadListener.java.patch b/paper-server/patches/sources/net/minecraft/server/level/progress/LoggingLevelLoadListener.java.patch index f935b67dc439..ef2a4cdde677 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/progress/LoggingLevelLoadListener.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/progress/LoggingLevelLoadListener.java.patch @@ -4,10 +4,10 @@ private long startTime = Long.MAX_VALUE; private long nextLogTime = Long.MAX_VALUE; -- public LoggingLevelLoadListener(boolean includePlayerChunks) { +- public LoggingLevelLoadListener(final boolean includePlayerChunks) { + // Paper start - add level + private final net.minecraft.server.level.ServerLevel level; -+ public LoggingLevelLoadListener(boolean includePlayerChunks, net.minecraft.server.level.ServerLevel level) { ++ public LoggingLevelLoadListener(final boolean includePlayerChunks, net.minecraft.server.level.ServerLevel level) { + this.level = level; + // Paper end - add level this.includePlayerChunks = includePlayerChunks; @@ -31,14 +31,14 @@ case PREPARE_GLOBAL_SPAWN: + // Paper start - log dimension + if (this.level != null) { -+ LOGGER.info("Selecting spawn point for world '{}'...", this.level.dimension().identifier()); ++ LOGGER.info("Selecting spawn point for level '{}'...", this.level.dimension().identifier()); + } else { LOGGER.info("Selecting global world spawn..."); + } break; case LOAD_INITIAL_CHUNKS: + if (this.level != null) { -+ LOGGER.info("Loading {} persistent chunks for world '{}'...", totalChunks, this.level.dimension().identifier()); ++ LOGGER.info("Loading {} persistent chunks for level '{}'...", totalChunks, this.level.dimension().identifier()); + } else { LOGGER.info("Loading {} persistent chunks...", totalChunks); + } @@ -49,7 +49,7 @@ @@ -73,7 +_,7 @@ ? LevelLoadListener.Stage.LOAD_PLAYER_CHUNKS : LevelLoadListener.Stage.LOAD_INITIAL_CHUNKS; - if (stage == stage1) { + if (stage == finalStage) { - LOGGER.info("Time elapsed: {} ms", Util.getMillis() - this.startTime); + LOGGER.info("Prepared spawn area in {} ms", Util.getMillis() - this.startTime); // Paper this.nextLogTime = Long.MAX_VALUE; diff --git a/paper-server/patches/sources/net/minecraft/server/network/CommonListenerCookie.java.patch b/paper-server/patches/sources/net/minecraft/server/network/CommonListenerCookie.java.patch index fb7e69164775..52d65de72cab 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/CommonListenerCookie.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/CommonListenerCookie.java.patch @@ -5,8 +5,8 @@ import net.minecraft.server.level.ClientInformation; -public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred) { -+public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @javax.annotation.Nullable String brandName, java.util.Set channels) { // Paper - public static CommonListenerCookie createInitial(GameProfile gameProfile, boolean transferred) { ++public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jspecify.annotations.Nullable String brandName, java.util.Set channels) { // Paper + public static CommonListenerCookie createInitial(final GameProfile gameProfile, final boolean transferred) { - return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred); + return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new java.util.HashSet<>()); // Paper } diff --git a/paper-server/patches/sources/net/minecraft/server/network/EventLoopGroupHolder.java.patch b/paper-server/patches/sources/net/minecraft/server/network/EventLoopGroupHolder.java.patch index 84488a498354..e5a97072213b 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/EventLoopGroupHolder.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/EventLoopGroupHolder.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/network/EventLoopGroupHolder.java +++ b/net/minecraft/server/network/EventLoopGroupHolder.java -@@ -48,11 +_,20 @@ +@@ -48,19 +_,39 @@ return LocalIoHandler.newFactory(); } }; @@ -18,32 +18,35 @@ private volatile @Nullable EventLoopGroup group; + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - use variant with address param - public static EventLoopGroupHolder remote(boolean tryNativeTransport) { - if (tryNativeTransport) { + public static EventLoopGroupHolder remote(final boolean allowNativeTransport) { ++ // Paper start - Unix domain socket support ++ return remote(null, allowNativeTransport); ++ } ++ public static EventLoopGroupHolder remote(final java.net.@Nullable SocketAddress address, final boolean allowNativeTransport) { ++ // Paper end - Unix domain socket support + if (allowNativeTransport) { if (KQueue.isAvailable()) { -@@ -66,6 +_,25 @@ + return KQUEUE; + } - return NIO; - } -+ -+ // Paper start - Unix domain socket support -+ public static EventLoopGroupHolder remote(java.net.SocketAddress address, boolean tryNativeTransport) { -+ if (tryNativeTransport) { -+ if (KQueue.isAvailable()) { -+ return KQUEUE; -+ } -+ -+ if (Epoll.isAvailable()) { + if (Epoll.isAvailable()) { +- return EPOLL; ++ // Paper start - Unix domain socket support + if (address instanceof io.netty.channel.unix.DomainSocketAddress) { + return EPOLL_UNIX_DOMAIN; + } else { + return EPOLL; + } -+ } -+ } -+ return NIO; -+ } -+ // Paper end - Unix domain socket support ++ // Paper end - Unix domain socket support + } + } + +@@ -78,7 +_,7 @@ + } + + private ThreadFactory createThreadFactory() { +- return new ThreadFactoryBuilder().setNameFormat("Netty " + this.type + " IO #%d").setDaemon(true).build(); ++ return new ThreadFactoryBuilder().setNameFormat("Netty " + this.type + " IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build(); // Paper + } - public static EventLoopGroupHolder local() { - return LOCAL; + protected abstract IoHandlerFactory ioHandlerFactory(); diff --git a/paper-server/patches/sources/net/minecraft/server/network/LegacyQueryHandler.java.patch b/paper-server/patches/sources/net/minecraft/server/network/LegacyQueryHandler.java.patch index 821b387c4698..ab3151fb49dd 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/LegacyQueryHandler.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/LegacyQueryHandler.java.patch @@ -6,80 +6,80 @@ private final ServerInfo server; + private ByteBuf buf; // Paper - public LegacyQueryHandler(ServerInfo server) { + public LegacyQueryHandler(final ServerInfo server) { this.server = server; @@ -22,6 +_,17 @@ @Override - public void channelRead(ChannelHandlerContext ctx, Object message) { - ByteBuf byteBuf = (ByteBuf)message; + public void channelRead(final ChannelHandlerContext ctx, final Object msg) { + ByteBuf in = (ByteBuf)msg; + // Paper start - Make legacy ping handler more reliable + if (this.buf != null) { + try { -+ readLegacy1_6(ctx, byteBuf); ++ this.readLegacy1_6(ctx, in); + } finally { -+ byteBuf.release(); ++ in.release(); + } + return; + } + // Paper end - Make legacy ping handler more reliable + - byteBuf.markReaderIndex(); - boolean flag = true; + in.markReaderIndex(); + boolean connectNormally = true; @@ -33,9 +_,19 @@ - SocketAddress socketAddress = ctx.channel().remoteAddress(); - int i = byteBuf.readableBytes(); -+ String string = null; // Paper - if (i == 0) { -- LOGGER.debug("Ping: (<1.3.x) from {}", socketAddress); -- String string = createVersion0Response(this.server); -+ LOGGER.debug("Ping: (<1.3.x) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? socketAddress : ""); // Paper - Respect logIPs option + SocketAddress socket = ctx.channel().remoteAddress(); + int length = in.readableBytes(); ++ String body = null; // Paper + if (length == 0) { +- LOGGER.debug("Ping: (<1.3.x) from {}", socket); +- String body = createVersion0Response(this.server); ++ LOGGER.debug("Ping: (<1.3.x) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? socket : ""); // Paper - Respect logIPs option + // Paper start - Call PaperServerListPingEvent and use results -+ com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), (java.net.InetSocketAddress) socketAddress, 39, null); ++ com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), (java.net.InetSocketAddress) socket, 39, null); + if (event == null) { + ctx.close(); -+ byteBuf.release(); -+ flag = false; ++ in.release(); ++ connectNormally = false; + return; + } -+ string = String.format(Locale.ROOT, "%s§%d§%d", com.destroystokyo.paper.network.PaperLegacyStatusClient.getUnformattedMotd(event), event.getNumPlayers(), event.getMaxPlayers()); ++ body = String.format(Locale.ROOT, "%s§%d§%d", com.destroystokyo.paper.network.PaperLegacyStatusClient.getUnformattedMotd(event), event.getNumPlayers(), event.getMaxPlayers()); + // Paper end - Call PaperServerListPingEvent and use results - sendFlushAndClose(ctx, createLegacyDisconnectPacket(ctx.alloc(), string)); + sendFlushAndClose(ctx, createLegacyDisconnectPacket(ctx.alloc(), body)); } else { - if (byteBuf.readUnsignedByte() != 1) { + if (in.readUnsignedByte() != 1) { @@ -43,16 +_,39 @@ } - if (byteBuf.isReadable()) { -- if (!readCustomPayloadPacket(byteBuf)) { + if (in.isReadable()) { +- if (!readCustomPayloadPacket(in)) { + // Paper start - Replace below -+ if (byteBuf.readUnsignedByte() != LegacyProtocolUtils.CUSTOM_PAYLOAD_PACKET_ID) { -+ string = this.readLegacy1_6(ctx, byteBuf); -+ if (string == null) { ++ if (in.readUnsignedByte() != LegacyProtocolUtils.CUSTOM_PAYLOAD_PACKET_ID) { ++ body = this.readLegacy1_6(ctx, in); ++ if (body == null) { + return; + } + } + // Paper end - Replace below + } else { -+ LOGGER.debug("Ping: (1.4-1.5.x) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? socketAddress : ""); // Paper - Respect logIPs option ++ LOGGER.debug("Ping: (1.4-1.5.x) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? socket : ""); // Paper - Respect logIPs option + } + + // Paper start - Call PaperServerListPingEvent and use results -+ if (string == null) { -+ com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), (java.net.InetSocketAddress) socketAddress, 127, null); // Paper ++ if (body == null) { ++ com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), (java.net.InetSocketAddress) socket, 127, null); // Paper + if (event == null) { + ctx.close(); -+ byteBuf.release(); -+ flag = false; ++ in.release(); ++ connectNormally = false; return; } -- LOGGER.debug("Ping: (1.6) from {}", socketAddress); +- LOGGER.debug("Ping: (1.6) from {}", socket); - } else { -- LOGGER.debug("Ping: (1.4-1.5.x) from {}", socketAddress); +- LOGGER.debug("Ping: (1.4-1.5.x) from {}", socket); + // See createVersion1Response -+ string = String.format( ++ body = String.format( + Locale.ROOT, + "§1\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", + event.getProtocolVersion(), this.server.getServerVersion(), @@ -90,17 +90,17 @@ + // Paper end - Call PaperServerListPingEvent and use results } - -- String string = createVersion1Response(this.server); - sendFlushAndClose(ctx, createLegacyDisconnectPacket(ctx.alloc(), string)); +- String body = createVersion1Response(this.server); + sendFlushAndClose(ctx, createLegacyDisconnectPacket(ctx.alloc(), body)); } -@@ -110,6 +_,98 @@ +@@ -110,6 +_,96 @@ server.getMaxPlayers() ); } + + // Paper start -+ private static @javax.annotation.Nullable String readLegacyString(ByteBuf buf) { ++ private static @org.jspecify.annotations.Nullable String readLegacyString(final ByteBuf buf) { + int size = buf.readShort() * Character.BYTES; + if (!buf.isReadable(size)) { + return null; @@ -111,7 +111,7 @@ + return result; + } + -+ private @javax.annotation.Nullable String readLegacy1_6(ChannelHandlerContext ctx, ByteBuf part) { ++ private @org.jspecify.annotations.Nullable String readLegacy1_6(final ChannelHandlerContext ctx, final ByteBuf part) { + ByteBuf buf = this.buf; + + if (buf == null) { @@ -133,7 +133,7 @@ + } + + if (!string.equals(LegacyProtocolUtils.CUSTOM_PAYLOAD_PACKET_PING_CHANNEL)) { -+ removeHandler(ctx); ++ this.removeHandler(ctx); + return null; + } + @@ -144,13 +144,13 @@ + int protocolVersion = buf.readByte(); + String host = readLegacyString(buf); + if (host == null) { -+ removeHandler(ctx); ++ this.removeHandler(ctx); + return null; + } + + int port = buf.readInt(); + if (buf.isReadable()) { -+ removeHandler(ctx); ++ this.removeHandler(ctx); + return null; + } + @@ -161,19 +161,17 @@ + + net.minecraft.server.MinecraftServer server = net.minecraft.server.MinecraftServer.getServer(); + java.net.InetSocketAddress virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(host, port); -+ com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest( -+ server, (java.net.InetSocketAddress) ctx.channel().remoteAddress(), protocolVersion, virtualHost); ++ com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(server, (java.net.InetSocketAddress) ctx.channel().remoteAddress(), protocolVersion, virtualHost); + if (event == null) { + ctx.close(); + return null; + } + -+ String response = String.format("§1\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", event.getProtocolVersion(), event.getVersion(), -+ com.destroystokyo.paper.network.PaperLegacyStatusClient.getMotd(event), event.getNumPlayers(), event.getMaxPlayers()); ++ String response = String.format("§1\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", event.getProtocolVersion(), event.getVersion(), com.destroystokyo.paper.network.PaperLegacyStatusClient.getMotd(event), event.getNumPlayers(), event.getMaxPlayers()); + return response; + } + -+ private void removeHandler(ChannelHandlerContext ctx) { ++ private void removeHandler(final ChannelHandlerContext ctx) { + ByteBuf buf = this.buf; + this.buf = null; + @@ -183,7 +181,7 @@ + } + + @Override -+ public void handlerRemoved(ChannelHandlerContext ctx) { ++ public void handlerRemoved(final ChannelHandlerContext ctx) { + if (this.buf != null) { + this.buf.release(); + this.buf = null; @@ -191,5 +189,5 @@ + } + // Paper end - private static void sendFlushAndClose(ChannelHandlerContext ctx, ByteBuf buffer) { - ctx.pipeline().firstContext().writeAndFlush(buffer).addListener(ChannelFutureListener.CLOSE); + private static void sendFlushAndClose(final ChannelHandlerContext ctx, final ByteBuf out) { + ctx.pipeline().firstContext().writeAndFlush(out).addListener(ChannelFutureListener.CLOSE); diff --git a/paper-server/patches/sources/net/minecraft/server/network/PlayerChunkSender.java.patch b/paper-server/patches/sources/net/minecraft/server/network/PlayerChunkSender.java.patch index 0681c61c8ac3..fcbaf082f271 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/PlayerChunkSender.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/PlayerChunkSender.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/server/network/PlayerChunkSender.java +++ b/net/minecraft/server/network/PlayerChunkSender.java @@ -44,6 +_,11 @@ - public void dropChunk(ServerPlayer player, ChunkPos chunkPos) { - if (!this.pendingChunks.remove(chunkPos.toLong()) && player.isAlive()) { - player.connection.send(new ClientboundForgetLevelChunkPacket(chunkPos)); + public void dropChunk(final ServerPlayer player, final ChunkPos pos) { + if (!this.pendingChunks.remove(pos.pack()) && player.isAlive()) { + player.connection.send(new ClientboundForgetLevelChunkPacket(pos)); + // Paper start - PlayerChunkUnloadEvent + if (io.papermc.paper.event.packet.PlayerChunkUnloadEvent.getHandlerList().getRegisteredListeners().length > 0) { -+ new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(player.getBukkitEntity().getWorld().getChunkAt(chunkPos.longKey), player.getBukkitEntity()).callEvent(); ++ new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(player.getBukkitEntity().getWorld().getChunkAt(pos.pack()), player.getBukkitEntity()).callEvent(); + } + // Paper end - PlayerChunkUnloadEvent } @@ -14,11 +14,11 @@ @@ -75,6 +_,11 @@ - private static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) { - packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null)); + private static void sendChunk(final ServerGamePacketListenerImpl connection, final ServerLevel level, final LevelChunk chunk) { + connection.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null)); + // Paper start - PlayerChunkLoadEvent + if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) { -+ new io.papermc.paper.event.packet.PlayerChunkLoadEvent(new org.bukkit.craftbukkit.CraftChunk(chunk), packetListener.getPlayer().getBukkitEntity()).callEvent(); ++ new io.papermc.paper.event.packet.PlayerChunkLoadEvent(new org.bukkit.craftbukkit.CraftChunk(chunk), connection.getPlayer().getBukkitEntity()).callEvent(); + } + // Paper end - PlayerChunkLoadEvent ChunkPos pos = chunk.getPos(); diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch index 0f41a4c322e1..dc4d70a76b47 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch @@ -25,7 +25,7 @@ + public final java.util.Set pluginMessagerChannels; + // Paper end - retain certain values - public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) { + public ServerCommonPacketListenerImpl(final MinecraftServer server, final Connection connection, final CommonListenerCookie cookie) { this.server = server; @@ -53,7 +_,18 @@ this.keepAliveTime = Util.getMillis(); @@ -47,7 +47,7 @@ private void close() { if (!this.closed) { @@ -83,7 +_,7 @@ - this.latency = (this.latency * 3 + i) / 4; + this.latency = (this.latency * 3 + time) / 4; this.keepAlivePending = false; } else if (!this.isSingleplayerOwner()) { - this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE); @@ -56,7 +56,7 @@ } @@ -91,14 +_,88 @@ - public void handlePong(ServerboundPongPacket packet) { + public void handlePong(final ServerboundPongPacket serverboundPongPacket) { } + // Paper start @@ -65,7 +65,7 @@ + // Paper end + @Override - public void handleCustomPayload(ServerboundCustomPayloadPacket packet) { + public void handleCustomPayload(final ServerboundCustomPayloadPacket packet) { + // Paper start + if (!(packet.payload() instanceof final net.minecraft.network.protocol.common.custom.DiscardedPayload discardedPayload)) { + return; @@ -101,7 +101,7 @@ + + this.cserver.getMessenger().dispatchIncomingMessage(paperConnection(), identifier.toString(), data); + } catch (final Exception e) { -+ ServerGamePacketListenerImpl.LOGGER.error("Couldn't handle custom payload on channel {}", identifier, e); ++ LOGGER.error("Couldn't handle custom payload on channel {}", identifier, e); + this.disconnect(net.minecraft.network.chat.Component.literal("Invalid custom payload payload!"), io.papermc.paper.connection.DisconnectionReason.INVALID_PAYLOAD); // Paper - kick event cause + } + } @@ -132,7 +132,7 @@ } @Override - public void handleCustomClickAction(ServerboundCustomClickActionPacket packet) { + public void handleCustomClickAction(final ServerboundCustomClickActionPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.server.packetProcessor()); this.server.handleCustomClickAction(packet.id(), packet.payload()); + // Paper start - Implement click callbacks with custom click action @@ -173,7 +173,7 @@ } @Override - public void handleCookieResponse(ServerboundCookieResponsePacket packet) { + public void handleCookieResponse(final ServerboundCookieResponsePacket packet) { - this.disconnect(DISCONNECT_UNEXPECTED_QUERY); + if (this.paperConnection().handleCookieResponse(packet)) return; // Paper + this.disconnect(DISCONNECT_UNEXPECTED_QUERY, io.papermc.paper.connection.DisconnectionReason.INVALID_COOKIE); // Paper - kick event cause @@ -181,11 +181,11 @@ protected void keepConnectionAlive() { Profiler.get().push("keepAlive"); - long millis = Util.getMillis(); -- if (!this.isSingleplayerOwner() && millis - this.keepAliveTime >= 15000L) { + long now = Util.getMillis(); +- if (!this.isSingleplayerOwner() && now - this.keepAliveTime >= 15000L) { + // Paper start - give clients a longer time to respond to pings as per pre 1.12.2 timings + // This should effectively place the keepalive handling back to "as it was" before 1.12.2 -+ final long elapsedTime = millis - this.keepAliveTime; ++ final long elapsedTime = now - this.keepAliveTime; + if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // use vanilla's 15000L between keep alive packets if (this.keepAlivePending) { - this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE); @@ -193,13 +193,13 @@ + this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, io.papermc.paper.connection.DisconnectionReason.TIMEOUT); // Paper - kick event cause + } + // Paper end - give clients a longer time to respond to pings as per pre 1.12.2 timings - } else if (this.checkIfClosed(millis)) { + } else if (this.checkIfClosed(now)) { this.keepAlivePending = true; - this.keepAliveTime = millis; + this.keepAliveTime = now; @@ -135,7 +_,7 @@ - private boolean checkIfClosed(long time) { + private boolean checkIfClosed(final long now) { if (this.closed) { - if (time - this.closedListenerTime >= 15000L) { + if (now - this.closedListenerTime >= 15000L) { - this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE); + this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, io.papermc.paper.connection.DisconnectionReason.TIMEOUT); // Paper - kick event cause } @@ -208,7 +208,7 @@ @@ -158,6 +_,13 @@ } - public void send(Packet packet, @Nullable ChannelFutureListener sendListener) { + public void send(final Packet packet, final @Nullable ChannelFutureListener listener) { + // CraftBukkit start + if (packet == null || this.processedDisconnect) { // Spigot + return; @@ -219,12 +219,12 @@ if (packet.isTerminal()) { this.close(); } -@@ -174,19 +_,114 @@ +@@ -174,15 +_,111 @@ } } + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - kick event causes - public void disconnect(Component reason) { + public void disconnect(final Component reason) { - this.disconnect(new DisconnectionDetails(reason)); + // Paper start - kick event causes + this.disconnect(reason, io.papermc.paper.connection.DisconnectionReason.UNKNOWN); @@ -235,11 +235,7 @@ + // Paper end - kick event causes } - public void disconnect(DisconnectionDetails disconnectionDetails) { -- this.connection -- .send( -- new ClientboundDisconnectPacket(disconnectionDetails.reason()), -- PacketSendListener.thenRun(() -> this.connection.disconnect(disconnectionDetails)) + public void disconnect(final DisconnectionDetails details) { + // CraftBukkit start - fire PlayerKickEvent + if (this.processedDisconnect) { + return; @@ -248,7 +244,7 @@ + org.bukkit.craftbukkit.util.Waitable waitable = new org.bukkit.craftbukkit.util.Waitable() { + @Override + protected Object evaluate() { -+ ServerCommonPacketListenerImpl.this.disconnect(disconnectionDetails); ++ ServerCommonPacketListenerImpl.this.disconnect(details); + return null; + } + }; @@ -271,13 +267,13 @@ + net.kyori.adventure.text.Component rawLeaveMessage = net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? serverGamePacketListener.player.getBukkitEntity().displayName() : net.kyori.adventure.text.Component.text(serverGamePacketListener.player.getScoreboardName())); // Paper - Adventure + + net.minecraft.server.level.ServerPlayer player = serverGamePacketListener.player; -+ org.bukkit.event.player.PlayerKickEvent.Cause cause = disconnectionDetails.disconnectionReason().orElseThrow().game().orElse(org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); ++ org.bukkit.event.player.PlayerKickEvent.Cause cause = details.disconnectionReason().orElseThrow().game().orElse(org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); + org.bukkit.event.player.PlayerKickEvent event = new org.bukkit.event.player.PlayerKickEvent( + player.getBukkitEntity(), -+ io.papermc.paper.adventure.PaperAdventure.asAdventure(disconnectionDetails.reason()), ++ io.papermc.paper.adventure.PaperAdventure.asAdventure(details.reason()), + rawLeaveMessage, cause + - ); ++ ); + + if (this.cserver.getServer().isRunning()) { + this.cserver.getPluginManager().callEvent(event); @@ -298,21 +294,18 @@ + } + } else { + // TODO: Add event for config event -+ reason = disconnectionDetails.reason(); ++ reason = details.reason(); + leaveMessage = null; + } + + // Send the possibly modified leave message -+ this.disconnect0(new DisconnectionDetails(reason, disconnectionDetails.report(), disconnectionDetails.bugReportLink(), java.util.Optional.ofNullable(leaveMessage), disconnectionDetails.disconnectionReason())); ++ this.disconnect0(new DisconnectionDetails(reason, details.report(), details.bugReportLink(), java.util.Optional.ofNullable(leaveMessage), details.disconnectionReason())); + } + -+ private void disconnect0(DisconnectionDetails disconnectionDetails) { -+ this.connection -+ .send( -+ new ClientboundDisconnectPacket(disconnectionDetails.reason()), -+ PacketSendListener.thenRun(() -> this.connection.disconnect(disconnectionDetails)) -+ ); -+ this.onDisconnect(disconnectionDetails); ++ private void disconnect0(final DisconnectionDetails details) { ++ // CraftBukkit end + this.connection.send(new ClientboundDisconnectPacket(details.reason()), PacketSendListener.thenRun(() -> this.connection.disconnect(details))); ++ this.onDisconnect(details); // CraftBukkit - fire quit instantly this.connection.setReadOnly(); - this.server.executeBlocking(this.connection::handleDisconnection); - } @@ -341,10 +334,10 @@ protected boolean isSingleplayerOwner() { return this.server.isSingleplayerOwner(new NameAndId(this.playerProfile())); -@@ -204,6 +_,6 @@ +@@ -200,6 +_,6 @@ } - protected CommonListenerCookie createCookie(ClientInformation clientInformation) { + protected CommonListenerCookie createCookie(final ClientInformation clientInformation) { - return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred); + return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels); // Paper } diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java.patch index 4f05492b68cd..9d6515de84a2 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java.patch @@ -6,7 +6,7 @@ private @Nullable PrepareSpawnTask prepareSpawnTask; + public io.papermc.paper.connection.PaperPlayerConfigurationConnection paperConnection; // Paper - public ServerConfigurationPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) { + public ServerConfigurationPacketListenerImpl(final MinecraftServer server, final Connection connection, final CommonListenerCookie cookie) { super(server, connection, cookie); this.gameProfile = cookie.gameProfile(); this.clientInformation = cookie.clientInformation(); @@ -31,7 +31,7 @@ @@ -66,6 +_,11 @@ @Override - public void onDisconnect(DisconnectionDetails details) { + public void onDisconnect(final DisconnectionDetails details) { + // Paper start - Debugging + if (this.server.isDebugging()) { + ServerConfigurationPacketListenerImpl.LOGGER.info("{} ({}) lost connection: {}, while in configuration phase {}", this.gameProfile.name(), this.gameProfile.id(), details.reason().getString(), this.currentTask != null ? this.currentTask.type().id() : "null"); @@ -56,9 +56,9 @@ + // Paper end } - LayeredRegistryAccess layeredRegistryAccess = this.server.registries(); + LayeredRegistryAccess registries = this.server.registries(); @@ -97,11 +_,12 @@ - this.synchronizeRegistriesTask = new SynchronizeRegistriesTask(list, layeredRegistryAccess); + this.synchronizeRegistriesTask = new SynchronizeRegistriesTask(knownPacks, registries); this.configurationTasks.add(this.synchronizeRegistriesTask); this.addOptionalTasks(); + this.configurationTasks.add(new io.papermc.paper.connection.PaperConfigurationTask(this)); // Paper @@ -71,15 +71,15 @@ this.configurationTasks.add(this.prepareSpawnTask); this.configurationTasks.add(new JoinWorldTask()); this.startNextTask(); -@@ -132,12 +_,14 @@ +@@ -130,12 +_,14 @@ @Override - public void handleClientInformation(ServerboundClientInformationPacket packet) { + public void handleClientInformation(final ServerboundClientInformationPacket packet) { this.clientInformation = packet.information(); + this.connection.channel.attr(io.papermc.paper.adventure.PaperAdventure.LOCALE_ATTRIBUTE).set(net.kyori.adventure.translation.Translator.parseLocale(packet.information().language())); // Paper } @Override - public void handleResourcePackResponse(ServerboundResourcePackPacket packet) { + public void handleResourcePackResponse(final ServerboundResourcePackPacket packet) { super.handleResourcePackResponse(packet); - if (packet.action().isTerminal()) { + this.connection.resourcePackStatus = org.bukkit.event.player.PlayerResourcePackStatusEvent.Status.values()[packet.action().ordinal()]; // Paper @@ -87,16 +87,16 @@ this.finishCurrentTask(ServerResourcePackConfigurationTask.TYPE); } } -@@ -171,7 +_,7 @@ +@@ -169,7 +_,7 @@ return; } -- Component component = playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(this.gameProfile)); -+ Component component = org.bukkit.craftbukkit.event.CraftEventFactory.handleLoginResult(playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(this.gameProfile)), this.paperConnection, this.connection, this.gameProfile, this.server, false); // Paper - Login event logic - if (component != null) { - this.disconnect(component); +- Component loginError = playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(this.gameProfile)); ++ Component loginError = org.bukkit.craftbukkit.event.CraftEventFactory.handleLoginResult(playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(this.gameProfile)), this.paperConnection, this.connection, this.gameProfile, this.server, false); // Paper - Login event logic + if (loginError != null) { + this.disconnect(loginError); return; -@@ -231,4 +_,29 @@ +@@ -229,4 +_,29 @@ this.startNextTask(); } } diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerConnectionListener.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerConnectionListener.java.patch index d4308c9813fd..bb81ac2cfbab 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerConnectionListener.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerConnectionListener.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/network/ServerConnectionListener.java +++ b/net/minecraft/server/network/ServerConnectionListener.java -@@ -48,9 +_,31 @@ +@@ -50,9 +_,31 @@ this.running = true; } @@ -15,7 +15,7 @@ + } + // Paper end - prevent blocking on adding a new connection while the server is ticking + - public void startTcpServerListener(@Nullable InetAddress address, int port) throws IOException { + public void startTcpServerListener(final @Nullable InetAddress address, final int port) throws IOException { + // Paper start - Unix domain socket support + this.startTcpServerListener(new java.net.InetSocketAddress(address, port)); + } @@ -33,26 +33,26 @@ this.channels .add( new ServerBootstrap() -@@ -74,22 +_,64 @@ +@@ -80,22 +_,64 @@ Connection connection = (Connection)(rateLimitPacketsPerSecond > 0 ? new RateKickingConnection(rateLimitPacketsPerSecond) : new Connection(PacketFlow.SERVERBOUND)); - ServerConnectionListener.this.connections.add(connection); + // Paper start - Add support for Proxy Protocol + if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.proxyProtocol) { -+ channel.pipeline().addAfter("timeout", "haproxy-decoder", new io.netty.handler.codec.haproxy.HAProxyMessageDecoder()); ++ channel.pipeline().addAfter(HandlerNames.TIMEOUT, "haproxy-decoder", new io.netty.handler.codec.haproxy.HAProxyMessageDecoder()); + channel.pipeline().addAfter("haproxy-decoder", "haproxy-handler", new ChannelInboundHandlerAdapter() { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (msg instanceof io.netty.handler.codec.haproxy.HAProxyMessage message) { + if (message.command() == io.netty.handler.codec.haproxy.HAProxyCommand.PROXY) { -+ String realaddress = message.sourceAddress(); -+ int realport = message.sourcePort(); ++ String realAddress = message.sourceAddress(); ++ int realPort = message.sourcePort(); + -+ SocketAddress socketaddr = new java.net.InetSocketAddress(realaddress, realport); ++ SocketAddress socketAddr = new java.net.InetSocketAddress(realAddress, realPort); + -+ Connection connection = (Connection) channel.pipeline().get("packet_handler"); -+ connection.address = socketaddr; ++ Connection connection = (Connection) channel.pipeline().get(HandlerNames.PACKET_HANDLER); ++ connection.address = socketAddr; + // Paper start - Add API to get player's proxy address + final String proxyAddress = message.destinationAddress(); + final int proxyPort = message.destinationPort(); @@ -69,7 +69,7 @@ + // Paper end - Add support for proxy protocol + // ServerConnectionListener.this.connections.add(connection); // Paper - prevent blocking on adding a new connection while the server is ticking + ServerConnectionListener.this.pending.add(connection); // Paper - prevent blocking on adding a new connection while the server is ticking - connection.configurePacketHandler(channelPipeline); + connection.configurePacketHandler(pipeline); connection.setListenerForServerboundHandshake( new ServerHandshakePacketListenerImpl(ServerConnectionListener.this.server, connection) ); @@ -98,9 +98,9 @@ + // CraftBukkit end + public SocketAddress startMemoryChannel() { - ChannelFuture channelFuture; + ChannelFuture newChannel; synchronized (this.channels) { -@@ -141,6 +_,13 @@ +@@ -151,6 +_,13 @@ public void tick() { synchronized (this.connections) { @@ -114,11 +114,11 @@ Iterator iterator = this.connections.iterator(); while (iterator.hasNext()) { -@@ -160,6 +_,7 @@ +@@ -170,6 +_,7 @@ connection.setReadOnly(); } } else { -+ if (connection.preparing) continue; // Spigot - Fix a race condition where a NetworkManager could be unregistered just before connection ++ if (connection.preparing) continue; // Spigot - Fix a race condition where a Connection could be unregistered just before connection iterator.remove(); connection.handleDisconnection(); } diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index cb5a872dad48..dc6d228ae5b6 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -214,6 +_,38 @@ +@@ -222,6 +_,38 @@ import org.jspecify.annotations.Nullable; import org.slf4j.Logger; @@ -38,8 +38,8 @@ + public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - implements GameProtocols.Context, -@@ -234,7 +_,9 @@ + implements ServerGamePacketListener, +@@ -242,7 +_,9 @@ private int tickCount; private int ackBlockChangesUpTo = -1; private final TickThrottler chatSpamThrottler = new TickThrottler(20, 200); @@ -49,7 +49,7 @@ private double firstGoodX; private double firstGoodY; private double firstGoodZ; -@@ -258,7 +_,23 @@ +@@ -266,7 +_,23 @@ private int receivedMovePacketCount; private int knownMovePacketCount; private boolean receivedMovementThisTick; @@ -73,7 +73,7 @@ private SignedMessageChain.Decoder signedMessageDecoder; private final LastSeenMessagesValidator lastSeenMessages = new LastSeenMessagesValidator(20); private int nextChatIndex; -@@ -267,6 +_,9 @@ +@@ -275,6 +_,9 @@ private boolean waitingForSwitchToConfig; private boolean waitingForRespawn; private int clientLoadedTimeoutTimer; @@ -81,9 +81,9 @@ + private final io.papermc.paper.event.packet.ClientTickEndEvent tickEndEvent; // Paper - add client tick end event + public final io.papermc.paper.connection.PaperPlayerGameConnection playerGameConnection; // Paper - public ServerGamePacketListenerImpl(MinecraftServer server, Connection connection, ServerPlayer player, CommonListenerCookie cookie) { + public ServerGamePacketListenerImpl(final MinecraftServer server, final Connection connection, final ServerPlayer player, final CommonListenerCookie cookie) { super(server, connection, cookie); -@@ -276,11 +_,26 @@ +@@ -284,11 +_,26 @@ player.connection = this; player.getTextFilter().join(); this.signedMessageDecoder = SignedMessageChain.Decoder.unsigned(player.getUUID(), server::enforceSecureProfile); @@ -112,7 +112,7 @@ if (this.ackBlockChangesUpTo > -1) { this.send(new ClientboundBlockChangedAckPacket(this.ackBlockChangesUpTo)); this.ackBlockChangesUpTo = -1; -@@ -290,11 +_,13 @@ +@@ -298,11 +_,13 @@ this.keepConnectionAlive(); this.chatSpamThrottler.tick(); this.dropSpamThrottler.tick(); @@ -127,7 +127,7 @@ } } } -@@ -310,8 +_,8 @@ +@@ -318,8 +_,8 @@ this.knownMovePacketCount = this.receivedMovePacketCount; if (this.clientIsFloating && !this.player.isSleeping() && !this.player.isPassenger() && !this.player.isDeadOrDying()) { if (++this.aboveGroundTickCount > this.getMaximumFlyingTicks(this.player)) { @@ -138,7 +138,7 @@ return true; } } else { -@@ -329,8 +_,8 @@ +@@ -337,8 +_,8 @@ this.vehicleLastGoodZ = this.lastVehicle.getZ(); if (this.clientVehicleIsFloating && this.lastVehicle.getControllingPassenger() == this.player) { if (++this.aboveGroundVehicleTickCount > this.getMaximumFlyingTicks(this.lastVehicle)) { @@ -149,7 +149,7 @@ return true; } } else { -@@ -343,6 +_,12 @@ +@@ -351,6 +_,12 @@ this.aboveGroundVehicleTickCount = 0; } @@ -162,9 +162,9 @@ return false; } -@@ -408,11 +_,37 @@ +@@ -416,11 +_,37 @@ @Override - public void handlePlayerInput(ServerboundPlayerInputPacket packet) { + public void handlePlayerInput(final ServerboundPlayerInputPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + // CraftBukkit start + if (!packet.input().equals(this.player.getLastClientInput())) { @@ -201,22 +201,22 @@ + } - private static boolean containsInvalidValues(double x, double y, double z, float yRot, float xRot) { -@@ -431,17 +_,29 @@ - public void handleMoveVehicle(ServerboundMoveVehiclePacket packet) { + private static boolean containsInvalidValues(final double x, final double y, final double z, final float yRot, final float xRot) { +@@ -439,11 +_,23 @@ + public void handleMoveVehicle(final ServerboundMoveVehiclePacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); if (containsInvalidValues(packet.position().x(), packet.position().y(), packet.position().z(), packet.yRot(), packet.xRot())) { - this.disconnect(Component.translatable("multiplayer.disconnect.invalid_vehicle_movement")); + this.disconnect(Component.translatable("multiplayer.disconnect.invalid_vehicle_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_VEHICLE_MOVEMENT); // Paper - kick event cause } else if (!this.updateAwaitingTeleport() && this.hasClientLoaded()) { - Entity rootVehicle = this.player.getRootVehicle(); + Entity vehicle = this.player.getRootVehicle(); + // Paper start - Don't allow vehicle movement from players while teleporting -+ if (this.awaitingPositionFromClient != null || this.player.isImmobile() || rootVehicle.isRemoved()) { ++ if (this.awaitingPositionFromClient != null || this.player.isImmobile() || vehicle.isRemoved()) { + return; + } + // Paper end - Don't allow vehicle movement from players while teleporting - if (rootVehicle != this.player && rootVehicle.getControllingPassenger() == this.player && rootVehicle == this.lastVehicle) { - ServerLevel serverLevel = this.player.level(); + if (vehicle != this.player && vehicle.getControllingPassenger() == this.player && vehicle == this.lastVehicle) { + ServerLevel level = this.player.level(); + // CraftBukkit - store current player position + double prevX = this.player.getX(); + double prevY = this.player.getY(); @@ -224,32 +224,23 @@ + float prevYaw = this.player.getYRot(); + float prevPitch = this.player.getXRot(); + // CraftBukkit end - double x = rootVehicle.getX(); - double y = rootVehicle.getY(); - double z = rootVehicle.getZ(); -- double d = clampHorizontal(packet.position().x()); -- double d1 = clampVertical(packet.position().y()); -- double d2 = clampHorizontal(packet.position().z()); -+ double d = clampHorizontal(packet.position().x()); final double toX = d; // Paper - OBFHELPER -+ double d1 = clampVertical(packet.position().y()); final double toY = d1; // Paper - OBFHELPER -+ double d2 = clampHorizontal(packet.position().z()); final double toZ = d2; // Paper - OBFHELPER - float f = Mth.wrapDegrees(packet.yRot()); - float f1 = Mth.wrapDegrees(packet.xRot()); - double d3 = d - this.vehicleFirstGoodX; -@@ -449,16 +_,61 @@ - double d5 = d2 - this.vehicleFirstGoodZ; - double d6 = rootVehicle.getDeltaMovement().lengthSqr(); - double d7 = d3 * d3 + d4 * d4 + d5 * d5; -- if (d7 - d6 > 100.0 && !this.isSingleplayerOwner()) { + double oldX = vehicle.getX(); + double oldY = vehicle.getY(); + double oldZ = vehicle.getZ(); +@@ -457,7 +_,52 @@ + double zDist = targetZ - this.vehicleFirstGoodZ; + double expectedDist = vehicle.getDeltaMovement().lengthSqr(); + double movedDist = xDist * xDist + yDist * yDist + zDist * zDist; +- if (movedDist - expectedDist > 100.0 && !this.isSingleplayerOwner()) { + // Paper start - fix large move vectors killing the server -+ double currDeltaX = toX - x; -+ double currDeltaY = toY - y; -+ double currDeltaZ = toZ - z; -+ d7 = Math.max(d7, (currDeltaX * currDeltaX + currDeltaY * currDeltaY + currDeltaZ * currDeltaZ) - 1); -+ double otherFieldX = toX - this.vehicleLastGoodX; -+ double otherFieldY = toY - this.vehicleLastGoodY; -+ double otherFieldZ = toZ - this.vehicleLastGoodZ; -+ d7 = Math.max(d7, (otherFieldX * otherFieldX + otherFieldY * otherFieldY + otherFieldZ * otherFieldZ) - 1); ++ double currDeltaX = targetX - oldX; ++ double currDeltaY = targetY - oldY; ++ double currDeltaZ = targetZ - oldZ; ++ movedDist = Math.max(movedDist, (currDeltaX * currDeltaX + currDeltaY * currDeltaY + currDeltaZ * currDeltaZ) - 1); ++ double otherFieldX = targetX - this.vehicleLastGoodX; ++ double otherFieldY = targetY - this.vehicleLastGoodY; ++ double otherFieldZ = targetZ - this.vehicleLastGoodZ; ++ movedDist = Math.max(movedDist, (otherFieldX * otherFieldX + otherFieldY * otherFieldY + otherFieldZ * otherFieldZ) - 1); + // Paper end - fix large move vectors killing the server + + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick; @@ -263,7 +254,7 @@ + i = 1; + } + -+ if (d7 > 0) { ++ if (movedDist > 0) { + this.allowedPlayerTicks -= 1; + } else { + this.allowedPlayerTicks = 20; @@ -278,45 +269,46 @@ + + // Paper start - Prevent moving into unloaded chunks + if (this.player.level().paperConfig().chunks.preventMovingIntoUnloadedChunks && ( -+ !serverLevel.areChunksLoadedForMove(this.player.getBoundingBox().expandTowards(new Vec3(toX, toY, toZ).subtract(this.player.position()))) || -+ !serverLevel.areChunksLoadedForMove(rootVehicle.getBoundingBox().expandTowards(new Vec3(toX, toY, toZ).subtract(rootVehicle.position()))) ++ !level.areChunksLoadedForMove(this.player.getBoundingBox().expandTowards(new Vec3(targetX, targetY, targetZ).subtract(this.player.position()))) || ++ !level.areChunksLoadedForMove(vehicle.getBoundingBox().expandTowards(new Vec3(targetX, targetY, targetZ).subtract(vehicle.position()))) + )) { -+ this.connection.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle)); ++ this.connection.send(ClientboundMoveVehiclePacket.fromEntity(vehicle)); + return; + } + // Paper end - Prevent moving into unloaded chunks -+ if (d7 - d6 > Math.max(100.0, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed)) && !this.isSingleplayerOwner()) { ++ if (movedDist - expectedDist > Math.max(100.0, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed)) && !this.isSingleplayerOwner()) { + // CraftBukkit end - LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", rootVehicle.getPlainTextName(), this.player.getPlainTextName(), d3, d4, d5); - this.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle)); - return; + LOGGER.warn( + "{} (vehicle of {}) moved too quickly! {},{},{}", vehicle.getPlainTextName(), this.player.getPlainTextName(), xDist, yDist, zDist + ); +@@ -466,9 +_,9 @@ } - AABB boundingBox = rootVehicle.getBoundingBox(); -- d3 = d - this.vehicleLastGoodX; -- d4 = d1 - this.vehicleLastGoodY; -- d5 = d2 - this.vehicleLastGoodZ; -+ d3 = d - this.vehicleLastGoodX; // Paper - diff on change, used for checking large move vectors above -+ d4 = d1 - this.vehicleLastGoodY; // Paper - diff on change, used for checking large move vectors above -+ d5 = d2 - this.vehicleLastGoodZ; // Paper - diff on change, used for checking large move vectors above - boolean flag = rootVehicle.verticalCollisionBelow; - if (rootVehicle instanceof LivingEntity livingEntity && livingEntity.onClimbable()) { - livingEntity.resetFallDistance(); -@@ -475,7 +_,7 @@ - d5 = d2 - rootVehicle.getZ(); - d7 = d3 * d3 + d4 * d4 + d5 * d5; - boolean flag1 = false; -- if (d7 > 0.0625) { -+ if (d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot - flag1 = true; - LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(d7)); + AABB oldAABB = vehicle.getBoundingBox(); +- xDist = targetX - this.vehicleLastGoodX; +- yDist = targetY - this.vehicleLastGoodY; +- zDist = targetZ - this.vehicleLastGoodZ; ++ xDist = targetX - this.vehicleLastGoodX; // Paper - diff on change, used for checking large move vectors above ++ yDist = targetY - this.vehicleLastGoodY; // Paper - diff on change, used for checking large move vectors above ++ zDist = targetZ - this.vehicleLastGoodZ; // Paper - diff on change, used for checking large move vectors above + boolean vehicleRestsOnSomething = vehicle.verticalCollisionBelow; + if (vehicle instanceof LivingEntity livingVehicle && livingVehicle.onClimbable()) { + livingVehicle.resetFallDistance(); +@@ -485,7 +_,7 @@ + zDist = targetZ - vehicle.getZ(); + movedDist = xDist * xDist + yDist * yDist + zDist * zDist; + boolean fail = false; +- if (movedDist > 0.0625) { ++ if (movedDist > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot + fail = true; + LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", vehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(movedDist)); } -@@ -489,6 +_,57 @@ +@@ -498,6 +_,57 @@ } - rootVehicle.absSnapTo(d, d1, d2, f, f1); + vehicle.absSnapTo(targetX, targetY, targetZ, targetYRot, targetXRot); + // CraftBukkit start - fire PlayerMoveEvent TODO: this should be removed. -+ this.player.absSnapTo(d, d1, d2, this.player.getYRot(), this.player.getXRot()); // Paper - TODO: This breaks alot of stuff ++ this.player.absSnapTo(targetX, targetY, targetZ, this.player.getYRot(), this.player.getXRot()); // Paper - TODO: This breaks alot of stuff + org.bukkit.entity.Player player = this.getCraftPlayer(); + if (!this.hasMoved) { + this.lastPosX = prevX; @@ -367,9 +359,9 @@ + } + // CraftBukkit end this.player.level().getChunkSource().move(this.player); - Vec3 vec3 = new Vec3(rootVehicle.getX() - x, rootVehicle.getY() - y, rootVehicle.getZ() - z); - this.handlePlayerKnownMovement(vec3); -@@ -519,7 +_,7 @@ + Vec3 clientDeltaMovement = new Vec3(vehicle.getX() - oldX, vehicle.getY() - oldY, vehicle.getZ() - oldZ); + this.handlePlayerKnownMovement(clientDeltaMovement); +@@ -528,7 +_,7 @@ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); if (packet.getId() == this.awaitingTeleport) { if (this.awaitingPositionFromClient == null) { @@ -378,7 +370,7 @@ return; } -@@ -536,13 +_,14 @@ +@@ -545,13 +_,14 @@ this.lastGoodZ = this.awaitingPositionFromClient.z; this.player.hasChangedDimension(); this.awaitingPositionFromClient = null; @@ -387,22 +379,22 @@ } @Override - public void handleAcceptPlayerLoad(ServerboundPlayerLoadedPacket packet) { + public void handleAcceptPlayerLoad(final ServerboundPlayerLoadedPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); - this.markClientLoaded(); + this.markClientLoaded(false); // Paper - Add PlayerLoadedWorldEvent } @Override -@@ -563,6 +_,7 @@ +@@ -572,6 +_,7 @@ @Override - public void handleRecipeBookChangeSettingsPacket(ServerboundRecipeBookChangeSettingsPacket packet) { + public void handleRecipeBookChangeSettingsPacket(final ServerboundRecipeBookChangeSettingsPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + CraftEventFactory.callRecipeBookSettingsEvent(this.player, packet.getBookType(), packet.isOpen(), packet.isFiltering()); // CraftBukkit this.player.getRecipeBook().setBookSetting(packet.getBookType(), packet.isOpen(), packet.isFiltering()); } -@@ -578,25 +_,110 @@ +@@ -587,25 +_,112 @@ } } @@ -412,7 +404,7 @@ + // Paper end - AsyncTabCompleteEvent + @Override - public void handleCustomCommandSuggestions(ServerboundCommandSuggestionPacket packet) { + public void handleCustomCommandSuggestions(final ServerboundCommandSuggestionPacket packet) { - PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); // Paper - AsyncTabCompleteEvent; run this async + // CraftBukkit start @@ -439,9 +431,9 @@ + + private void handleCustomCommandSuggestions0(final ServerboundCommandSuggestionPacket packet) { + // Paper end - AsyncTabCompleteEvent - StringReader stringReader = new StringReader(packet.getCommand()); - if (stringReader.canRead() && stringReader.peek() == '/') { - stringReader.skip(); + StringReader command = new StringReader(packet.getCommand()); + if (command.canRead() && command.peek() == '/') { + command.skip(); } + // Paper start - AsyncTabCompleteEvent @@ -455,9 +447,9 @@ + } + + // This needs to be on main -+ this.server.scheduleOnMain(() -> this.sendServerSuggestions(packet, stringReader)); ++ this.server.scheduleOnMain(() -> this.sendServerSuggestions(packet, command)); + } else if (!completions.isEmpty()) { -+ final com.mojang.brigadier.suggestion.SuggestionsBuilder builder0 = new com.mojang.brigadier.suggestion.SuggestionsBuilder(packet.getCommand(), stringReader.getTotalLength()); ++ final com.mojang.brigadier.suggestion.SuggestionsBuilder builder0 = new com.mojang.brigadier.suggestion.SuggestionsBuilder(packet.getCommand(), command.getTotalLength()); + final com.mojang.brigadier.suggestion.SuggestionsBuilder builder = builder0.createOffset(builder0.getInput().lastIndexOf(' ') + 1); + for (final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion completion : completions) { + final Integer intSuggestion = com.google.common.primitives.Ints.tryParse(completion.suggestion()); @@ -483,90 +475,85 @@ + } + // Paper end - brig API + -+ private void sendServerSuggestions(final ServerboundCommandSuggestionPacket packet, final StringReader stringReader) { ++ private void sendServerSuggestions(final ServerboundCommandSuggestionPacket packet, final StringReader command) { + // Paper end - AsyncTabCompleteEvent - ParseResults parseResults = this.server.getCommands().getDispatcher().parse(stringReader, this.player.createCommandSourceStack()); + ParseResults parse = this.server.getCommands().getDispatcher().parse(command, this.player.createCommandSourceStack()); + // Paper start - Handle non-recoverable exceptions -+ if (!parseResults.getExceptions().isEmpty() -+ && parseResults.getExceptions().values().stream().anyMatch(e -> e instanceof io.papermc.paper.brigadier.TagParseCommandSyntaxException)) { ++ if (!parse.getExceptions().isEmpty() ++ && parse.getExceptions().values().stream().anyMatch(e -> e instanceof io.papermc.paper.brigadier.TagParseCommandSyntaxException)) { + this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); + return; + } + // Paper end - Handle non-recoverable exceptions - this.server - .getCommands() - .getDispatcher() - .getCompletionSuggestions(parseResults) - .thenAccept( - suggestions -> { -- Suggestions suggestions1 = suggestions.getList().size() <= 1000 -- ? suggestions -- : new Suggestions(suggestions.getRange(), suggestions.getList().subList(0, 1000)); -- this.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions1)); -+ // Paper start - Don't tab-complete namespaced commands if send-namespaced is false -+ if (!org.spigotmc.SpigotConfig.sendNamespaced && suggestions.getRange().getStart() <= 1) { -+ suggestions.getList().removeIf(suggestion -> suggestion.getText().contains(":")); -+ } -+ // Paper end - Don't tab-complete namespaced commands if send-namespaced is false -+ // Paper start - Brigadier API -+ com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, packet.getCommand()); -+ suggestEvent.setCancelled(suggestions.isEmpty()); -+ if (suggestEvent.callEvent()) { -+ this.send(new ClientboundCommandSuggestionsPacket(packet.getId(), limitTo(suggestEvent.getSuggestions(), ServerGamePacketListenerImpl.MAX_COMMAND_SUGGESTIONS))); -+ } -+ // Paper end - Brigadier API - } - ); + this.server.getCommands().getDispatcher().getCompletionSuggestions(parse).thenAccept(results -> { +- Suggestions suggestions = results.getList().size() <= 1000 ? results : new Suggestions(results.getRange(), results.getList().subList(0, 1000)); +- this.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions)); ++ // Paper start - Don't tab-complete namespaced commands if send-namespaced is false ++ if (!org.spigotmc.SpigotConfig.sendNamespaced && results.getRange().getStart() <= 1) { ++ results.getList().removeIf(suggestion -> suggestion.getText().contains(":")); ++ } ++ // Paper end - Don't tab-complete namespaced commands if send-namespaced is false ++ // Paper start - Brigadier API ++ com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), results, packet.getCommand()); ++ suggestEvent.setCancelled(results.isEmpty()); ++ if (suggestEvent.callEvent()) { ++ this.send(new ClientboundCommandSuggestionsPacket(packet.getId(), limitTo(suggestEvent.getSuggestions(), ServerGamePacketListenerImpl.MAX_COMMAND_SUGGESTIONS))); ++ } ++ // Paper end - Brigadier API + }); } -@@ -604,7 +_,7 @@ + @Override - public void handleSetCommandBlock(ServerboundSetCommandBlockPacket packet) { + public void handleSetCommandBlock(final ServerboundSetCommandBlockPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); - if (!this.player.canUseGameMasterBlocks()) { + if (!this.player.canUseGameMasterBlocks() && (!this.player.isCreative() || !this.player.getBukkitEntity().hasPermission("minecraft.commandblock"))) { // Paper - command block permission this.player.sendSystemMessage(Component.translatable("advMode.notAllowed")); } else { - BaseCommandBlock baseCommandBlock = null; -@@ -665,7 +_,7 @@ + BaseCommandBlock commandBlock = null; +@@ -666,7 +_,7 @@ @Override - public void handleSetCommandMinecart(ServerboundSetCommandMinecartPacket packet) { + public void handleSetCommandMinecart(final ServerboundSetCommandMinecartPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); - if (!this.player.canUseGameMasterBlocks()) { + if (!this.player.canUseGameMasterBlocks() && (!this.player.isCreative() || !this.player.getBukkitEntity().hasPermission("minecraft.commandblock"))) { // Paper - command block permission this.player.sendSystemMessage(Component.translatable("advMode.notAllowed")); } else { BaseCommandBlock commandBlock = packet.getCommandBlock(this.player.level()); -@@ -703,11 +_,11 @@ - boolean flag = this.player.hasInfiniteMaterials() && packet.includeData(); - ItemStack cloneItemStack = blockState.getCloneItemStack(serverLevel, blockPos, flag); - if (!cloneItemStack.isEmpty()) { -- if (flag) { -+ if (flag && this.player.getBukkitEntity().hasPermission("minecraft.nbt.copy")) { // Spigot - addBlockDataToItem(blockState, serverLevel, blockPos, cloneItemStack); +@@ -702,11 +_,11 @@ + boolean includeData = this.player.hasInfiniteMaterials() && packet.includeData(); + ItemStack itemStack = blockState.getCloneItemStack(level, pos, includeData); + if (!itemStack.isEmpty()) { +- if (includeData) { ++ if (includeData && this.player.getBukkitEntity().hasPermission("minecraft.nbt.copy")) { // Spigot + addBlockDataToItem(blockState, level, pos, itemStack); } -- this.tryPickItem(cloneItemStack); -+ this.tryPickItem(cloneItemStack, blockPos, null, packet.includeData()); // Paper - Extend PlayerPickItemEvent API +- this.tryPickItem(itemStack); ++ this.tryPickItem(itemStack, pos, null, packet.includeData()); // Paper - Extend PlayerPickItemEvent API } } } -@@ -734,27 +_,40 @@ - if (entityOrPart != null && this.player.isWithinEntityInteractionRange(entityOrPart, 3.0)) { - ItemStack pickResult = entityOrPart.getPickResult(); - if (pickResult != null && !pickResult.isEmpty()) { -- this.tryPickItem(pickResult); -+ this.tryPickItem(pickResult, null, entityOrPart, packet.includeData()); // Paper - Extend PlayerPickItemEvent API +@@ -733,7 +_,7 @@ + if (entity != null && this.player.isWithinEntityInteractionRange(entity, 3.0)) { + ItemStack itemStack = entity.getPickResult(); + if (itemStack != null && !itemStack.isEmpty()) { +- this.tryPickItem(itemStack); ++ this.tryPickItem(itemStack, null, entity, packet.includeData()); // Paper - Extend PlayerPickItemEvent API } + + if (packet.includeData() && this.player.canUseGameMasterBlocks() && entity instanceof Avatar avatar) { +@@ -742,22 +_,35 @@ } } -- private void tryPickItem(ItemStack stack) { -+ private void tryPickItem(ItemStack stack, @Nullable BlockPos blockPos, @Nullable Entity entity, boolean includeData) { // Paper - Extend PlayerPickItemEvent API - if (stack.isItemEnabled(this.player.level().enabledFeatures())) { +- private void tryPickItem(final ItemStack itemStack) { ++ private void tryPickItem(ItemStack itemStack, @Nullable BlockPos blockPos, @Nullable Entity entity, boolean includeData) { // Paper - Extend PlayerPickItemEvent API + if (itemStack.isItemEnabled(this.player.level().enabledFeatures())) { Inventory inventory = this.player.getInventory(); - int i = inventory.findSlotMatchingItem(stack); + int slotWithExistingItem = inventory.findSlotMatchingItem(itemStack); + // Paper start - Add PlayerPickItemEvent -+ final int sourceSlot = i; ++ final int sourceSlot = slotWithExistingItem; + final int targetSlot = Inventory.isHotbarSlot(sourceSlot) ? sourceSlot : inventory.getSuitableHotbarSlot(); + final org.bukkit.entity.Player bukkitPlayer = this.player.getBukkitEntity(); + final io.papermc.paper.event.player.PlayerPickItemEvent event = entity != null @@ -575,20 +562,20 @@ + if (!event.callEvent()) { + return; + } -+ i = event.getSourceSlot(); ++ slotWithExistingItem = event.getSourceSlot(); + // Paper end - Add PlayerPickItemEvent - if (i != -1) { -- if (Inventory.isHotbarSlot(i)) { -- inventory.setSelectedSlot(i); -+ if (Inventory.isHotbarSlot(i) && Inventory.isHotbarSlot(event.getTargetSlot())) { // Paper - Add PlayerPickItemEvent + if (slotWithExistingItem != Inventory.NOT_FOUND_INDEX) { +- if (Inventory.isHotbarSlot(slotWithExistingItem)) { +- inventory.setSelectedSlot(slotWithExistingItem); ++ if (Inventory.isHotbarSlot(slotWithExistingItem) && Inventory.isHotbarSlot(event.getTargetSlot())) { // Paper - Add PlayerPickItemEvent + inventory.setSelectedSlot(event.getTargetSlot()); // Paper - Add PlayerPickItemEvent } else { -- inventory.pickSlot(i); -+ inventory.pickSlot(i, event.getTargetSlot()); // Paper - Add PlayerPickItemEvent +- inventory.pickSlot(slotWithExistingItem); ++ inventory.pickSlot(slotWithExistingItem, event.getTargetSlot()); // Paper - Add PlayerPickItemEvent } } else if (this.player.hasInfiniteMaterials()) { -- inventory.addAndPickItem(stack); -+ inventory.addAndPickItem(stack, event.getTargetSlot()); // Paper - Add PlayerPickItemEvent +- inventory.addAndPickItem(itemStack); ++ inventory.addAndPickItem(itemStack, event.getTargetSlot()); // Paper - Add PlayerPickItemEvent } this.send(new ClientboundSetHeldSlotPacket(inventory.getSelectedSlot())); @@ -597,24 +584,42 @@ } } -@@ -932,6 +_,13 @@ +@@ -790,7 +_,7 @@ + @Override + public void handleSetGameRule(final ServerboundSetGameRulePacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); +- if (!this.player.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER)) { ++ if (!this.player.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER) && !this.player.getBukkitEntity().hasPermission("minecraft.command.gamerule")) { // Paper - add permission check + LOGGER.warn("Player {} tried to set game rule values without required permissions", this.player.getGameProfile().name()); + } else { + GameRules gameRules = this.player.level().getGameRules(); +@@ -808,7 +_,7 @@ + + private void setGameRuleValue(final GameRules gameRules, final GameRule rule, final String value) { + rule.deserialize(value).result().ifPresent(parsedValue -> { +- gameRules.set(rule, (T)parsedValue, this.server); ++ parsedValue = org.bukkit.craftbukkit.event.CraftEventFactory.handleGameRuleSet(rule, parsedValue, this.player.level(), this.player.getBukkitEntity()).value(); // Paper - per-world game rules and event + this.broadcastGameRuleChangeToOperators(rule, (T)parsedValue); + }); + } +@@ -967,6 +_,13 @@ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); - int item = packet.getItem(); - if (this.player.containerMenu instanceof MerchantMenu merchantMenu) { + int selection = packet.getItem(); + if (this.player.containerMenu instanceof MerchantMenu menu) { + // CraftBukkit start -+ final org.bukkit.event.inventory.TradeSelectEvent tradeSelectEvent = CraftEventFactory.callTradeSelectEvent(item, merchantMenu); ++ final org.bukkit.event.inventory.TradeSelectEvent tradeSelectEvent = CraftEventFactory.callTradeSelectEvent(selection, menu); + if (tradeSelectEvent.isCancelled()) { + this.player.containerMenu.sendAllDataToRemote(); + return; + } + // CraftBukkit end - if (!merchantMenu.stillValid(this.player)) { - LOGGER.debug("Player {} interacted with invalid menu {}", this.player, merchantMenu); + if (!menu.stillValid(this.player)) { + LOGGER.debug("Player {} interacted with invalid menu {}", this.player, menu); return; -@@ -944,6 +_,51 @@ +@@ -979,6 +_,51 @@ @Override - public void handleEditBook(ServerboundEditBookPacket packet) { + public void handleEditBook(final ServerboundEditBookPacket packet) { + // Paper start - Book size limits + final io.papermc.paper.configuration.type.number.IntOr.Disabled pageMax = io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.pageMax; + if (!this.cserver.isPrimaryThread() && pageMax.enabled()) { @@ -662,63 +667,49 @@ + // CraftBukkit end int slot = packet.slot(); if (Inventory.isHotbarSlot(slot) || slot == 40) { - List list = Lists.newArrayList(); -@@ -958,10 +_,14 @@ + List contents = Lists.newArrayList(); +@@ -993,10 +_,14 @@ } - private void updateBookContents(List pages, int index) { -- ItemStack item = this.player.getInventory().getItem(index); + private void updateBookContents(final List contents, final int slot) { +- ItemStack carried = this.player.getInventory().getItem(slot); + // CraftBukkit start -+ ItemStack handItem = this.player.getInventory().getItem(index); -+ ItemStack item = handItem.copy(); ++ ItemStack handItem = this.player.getInventory().getItem(slot); ++ ItemStack carried = handItem.copy(); + // CraftBukkit end - if (item.has(DataComponents.WRITABLE_BOOK_CONTENT)) { - List> list = pages.stream().map(this::filterableFromOutgoing).toList(); - item.set(DataComponents.WRITABLE_BOOK_CONTENT, new WritableBookContent(list)); -+ this.player.getInventory().setItem(index, CraftEventFactory.handleEditBookEvent(this.player, index, handItem, item)); // CraftBukkit // Paper - Don't ignore result (see other callsite for handleEditBookEvent) + if (carried.has(DataComponents.WRITABLE_BOOK_CONTENT)) { + List> pages = contents.stream().map(this::filterableFromOutgoing).toList(); + carried.set(DataComponents.WRITABLE_BOOK_CONTENT, new WritableBookContent(pages)); ++ this.player.getInventory().setItem(slot, CraftEventFactory.handleEditBookEvent(this.player, slot, handItem, carried)); // CraftBukkit // Paper - Don't ignore result (see other callsite for handleEditBookEvent) } } -@@ -974,7 +_,8 @@ - itemStack.set( - DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent(this.filterableFromOutgoing(title), this.player.getPlainTextName(), 0, list, true) +@@ -1009,7 +_,8 @@ + writtenBook.set( + DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent(this.filterableFromOutgoing(title), this.player.getPlainTextName(), 0, pages, true) ); -- this.player.getInventory().setItem(index, itemStack); -+ CraftEventFactory.handleEditBookEvent(this.player, index, item, itemStack); // CraftBukkit -+ this.player.getInventory().setItem(index, item); // CraftBukkit - event factory updates the hand book +- this.player.getInventory().setItem(slot, writtenBook); ++ CraftEventFactory.handleEditBookEvent(this.player, slot, carried, writtenBook); // CraftBukkit ++ this.player.getInventory().setItem(slot, carried); // CraftBukkit - event factory updates the hand book } } -@@ -1022,27 +_,35 @@ - public void handleMovePlayer(ServerboundMovePlayerPacket packet) { +@@ -1057,10 +_,10 @@ + public void handleMovePlayer(final ServerboundMovePlayerPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); if (containsInvalidValues(packet.getX(0.0), packet.getY(0.0), packet.getZ(0.0), packet.getYRot(0.0F), packet.getXRot(0.0F))) { - this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement")); + this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause } else { - ServerLevel serverLevel = this.player.level(); + ServerLevel level = this.player.level(); - if (!this.player.wonGame) { + if (!this.player.wonGame && !this.player.isImmobile()) { // CraftBukkit if (this.tickCount == 0) { this.resetPosition(); } - - if (this.hasClientLoaded()) { -- float f = Mth.wrapDegrees(packet.getYRot(this.player.getYRot())); -- float f1 = Mth.wrapDegrees(packet.getXRot(this.player.getXRot())); -+ float f = Mth.wrapDegrees(packet.getYRot(this.player.getYRot())); final float toYaw = f; // Paper - OBFHELPER -+ float f1 = Mth.wrapDegrees(packet.getXRot(this.player.getXRot())); final float toPitch = f1; // Paper - OBFHELPER - if (this.updateAwaitingTeleport()) { - this.player.absSnapRotationTo(f, f1); - } else { -- double d = clampHorizontal(packet.getX(this.player.getX())); -- double d1 = clampVertical(packet.getY(this.player.getY())); -- double d2 = clampHorizontal(packet.getZ(this.player.getZ())); -+ double d = clampHorizontal(packet.getX(this.player.getX())); final double toX = d; // Paper - OBFHELPER -+ double d1 = clampVertical(packet.getY(this.player.getY())); final double toY = d1; // Paper - OBFHELPER -+ double d2 = clampHorizontal(packet.getZ(this.player.getZ())); final double toZ = d2; // Paper - OBFHELPER +@@ -1077,7 +_,15 @@ if (this.player.isPassenger()) { - this.player.absSnapTo(this.player.getX(), this.player.getY(), this.player.getZ(), f, f1); + this.player.absSnapTo(this.player.getX(), this.player.getY(), this.player.getZ(), targetYRot, targetXRot); this.player.level().getChunkSource().move(this.player); + this.allowedPlayerTicks = 20; // CraftBukkit } else { @@ -729,45 +720,47 @@ + float prevYaw = this.player.getYRot(); + float prevPitch = this.player.getXRot(); + // CraftBukkit end - double x = this.player.getX(); - double y = this.player.getY(); - double z = this.player.getZ(); -@@ -1051,6 +_,16 @@ - double d5 = d2 - this.firstGoodZ; - double d6 = this.player.getDeltaMovement().lengthSqr(); - double d7 = d3 * d3 + d4 * d4 + d5 * d5; + double startX = this.player.getX(); + double startY = this.player.getY(); + double startZ = this.player.getZ(); +@@ -1086,6 +_,16 @@ + double zDist = targetZ - this.firstGoodZ; + double expectedDist = this.player.getDeltaMovement().lengthSqr(); + double movedDist = xDist * xDist + yDist * yDist + zDist * zDist; + // Paper start - fix large move vectors killing the server -+ double currDeltaX = toX - prevX; -+ double currDeltaY = toY - prevY; -+ double currDeltaZ = toZ - prevZ; -+ d7 = Math.max(d7, (currDeltaX * currDeltaX + currDeltaY * currDeltaY + currDeltaZ * currDeltaZ) - 1); -+ double otherFieldX = d - this.lastGoodX; -+ double otherFieldY = d1 - this.lastGoodY; -+ double otherFieldZ = d2 - this.lastGoodZ; -+ d7 = Math.max(d7, (otherFieldX * otherFieldX + otherFieldY * otherFieldY + otherFieldZ * otherFieldZ) - 1); ++ double currDeltaX = targetX - prevX; ++ double currDeltaY = targetY - prevY; ++ double currDeltaZ = targetZ - prevZ; ++ movedDist = Math.max(movedDist, (currDeltaX * currDeltaX + currDeltaY * currDeltaY + currDeltaZ * currDeltaZ) - 1); ++ double otherFieldX = targetX - this.lastGoodX; ++ double otherFieldY = targetY - this.lastGoodY; ++ double otherFieldZ = targetZ - this.lastGoodZ; ++ movedDist = Math.max(movedDist, (otherFieldX * otherFieldX + otherFieldY * otherFieldY + otherFieldZ * otherFieldZ) - 1); + // Paper end - fix large move vectors killing the server if (this.player.isSleeping()) { - if (d7 > 1.0) { - this.teleport(this.player.getX(), this.player.getY(), this.player.getZ(), f, f1); -@@ -1060,36 +_,108 @@ - if (serverLevel.tickRateManager().runsNormally()) { + if (movedDist > 1.0) { + this.teleport(this.player.getX(), this.player.getY(), this.player.getZ(), targetYRot, targetXRot); +@@ -1095,7 +_,13 @@ + if (level.tickRateManager().runsNormally()) { this.receivedMovePacketCount++; - int i = this.receivedMovePacketCount - this.knownMovePacketCount; -- if (i > 5) { + int deltaPackets = this.receivedMovePacketCount - this.knownMovePacketCount; +- if (deltaPackets > 5) { + // CraftBukkit start - handle custom speeds and skipped ticks + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick; + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1); + this.lastTick = (int) (System.currentTimeMillis() / 50); + -+ if (i > Math.max(this.allowedPlayerTicks, 5)) { ++ if (deltaPackets > Math.max(this.allowedPlayerTicks, 5)) { + // CraftBukkit end LOGGER.debug( - "{} is sending move packets too frequently ({} packets since last tick)", this.player.getPlainTextName(), i + "{} is sending move packets too frequently ({} packets since last tick)", + this.player.getPlainTextName(), +@@ -1103,30 +_,96 @@ ); - i = 1; + deltaPackets = 1; } + // CraftBukkit start - handle custom speeds and skipped ticks -+ if (packet.hasRot || d7 > 0) { ++ if (packet.hasRot || movedDist > 0) { + this.allowedPlayerTicks -= 1; + } else { + this.allowedPlayerTicks = 20; @@ -779,10 +772,10 @@ + speed = this.player.getAbilities().walkingSpeed * 10f; + } + // Paper start - Prevent moving into unloaded chunks -+ if (this.player.level().paperConfig().chunks.preventMovingIntoUnloadedChunks && (this.player.getX() != toX || this.player.getZ() != toZ) && !serverLevel.areChunksLoadedForMove(this.player.getBoundingBox().expandTowards(new Vec3(toX, toY, toZ).subtract(this.player.position())))) { ++ if (this.player.level().paperConfig().chunks.preventMovingIntoUnloadedChunks && (this.player.getX() != targetX || this.player.getZ() != targetZ) && !level.areChunksLoadedForMove(this.player.getBoundingBox().expandTowards(new Vec3(targetX, targetY, targetZ).subtract(this.player.position())))) { + // Paper start - Add fail move event + io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_INTO_UNLOADED_CHUNK, -+ toX, toY, toZ, toYaw, toPitch, false); ++ targetX, targetY, targetZ, targetYRot, targetXRot, false); + if (!event.isAllowed()) { + this.internalTeleport(PositionMoveRotation.of(this.player), Collections.emptySet()); + return; @@ -792,16 +785,16 @@ + // Paper end - Prevent moving into unloaded chunks if (this.shouldCheckPlayerMovement(isFallFlying)) { - float f2 = isFallFlying ? 300.0F : 100.0F; -- if (d7 - d6 > f2 * i) { -+ if (d7 - d6 > Math.max(f2, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed))) { + float metersPerTick = isFallFlying ? 300.0F : 100.0F; +- if (movedDist - expectedDist > metersPerTick * deltaPackets) { ++ if (movedDist - expectedDist > Math.max(metersPerTick, Mth.square(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) deltaPackets * speed))) { + // CraftBukkit end + // Paper start - Add fail move event + io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY, -+ toX, toY, toZ, toYaw, toPitch, true); ++ targetX, targetY, targetZ, targetYRot, targetXRot, true); + if (!event.isAllowed()) { + if (event.getLogWarning()) { - LOGGER.warn("{} moved too quickly! {},{},{}", this.player.getPlainTextName(), d3, d4, d5); + LOGGER.warn("{} moved too quickly! {},{},{}", this.player.getPlainTextName(), xDist, yDist, zDist); - this.teleport( - this.player.getX(), this.player.getY(), this.player.getZ(), this.player.getYRot(), this.player.getXRot() - ); @@ -817,15 +810,15 @@ } } - AABB boundingBox = this.player.getBoundingBox(); -- d3 = d - this.lastGoodX; -- d4 = d1 - this.lastGoodY; -- d5 = d2 - this.lastGoodZ; -+ d3 = d - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above -+ d4 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above -+ d5 = d2 - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above - boolean flag = d4 > 0.0; - if (this.player.onGround() && !packet.isOnGround() && flag) { + AABB oldAABB = this.player.getBoundingBox(); +- xDist = targetX - this.lastGoodX; +- yDist = targetY - this.lastGoodY; +- zDist = targetZ - this.lastGoodZ; ++ xDist = targetX - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above ++ yDist = targetY - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above ++ zDist = targetZ - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above + boolean movedUpwards = yDist > 0.0; + if (this.player.onGround() && !packet.isOnGround() && movedUpwards) { - this.player.jumpFromGround(); + // Paper start - Add PlayerJumpEvent + org.bukkit.entity.Player player = this.getCraftPlayer(); @@ -857,34 +850,34 @@ + // Paper end - Add PlayerJumpEvent } - boolean flag1 = this.player.verticalCollisionBelow; - this.player.move(MoverType.PLAYER, new Vec3(d3, d4, d5)); + boolean playerStandsOnSomething = this.player.verticalCollisionBelow; + this.player.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist)); + this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move + // Paper start - prevent position desync + if (this.awaitingPositionFromClient != null) { + return; // ... thanks Mojang for letting move calls teleport across dimensions. + } + // Paper end - prevent position desync - double verticalDelta = d4; - d3 = d - this.player.getX(); - d4 = d1 - this.player.getY(); -@@ -1099,21 +_,101 @@ - - d5 = d2 - this.player.getZ(); - d7 = d3 * d3 + d4 * d4 + d5 * d5; -- boolean flag2 = false; + double oyDist = yDist; + xDist = targetX - this.player.getX(); + yDist = targetY - this.player.getY(); +@@ -1136,21 +_,101 @@ + + zDist = targetZ - this.player.getZ(); + movedDist = xDist * xDist + yDist * yDist + zDist * zDist; +- boolean fail = false; + boolean movedWrongly = false; // Paper - Add fail move event; rename if (!this.player.isChangingDimension() -- && d7 > 0.0625 -+ && d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold // Spigot +- && movedDist > 0.0625 ++ && movedDist > org.spigotmc.SpigotConfig.movedWronglyThreshold // Spigot && !this.player.isSleeping() && !this.player.isCreative() && !this.player.isSpectator() && !this.player.isInPostImpulseGraceTime()) { -- flag2 = true; +- fail = true; + // Paper start - Add fail move event + io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_WRONGLY, -+ toX, toY, toZ, toYaw, toPitch, true); ++ targetX, targetY, targetZ, targetYRot, targetXRot, true); + if (!event.isAllowed()) { + movedWrongly = true; + if (event.getLogWarning()) @@ -894,16 +887,16 @@ - - if (this.player.noPhysics - || this.player.isSleeping() -- || (!flag2 || !serverLevel.noCollision(this.player, boundingBox)) -- && !this.isEntityCollidingWithAnythingNew(serverLevel, this.player, boundingBox, d, d1, d2)) { +- || (!fail || !level.noCollision(this.player, oldAABB)) +- && !this.isEntityCollidingWithAnythingNew(level, this.player, oldAABB, targetX, targetY, targetZ)) { + } // Paper + } + + // Paper start - Add fail move event -+ boolean allowMovement = this.player.noPhysics || this.player.isSleeping() || (!movedWrongly || !serverLevel.noCollision(this.player, boundingBox)) && !this.isEntityCollidingWithAnythingNew(serverLevel, this.player, boundingBox, d, d1, d2); ++ boolean allowMovement = this.player.noPhysics || this.player.isSleeping() || (!movedWrongly || !level.noCollision(this.player, oldAABB)) && !this.isEntityCollidingWithAnythingNew(level, this.player, oldAABB, targetX, targetY, targetZ); + if (!allowMovement) { + io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK, -+ toX, toY, toZ, toYaw, toPitch, false); ++ targetX, targetY, targetZ, targetYRot, targetXRot, false); + if (event.isAllowed()) { + allowMovement = true; + } @@ -976,19 +969,19 @@ + } + } + // Paper end - this.player.absSnapTo(d, d1, d2, f, f1); + this.player.absSnapTo(targetX, targetY, targetZ, targetYRot, targetXRot); boolean isAutoSpinAttack = this.player.isAutoSpinAttack(); - this.clientIsFloating = verticalDelta >= -0.03125 -@@ -1148,7 +_,7 @@ + this.clientIsFloating = oyDist >= -0.03125 +@@ -1185,7 +_,7 @@ this.lastGoodY = this.player.getY(); this.lastGoodZ = this.player.getZ(); } else { -- this.teleport(x, y, z, f, f1); -+ this.internalTeleport(x, y, z, f, f1); // CraftBukkit - SPIGOT-1807: Don't call teleport event, when the client thinks the player is falling, because the chunks are not loaded on the client yet. - this.player.doCheckFallDamage(this.player.getX() - x, this.player.getY() - y, this.player.getZ() - z, packet.isOnGround()); - this.player.removeLatestMovementRecording(); - } -@@ -1183,6 +_,7 @@ +- this.teleport(startX, startY, startZ, targetYRot, targetXRot); ++ this.internalTeleport(startX, startY, startZ, targetYRot, targetXRot); // CraftBukkit - SPIGOT-1807: Don't call teleport event, when the client thinks the player is falling, because the chunks are not loaded on the client yet. + this.player + .doCheckFallDamage( + this.player.getX() - startX, this.player.getY() - startY, this.player.getZ() - startZ, packet.isOnGround() +@@ -1223,6 +_,7 @@ this.player.getXRot() ); } @@ -996,10 +989,10 @@ return true; } else { -@@ -1206,10 +_,77 @@ +@@ -1248,10 +_,77 @@ } - public void teleport(double x, double y, double z, float yRot, float xRot) { + public void teleport(final double x, final double y, final double z, final float yRot, final float xRot) { - this.teleport(new PositionMoveRotation(new Vec3(x, y, z), Vec3.ZERO, yRot, xRot), Collections.emptySet()); + // CraftBukkit start + this.teleport(x, y, z, yRot, xRot, PlayerTeleportEvent.TeleportCause.UNKNOWN); @@ -1010,19 +1003,19 @@ + // CraftBukkit end } - public void teleport(PositionMoveRotation posMoveRotation, Set relatives) { + public void teleport(final PositionMoveRotation destination, final Set relatives) { + // CraftBukkit start -+ this.teleport(posMoveRotation, relatives, PlayerTeleportEvent.TeleportCause.UNKNOWN); ++ this.teleport(destination, relatives, PlayerTeleportEvent.TeleportCause.UNKNOWN); + } + -+ public boolean teleport(PositionMoveRotation posMoveRotation, Set relatives, PlayerTeleportEvent.TeleportCause cause) { // CraftBukkit - Return event status ++ public boolean teleport(PositionMoveRotation destination, Set relatives, PlayerTeleportEvent.TeleportCause cause) { // CraftBukkit - Return event status + org.bukkit.entity.Player player = this.getCraftPlayer(); + Location from = player.getLocation(); -+ PositionMoveRotation absolutePosition = PositionMoveRotation.calculateAbsolute(PositionMoveRotation.of(this.player), posMoveRotation, relatives); ++ PositionMoveRotation absolutePosition = PositionMoveRotation.calculateAbsolute(PositionMoveRotation.of(this.player), destination, relatives); + Location to = CraftLocation.toBukkit(absolutePosition.position(), this.player.level(), absolutePosition.yRot(), absolutePosition.xRot()); + // SPIGOT-5171: Triggered on join + if (from.equals(to)) { -+ this.internalTeleport(posMoveRotation, relatives); ++ this.internalTeleport(destination, relatives); + return true; // CraftBukkit - Return event status + } + @@ -1039,10 +1032,10 @@ + if (event.isCancelled() || !to.equals(event.getTo())) { + relatives = Set.of(); // target pos is absolute + to = event.isCancelled() ? event.getFrom() : event.getTo(); -+ posMoveRotation = new PositionMoveRotation(CraftLocation.toVec3(to), Vec3.ZERO, to.getYaw(), to.getPitch()); ++ destination = new PositionMoveRotation(CraftLocation.toVec3(to), Vec3.ZERO, to.getYaw(), to.getPitch()); + } + -+ this.internalTeleport(posMoveRotation, relatives); ++ this.internalTeleport(destination, relatives); + return !event.isCancelled(); // CraftBukkit - Return event status + } + @@ -1054,20 +1047,20 @@ + this.internalTeleport(new PositionMoveRotation(new Vec3(x, y, z), Vec3.ZERO, yRot, xRot), Collections.emptySet()); + } + -+ public void internalTeleport(PositionMoveRotation posMoveRotation, Set relatives) { ++ public void internalTeleport(PositionMoveRotation destination, Set relatives) { + org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper + // Paper start - Prevent teleporting dead entities + if (this.player.isRemoved()) { -+ LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName()); ++ LOGGER.info("Attempt to teleport removed player {} restricted", this.player.getScoreboardName()); + if (this.server.isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Attempt to teleport removed player"); + return; + } + // Paper end - Prevent teleporting dead entities -+ if (Float.isNaN(posMoveRotation.yRot())) { -+ posMoveRotation = new PositionMoveRotation(posMoveRotation.position(), posMoveRotation.deltaMovement(), 0, posMoveRotation.xRot()); ++ if (Float.isNaN(destination.yRot())) { ++ destination = new PositionMoveRotation(destination.position(), destination.deltaMovement(), 0, destination.xRot()); + } -+ if (Float.isNaN(posMoveRotation.xRot())) { -+ posMoveRotation = new PositionMoveRotation(posMoveRotation.position(), posMoveRotation.deltaMovement(), posMoveRotation.yRot(), 0); ++ if (Float.isNaN(destination.xRot())) { ++ destination = new PositionMoveRotation(destination.position(), destination.deltaMovement(), destination.yRot(), 0); + } + + this.justTeleported = true; @@ -1075,9 +1068,9 @@ this.awaitingTeleportTime = this.tickCount; if (++this.awaitingTeleport == Integer.MAX_VALUE) { this.awaitingTeleport = 0; -@@ -1217,12 +_,20 @@ +@@ -1259,12 +_,20 @@ - this.player.teleportSetPosition(posMoveRotation, relatives); + this.player.teleportSetPosition(destination, relatives); this.awaitingPositionFromClient = this.player.position(); + // CraftBukkit start - update last location + this.lastPosX = this.awaitingPositionFromClient.x; @@ -1086,24 +1079,24 @@ + this.lastYaw = this.player.getYRot(); + this.lastPitch = this.player.getXRot(); + // CraftBukkit end - this.send(ClientboundPlayerPositionPacket.of(this.awaitingTeleport, posMoveRotation, relatives)); + this.send(ClientboundPlayerPositionPacket.of(this.awaitingTeleport, destination, relatives)); } @Override - public void handlePlayerAction(ServerboundPlayerActionPacket packet) { + public void handlePlayerAction(final ServerboundPlayerActionPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + if (this.player.isImmobile()) return; // CraftBukkit if (this.hasClientLoaded()) { BlockPos pos = packet.getPos(); this.player.resetLastActionTime(); -@@ -1247,32 +_,95 @@ +@@ -1289,32 +_,95 @@ case SWAP_ITEM_WITH_OFFHAND: if (!this.player.isSpectator()) { - ItemStack itemInHand1 = this.player.getItemInHand(InteractionHand.OFF_HAND); + ItemStack swap = this.player.getItemInHand(InteractionHand.OFF_HAND); - this.player.setItemInHand(InteractionHand.OFF_HAND, this.player.getItemInHand(InteractionHand.MAIN_HAND)); -- this.player.setItemInHand(InteractionHand.MAIN_HAND, itemInHand1); +- this.player.setItemInHand(InteractionHand.MAIN_HAND, swap); + // CraftBukkit start - inspiration taken from DispenserRegistry (See SpigotCraft#394) -+ CraftItemStack mainHand = CraftItemStack.asCraftMirror(itemInHand1); ++ CraftItemStack mainHand = CraftItemStack.asCraftMirror(swap); + CraftItemStack offHand = CraftItemStack.asCraftMirror(this.player.getItemInHand(InteractionHand.MAIN_HAND)); + PlayerSwapHandItemsEvent swapItemsEvent = new PlayerSwapHandItemsEvent(this.getCraftPlayer(), mainHand.clone(), offHand.clone()); + this.cserver.getPluginManager().callEvent(swapItemsEvent); @@ -1116,7 +1109,7 @@ + this.player.setItemInHand(InteractionHand.OFF_HAND, CraftItemStack.asNMSCopy(swapItemsEvent.getOffHandItem())); + } + if (swapItemsEvent.getMainHandItem().equals(mainHand)) { -+ this.player.setItemInHand(InteractionHand.MAIN_HAND, itemInHand1); ++ this.player.setItemInHand(InteractionHand.MAIN_HAND, swap); + } else { + this.player.setItemInHand(InteractionHand.MAIN_HAND, CraftItemStack.asNMSCopy(swapItemsEvent.getMainHandItem())); + } @@ -1194,7 +1187,7 @@ return; default: throw new IllegalArgumentException("Invalid player action"); -@@ -1290,9 +_,36 @@ +@@ -1332,9 +_,36 @@ } } @@ -1224,111 +1217,111 @@ + // Paper end - limit place/interactions + @Override - public void handleUseItemOn(ServerboundUseItemOnPacket packet) { + public void handleUseItemOn(final ServerboundUseItemOnPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + if (this.player.isImmobile()) return; // CraftBukkit + if (!this.checkLimit(packet.timestamp)) return; // Spigot - check limit if (this.hasClientLoaded()) { this.ackBlockChangesUpTo(packet.getSequence()); - ServerLevel serverLevel = this.player.level(); -@@ -1301,6 +_,11 @@ - if (itemInHand.isItemEnabled(serverLevel.enabledFeatures())) { - BlockHitResult hitResult = packet.getHitResult(); - Vec3 location = hitResult.getLocation(); + ServerLevel level = this.player.level(); +@@ -1343,6 +_,11 @@ + if (itemStack.isItemEnabled(level.enabledFeatures())) { + BlockHitResult blockHit = packet.getHitResult(); + Vec3 location = blockHit.getLocation(); + // Paper start - improve distance check -+ if (!Double.isFinite(location.x()) || !Double.isFinite(location.y()) || !Double.isFinite(location.z())) { ++ if (!location.isFinite()) { + return; + } + // Paper end - improve distance check - BlockPos blockPos = hitResult.getBlockPos(); - if (this.player.isWithinBlockInteractionRange(blockPos, 1.0)) { - Vec3 vec3 = location.subtract(Vec3.atCenterOf(blockPos)); -@@ -1310,7 +_,8 @@ - this.player.resetLastActionTime(); - int maxY = this.player.level().getMaxY(); - if (blockPos.getY() <= maxY) { -- if (this.awaitingPositionFromClient == null && serverLevel.mayInteract(this.player, blockPos)) { -+ if (this.awaitingPositionFromClient == null && (serverLevel.mayInteract(this.player, blockPos) || (serverLevel.paperConfig().spawn.allowUsingSignsInsideSpawnProtection && serverLevel.getBlockState(blockPos).getBlock() instanceof net.minecraft.world.level.block.SignBlock))) { // Paper - Allow using signs inside spawn protection + BlockPos pos = blockHit.getBlockPos(); + if (this.player.isWithinBlockInteractionRange(pos, 1.0)) { + Vec3 distance = location.subtract(Vec3.atCenterOf(pos)); +@@ -1357,9 +_,13 @@ + } else if (pos.getY() < minY) { + this.player.sendBuildLimitMessage(false, minY); + } else { +- if (this.server.isUnderSpawnProtection(level, pos, this.player)) { ++ // Paper start - Allow using signs inside spawn protection ++ final boolean passthroughSignInteraction = (level.paperConfig().spawn.allowUsingSignsInsideSpawnProtection && level.getBlockState(pos).getBlock() instanceof net.minecraft.world.level.block.SignBlock); ++ if (!passthroughSignInteraction && this.server.isUnderSpawnProtection(level, pos, this.player)) { + this.player.sendSpawnProtectionMessage(pos); +- } else if (this.awaitingPositionFromClient == null && level.mayInteract(this.player, pos)) { ++ } else if (this.awaitingPositionFromClient == null && (level.mayInteract(this.player, pos) || passthroughSignInteraction)) { ++ // Paper end - Allow using signs inside spawn protection + this.player.stopUsingItem(); // CraftBukkit - SPIGOT-4706 - InteractionResult interactionResult = this.player.gameMode.useItemOn(this.player, serverLevel, itemInHand, hand, hitResult); + InteractionResult interactionResult = this.player.gameMode.useItemOn(this.player, level, itemStack, hand, blockHit); if (interactionResult.consumesAction()) { - CriteriaTriggers.ANY_BLOCK_USE.trigger(this.player, hitResult.getBlockPos(), itemInHand.copy()); -@@ -1323,10 +_,19 @@ - Component component = Component.translatable("build.tooHigh", maxY).withStyle(ChatFormatting.RED); - this.player.sendSystemMessage(component, true); + CriteriaTriggers.ANY_BLOCK_USE.trigger(this.player, blockHit.getBlockPos(), itemStack); +@@ -1371,7 +_,7 @@ + && wasBlockPlacementAttempt(this.player, itemStack)) { + this.player.sendBuildLimitMessage(true, maxY); } else if (interactionResult instanceof InteractionResult.Success success - && success.swingSource() == InteractionResult.SwingSource.SERVER) { + && success.swingSource() == InteractionResult.SwingSource.SERVER && !this.player.gameMode.interactResult) { // Paper - Call interact event this.player.swing(hand, true); } + +@@ -1385,6 +_,15 @@ + && success.swingSource() == InteractionResult.SwingSource.SERVER) { + this.player.swing(hand, true); + } + // Paper start - Fix inventory desync; MC-99075 -+ // From serverLevel.mayInteract(this.player, blockPos) -+ } else if (this.server.isUnderSpawnProtection(serverLevel, blockPos, player)) { -+ if (io.papermc.paper.util.MCUtil.clientPredictsInteraction(this.player, serverLevel.getBlockState(blockPos), itemInHand)) { ++ // From level.mayInteract(this.player, pos) ++ } else if (this.server.isUnderSpawnProtection(level, pos, player)) { ++ if (io.papermc.paper.util.MCUtil.clientPredictsInteraction(this.player, level.getBlockState(pos), itemStack)) { + this.player.containerMenu.sendAllDataToRemote(); + } else { + this.player.containerMenu.forceHeldSlot(hand); + } - } + // Paper end - Fix inventory desync - } else { - Component component1 = Component.translatable("build.tooHigh", maxY).withStyle(ChatFormatting.RED); - this.player.sendSystemMessage(component1, true); -@@ -1334,13 +_,8 @@ - - this.send(new ClientboundBlockUpdatePacket(serverLevel, blockPos)); - this.send(new ClientboundBlockUpdatePacket(serverLevel, blockPos.relative(direction))); + } else { + this.player.sendBuildLimitMessage(true, maxY); + } +@@ -1392,13 +_,8 @@ + this.send(new ClientboundBlockUpdatePacket(level, pos)); + this.send(new ClientboundBlockUpdatePacket(level, pos.relative(direction))); + } - } else { - LOGGER.warn( - "Rejecting UseItemOnPacket from {}: Location {} too far away from hit block {}.", - this.player.getGameProfile().name(), - location, -- blockPos +- pos - ); + if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes. + // Paper - Remove unused warning } } } -@@ -1350,6 +_,8 @@ +@@ -1408,6 +_,8 @@ @Override - public void handleUseItem(ServerboundUseItemPacket packet) { + public void handleUseItem(final ServerboundUseItemPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + if (this.player.isImmobile()) return; // CraftBukkit + if (!this.checkLimit(packet.timestamp)) return; // Spigot - check limit if (this.hasClientLoaded()) { this.ackBlockChangesUpTo(packet.getSequence()); - ServerLevel serverLevel = this.player.level(); -@@ -1363,6 +_,54 @@ - this.player.absSnapRotationTo(f, f1); + ServerLevel level = this.player.level(); +@@ -1421,6 +_,44 @@ + this.player.absSnapRotationTo(targetYRot, targetXRot); } + // CraftBukkit start + // Raytrace to look for 'rogue armswings' -+ double x = this.player.getX(); -+ double eyeY = this.player.getEyeY(); -+ double z = this.player.getZ(); -+ Vec3 from = new Vec3(x, eyeY, z); -+ -+ float f3 = Mth.cos(-f * 0.017453292F - 3.1415927F); -+ float f4 = Mth.sin(-f * 0.017453292F - 3.1415927F); -+ float f5 = -Mth.cos(-f1 * 0.017453292F); -+ float f6 = Mth.sin(-f1 * 0.017453292F); -+ float f7 = f4 * f5; -+ float f8 = f3 * f5; -+ double d3 = this.player.blockInteractionRange(); -+ Vec3 to = from.add((double) f7 * d3, (double) f6 * d3, (double) f8 * d3); ++ Vec3 from = this.player.getEyePosition(); ++ Vec3 delta = this.player.getViewVector(1.0F).scale(this.player.blockInteractionRange()); ++ Vec3 to = from.add(delta); + BlockHitResult hitResult = this.player.level().clip(new net.minecraft.world.level.ClipContext(from, to, net.minecraft.world.level.ClipContext.Block.OUTLINE, net.minecraft.world.level.ClipContext.Fluid.NONE, this.player)); + + boolean cancelled; + if (hitResult == null || hitResult.getType() != HitResult.Type.BLOCK) { -+ org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemInHand, hand); ++ org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemStack, hand); + cancelled = event.useItemInHand() == Event.Result.DENY; + } else { -+ if (this.player.gameMode.firedInteract && this.player.gameMode.interactPosition.equals(hitResult.getBlockPos()) && this.player.gameMode.interactHand == hand && ItemStack.isSameItemSameComponents(this.player.gameMode.interactItemStack, itemInHand)) { ++ if (this.player.gameMode.firedInteract && this.player.gameMode.interactPosition.equals(hitResult.getBlockPos()) && this.player.gameMode.interactHand == hand && ItemStack.isSameItemSameComponents(this.player.gameMode.interactItemStack, itemStack)) { + cancelled = this.player.gameMode.interactResult; + } else { -+ org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_BLOCK, hitResult.getBlockPos(), hitResult.getDirection(), itemInHand, true, hand, hitResult.getLocation()); ++ org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_BLOCK, hitResult.getBlockPos(), hitResult.getDirection(), itemStack, true, hand, hitResult.getLocation()); + cancelled = event.useItemInHand() == Event.Result.DENY; + } + this.player.gameMode.firedInteract = false; @@ -1337,7 +1330,7 @@ + if (cancelled) { + this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items + // Paper start - Fix inventory desync; SPIGOT-2524 -+ if (io.papermc.paper.util.MCUtil.clientPredictsInteraction(this.player, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(), itemInHand)) { ++ if (io.papermc.paper.util.MCUtil.clientPredictsInteraction(this.player, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(), itemStack)) { + this.player.containerMenu.sendAllDataToRemote(); + } else { + this.player.containerMenu.forceHeldSlotAndArmor(hand); @@ -1345,28 +1338,28 @@ + // Paper end - Fix inventory desync + return; + } -+ itemInHand = this.player.getItemInHand(hand); // Update in case it was changed in the event -+ if (itemInHand.isEmpty()) { ++ itemStack = this.player.getItemInHand(hand); // Update in case it was changed in the event ++ if (itemStack.isEmpty()) { + return; + } + // CraftBukkit end + - if (this.player.gameMode.useItem(this.player, serverLevel, itemInHand, hand) instanceof InteractionResult.Success success + if (this.player.gameMode.useItem(this.player, level, itemStack, hand) instanceof InteractionResult.Success success && success.swingSource() == InteractionResult.SwingSource.SERVER) { this.player.swing(hand, true); -@@ -1378,7 +_,7 @@ - for (ServerLevel serverLevel : this.server.getAllLevels()) { - Entity entity = packet.getEntity(serverLevel); +@@ -1436,7 +_,7 @@ + for (ServerLevel level : this.server.getAllLevels()) { + Entity entity = packet.getEntity(level); if (entity != null) { -- this.player.teleportTo(serverLevel, entity.getX(), entity.getY(), entity.getZ(), Set.of(), entity.getYRot(), entity.getXRot(), true); -+ this.player.teleportTo(serverLevel, entity.getX(), entity.getY(), entity.getZ(), Set.of(), entity.getYRot(), entity.getXRot(), true, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.SPECTATE); // CraftBukkit +- this.player.teleportTo(level, entity.getX(), entity.getY(), entity.getZ(), Set.of(), entity.getYRot(), entity.getXRot(), true); ++ this.player.teleportTo(level, entity.getX(), entity.getY(), entity.getZ(), Set.of(), entity.getYRot(), entity.getXRot(), true, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.SPECTATE); // CraftBukkit return; } } -@@ -1395,24 +_,50 @@ +@@ -1453,24 +_,50 @@ @Override - public void onDisconnect(DisconnectionDetails details) { + public void onDisconnect(final DisconnectionDetails details) { + // CraftBukkit start - Rarely it would send a disconnect line twice + if (this.processedDisconnect) { + return; @@ -1410,15 +1403,15 @@ this.player.getTextFilter().leave(); } - public void ackBlockChangesUpTo(int sequence) { - if (sequence < 0) { + public void ackBlockChangesUpTo(final int packetSequenceNr) { + if (packetSequenceNr < 0) { + this.disconnect(Component.literal("Expected packet sequence nr >= 0"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - Treat sequence violations like they should be throw new IllegalArgumentException("Expected packet sequence nr >= 0"); } else { - this.ackBlockChangesUpTo = Math.max(sequence, this.ackBlockChangesUpTo); -@@ -1422,20 +_,38 @@ + this.ackBlockChangesUpTo = Math.max(packetSequenceNr, this.ackBlockChangesUpTo); +@@ -1480,20 +_,38 @@ @Override - public void handleSetCarriedItem(ServerboundSetCarriedItemPacket packet) { + public void handleSetCarriedItem(final ServerboundSetCarriedItemPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + if (this.player.isImmobile()) return; // CraftBukkit if (packet.getSlot() >= 0 && packet.getSlot() < Inventory.getSelectionSize()) { @@ -1445,30 +1438,30 @@ } @Override - public void handleChat(ServerboundChatPacket packet) { + public void handleChat(final ServerboundChatPacket packet) { + // CraftBukkit start - async chat + // SPIGOT-3638 + if (this.server.isStopped()) { + return; + } + // CraftBukkit end - Optional optional = this.unpackAndApplyLastSeen(packet.lastSeenMessages()); - if (!optional.isEmpty()) { + Optional unpackedLastSeen = this.unpackAndApplyLastSeen(packet.lastSeenMessages()); + if (!unpackedLastSeen.isEmpty()) { this.tryHandleChat(packet.message(), false, () -> { -@@ -1447,25 +_,45 @@ +@@ -1505,25 +_,45 @@ return; } -- CompletableFuture completableFuture = this.filterTextPacket(signedMessage.signedContent()); -- Component component = this.server.getChatDecorator().decorate(this.player, signedMessage.decoratedContent()); -- this.chatMessageChain.append(completableFuture, filteredText -> { -- PlayerChatMessage playerChatMessage = signedMessage.withUnsignedContent(component).filter(filteredText.mask()); -+ CompletableFuture completableFuture = this.filterTextPacket(signedMessage.signedContent()).thenApplyAsync(java.util.function.Function.identity(), this.server.chatExecutor); // CraftBukkit - async chat -+ CompletableFuture componentFuture = this.server.getChatDecorator().decorate(this.player, null, signedMessage.decoratedContent()); // Paper - Adventure +- CompletableFuture filteredFuture = this.filterTextPacket(signedMessage.signedContent()); +- Component decorated = this.server.getChatDecorator().decorate(this.player, signedMessage.decoratedContent()); +- this.chatMessageChain.append(filteredFuture, filtered -> { +- PlayerChatMessage filteredMessage = signedMessage.withUnsignedContent(decorated).filter(filtered.mask()); ++ CompletableFuture filteredFuture = this.filterTextPacket(signedMessage.signedContent()).thenApplyAsync(java.util.function.Function.identity(), this.server.chatExecutor); // CraftBukkit - async chat ++ CompletableFuture decoratedFuture = this.server.getChatDecorator().decorate(this.player, null, signedMessage.decoratedContent()); // Paper - Adventure + -+ this.chatMessageChain.append(CompletableFuture.allOf(completableFuture, componentFuture), ($) -> { // Paper - Adventure -+ PlayerChatMessage playerChatMessage = signedMessage.withUnsignedContent(componentFuture.join()).filter(completableFuture.join().mask()); // Paper - Adventure - this.broadcastChatMessage(playerChatMessage); ++ this.chatMessageChain.append(CompletableFuture.allOf(filteredFuture, decoratedFuture), ($) -> { // Paper - Adventure ++ PlayerChatMessage filteredMessage = signedMessage.withUnsignedContent(decoratedFuture.join()).filter(filteredFuture.join().mask()); // Paper - Adventure + this.broadcastChatMessage(filteredMessage); }); - }); + }, false); // CraftBukkit - async chat @@ -1476,7 +1469,7 @@ } @Override - public void handleChatCommand(ServerboundChatCommandPacket packet) { + public void handleChatCommand(final ServerboundChatCommandPacket packet) { this.tryHandleChat(packet.command(), true, () -> { + // CraftBukkit start - SPIGOT-7346: Prevent disconnected players from executing commands + if (this.player.hasDisconnected()) { @@ -1490,8 +1483,9 @@ + }, true); // CraftBukkit - sync commands } - private void performUnsignedChatCommand(String command) { -+ // CraftBukkit start +- private void performUnsignedChatCommand(final String command) { ++ // CraftBukkit start ++ private void performUnsignedChatCommand(String command) { + String prefixedCommand = "/" + command; + if (org.spigotmc.SpigotConfig.logCommands) { // Paper - Add missing SpigotConfig logCommands check + LOGGER.info("{} issued server command: {}", this.player.getScoreboardName(), prefixedCommand); @@ -1505,19 +1499,19 @@ + } + command = event.getMessage().substring(1); + // CraftBukkit end - ParseResults parseResults = this.parseCommand(command); - if (this.server.enforceSecureProfile() && SignableCommand.hasSignableArguments(parseResults)) { + ParseResults parsed = this.parseCommand(command); + if (this.server.enforceSecureProfile() && SignableCommand.hasSignableArguments(parsed)) { LOGGER.error( -@@ -1482,28 +_,57 @@ - Optional optional = this.unpackAndApplyLastSeen(packet.lastSeenMessages()); - if (!optional.isEmpty()) { +@@ -1540,26 +_,55 @@ + Optional unpackedLastSeen = this.unpackAndApplyLastSeen(packet.lastSeenMessages()); + if (!unpackedLastSeen.isEmpty()) { this.tryHandleChat(packet.command(), true, () -> { + // CraftBukkit start - SPIGOT-7346: Prevent disconnected players from executing commands + if (this.player.hasDisconnected()) { + return; + } + // CraftBukkit end - this.performSignedChatCommand(packet, optional.get()); + this.performSignedChatCommand(packet, unpackedLastSeen.get()); - this.detectRateSpam(); - }); + this.detectRateSpam("/" + packet.command()); // Spigot @@ -1525,23 +1519,23 @@ } } - private void performSignedChatCommand(ServerboundChatCommandSignedPacket packet, LastSeenMessages lastSeenMessages) { + private void performSignedChatCommand(final ServerboundChatCommandSignedPacket packet, final LastSeenMessages lastSeenMessages) { + // CraftBukkit start -+ String command = "/" + packet.command(); ++ String commandString = "/" + packet.command(); + if (org.spigotmc.SpigotConfig.logCommands) { // Paper - Add missing SpigotConfig logCommands check -+ LOGGER.info("{} issued server command: {}", this.player.getScoreboardName(), command); ++ LOGGER.info("{} issued server command: {}", this.player.getScoreboardName(), commandString); + } // Paper - Add missing SpigotConfig logCommands check + -+ PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(this.getCraftPlayer(), command, new org.bukkit.craftbukkit.util.LazyPlayerSet(this.server)); ++ PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(this.getCraftPlayer(), commandString, new org.bukkit.craftbukkit.util.LazyPlayerSet(this.server)); + this.cserver.getPluginManager().callEvent(event); -+ command = event.getMessage().substring(1); ++ commandString = event.getMessage().substring(1); + - ParseResults parseResults = this.parseCommand(packet.command()); + ParseResults command = this.parseCommand(packet.command()); - Map map; + Map signedArguments; try { + // Paper - Always parse the original command to add to the chat chain - map = this.collectSignedArguments(packet, SignableCommand.of(parseResults), lastSeenMessages); + signedArguments = this.collectSignedArguments(packet, SignableCommand.of(command), lastSeenMessages); } catch (SignedMessageChain.DecodeException var6) { this.handleMessageDecodeFailure(var6); return; @@ -1554,48 +1548,46 @@ + } + + // Remove signed parts if the command was changed -+ if (!command.equals(packet.command())) { -+ parseResults = this.parseCommand(command); -+ map = Collections.emptyMap(); ++ if (!commandString.equals(packet.command())) { ++ command = this.parseCommand(commandString); ++ signedArguments = Collections.emptyMap(); + } + // Paper end - Fix cancellation and message changing + - CommandSigningContext commandSigningContext = new CommandSigningContext.SignedArguments(map); - parseResults = Commands.mapSource( - parseResults, commandSourceStack -> commandSourceStack.withSigningContext(commandSigningContext, this.chatMessageChain) - ); -- this.server.getCommands().performCommand(parseResults, packet.command()); -+ this.server.getCommands().performCommand(parseResults, command); // CraftBukkit + CommandSigningContext signingContext = new CommandSigningContext.SignedArguments(signedArguments); + command = Commands.mapSource(command, source -> source.withSigningContext(signingContext, this.chatMessageChain)); +- this.server.getCommands().performCommand(command, packet.command()); ++ this.server.getCommands().performCommand(command, commandString); // CraftBukkit } - private void handleMessageDecodeFailure(SignedMessageChain.DecodeException exception) { -@@ -1567,14 +_,20 @@ - return dispatcher.parse(command, this.player.createCommandSourceStack()); + private void handleMessageDecodeFailure(final SignedMessageChain.DecodeException e) { +@@ -1623,14 +_,20 @@ + return commands.parse(command, this.player.createCommandSourceStack()); } -- private void tryHandleChat(String message, boolean bypassHiddenChat, Runnable handler) { -+ private void tryHandleChat(String message, boolean bypassHiddenChat, Runnable handler, boolean sync) { // CraftBukkit +- private void tryHandleChat(final String message, final boolean isCommand, final Runnable chatHandler) { ++ private void tryHandleChat(final String message, final boolean isCommand, final Runnable chatHandler, final boolean sync) { // CraftBukkit if (isChatMessageIllegal(message)) { - this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters")); -- } else if (!bypassHiddenChat && this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { +- } else if (!isCommand && this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { + this.disconnectAsync(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - add proper async disconnect -+ } else if (this.player.isRemoved() || (!bypassHiddenChat && this.player.getChatVisibility() == ChatVisiblity.HIDDEN)) { // CraftBukkit - dead men tell no tales ++ } else if (this.player.isRemoved() || (!isCommand && this.player.getChatVisibility() == ChatVisiblity.HIDDEN)) { // CraftBukkit - dead men tell no tales this.send(new ClientboundSystemChatPacket(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED), false)); } else { this.player.resetLastActionTime(); -- this.server.execute(handler); +- this.server.execute(chatHandler); + // CraftBukkit start + if (sync) { -+ this.server.execute(handler); ++ this.server.execute(chatHandler); + } else { -+ handler.run(); ++ chatHandler.run(); + } + // CraftBukkit end } } -@@ -1586,7 +_,7 @@ - var10000 = Optional.of(lastSeenMessages); +@@ -1642,7 +_,7 @@ + var10000 = Optional.of(result); } catch (LastSeenMessagesValidator.ValidationException var5) { LOGGER.error("Failed to validate message acknowledgements from {}: {}", this.player.getPlainTextName(), var5.getMessage()); - this.disconnect(CHAT_VALIDATION_FAILED); @@ -1603,7 +1595,7 @@ return Optional.empty(); } -@@ -1604,22 +_,81 @@ +@@ -1660,22 +_,81 @@ return false; } @@ -1642,12 +1634,12 @@ + } + // CraftBukkit end + - private PlayerChatMessage getSignedMessage(ServerboundChatPacket packet, LastSeenMessages lastSeenMessages) throws SignedMessageChain.DecodeException { - SignedMessageBody signedMessageBody = new SignedMessageBody(packet.message(), packet.timeStamp(), packet.salt(), lastSeenMessages); - return this.signedMessageDecoder.unpack(packet.signature(), signedMessageBody); + private PlayerChatMessage getSignedMessage(final ServerboundChatPacket packet, final LastSeenMessages lastSeenMessages) throws SignedMessageChain.DecodeException { + SignedMessageBody body = new SignedMessageBody(packet.message(), packet.timeStamp(), packet.salt(), lastSeenMessages); + return this.signedMessageDecoder.unpack(packet.signature(), body); } - private void broadcastChatMessage(PlayerChatMessage message) { + private void broadcastChatMessage(final PlayerChatMessage message) { - this.server.getPlayerList().broadcastChatMessage(message, this.player, ChatType.bind(ChatType.CHAT, this.player)); - this.detectRateSpam(); + // CraftBukkit start @@ -1691,7 +1683,7 @@ } } -@@ -1630,7 +_,7 @@ +@@ -1686,7 +_,7 @@ this.lastSeenMessages.applyOffset(packet.offset()); } catch (LastSeenMessagesValidator.ValidationException var5) { LOGGER.error("Failed to validate message acknowledgement offset from {}: {}", this.player.getPlainTextName(), var5.getMessage()); @@ -1700,24 +1692,18 @@ } } } -@@ -1638,7 +_,40 @@ +@@ -1694,7 +_,34 @@ @Override - public void handleAnimate(ServerboundSwingPacket packet) { + public void handleAnimate(final ServerboundSwingPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + if (this.player.isImmobile()) return; // CraftBukkit this.player.resetLastActionTime(); + // CraftBukkit start - Raytrace to look for 'rogue armswings' -+ float f1 = this.player.getXRot(); -+ float f2 = this.player.getYRot(); -+ double d0 = this.player.getX(); -+ double d1 = this.player.getY() + (double) this.player.getEyeHeight(); -+ double d2 = this.player.getZ(); -+ Location origin = new Location(this.player.level().getWorld(), d0, d1, d2, f2, f1); -+ -+ double d3 = Math.max(this.player.blockInteractionRange(), this.player.entityInteractionRange()); ++ Location origin = CraftLocation.toBukkit(this.player.getEyePosition(), this.player.level(), this.player.getYRot(), this.player.getXRot()); ++ double maxRange = Math.max(this.player.blockInteractionRange(), this.player.entityInteractionRange()); // todo flawed + // SPIGOT-5607: Only call interact event if no block or entity is being clicked. Use bukkit ray trace method, because it handles blocks and entities at the same time + // SPIGOT-7429: Make sure to call PlayerInteractEvent for spectators and non-pickable entities -+ org.bukkit.util.RayTraceResult result = this.player.level().getWorld().rayTrace(origin, origin.getDirection(), d3, org.bukkit.FluidCollisionMode.NEVER, false, 0.0, entity -> { // Paper - Call interact event; change raySize from 0.1 to 0.0 ++ org.bukkit.util.RayTraceResult result = this.player.level().getWorld().rayTrace(origin, origin.getDirection(), maxRange, org.bukkit.FluidCollisionMode.NEVER, false, 0.0, entity -> { // Paper - Call interact event; change raySize from 0.1 to 0.0 + Entity handle = ((CraftEntity) entity).getHandle(); + return entity != this.player.getBukkitEntity() && this.player.getBukkitEntity().canSee(entity) && !handle.isSpectator() && handle.isPickable() && !handle.isPassengerOfSameVehicle(this.player); + }); @@ -1741,8 +1727,8 @@ this.player.swing(packet.getHand()); } -@@ -1646,6 +_,21 @@ - public void handlePlayerCommand(ServerboundPlayerCommandPacket packet) { +@@ -1702,6 +_,21 @@ + public void handlePlayerCommand(final ServerboundPlayerCommandPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); if (this.hasClientLoaded()) { + // CraftBukkit start @@ -1763,13 +1749,13 @@ this.player.resetLastActionTime(); switch (packet.getAction()) { case START_SPRINTING: -@@ -1690,6 +_,14 @@ +@@ -1746,6 +_,14 @@ } - public void sendPlayerChatMessage(PlayerChatMessage chatMessage, ChatType.Bound boundChatType) { + public void sendPlayerChatMessage(final PlayerChatMessage message, final ChatType.Bound chatType) { + // CraftBukkit start - SPIGOT-7262: if hidden we have to send as disguised message. Query whether we should send at all (but changing this may not be expected). -+ if (!this.getCraftPlayer().canSeePlayer(chatMessage.link().sender())) { -+ this.sendDisguisedChatMessage(chatMessage.decoratedContent(), boundChatType); ++ if (!this.getCraftPlayer().canSeePlayer(message.link().sender())) { ++ this.sendDisguisedChatMessage(message.decoratedContent(), chatType); + return; + } + // CraftBukkit end @@ -1778,10 +1764,10 @@ this.send( new ClientboundPlayerChatPacket( this.nextChatIndex++, -@@ -1712,9 +_,11 @@ +@@ -1768,9 +_,11 @@ } - if (i > 4096) { + if (trackedCount > 4096) { - this.disconnect(Component.translatable("multiplayer.disconnect.too_many_pending_chats")); + this.disconnectAsync(Component.translatable("multiplayer.disconnect.too_many_pending_chats"), org.bukkit.event.player.PlayerKickEvent.Cause.TOO_MANY_PENDING_CHATS); // Paper - kick event cause & add proper async disconnect } @@ -1790,8 +1776,8 @@ + // Paper end - Ensure that client receives chat packets in the same order that we add into the message signature cache } - public void sendDisguisedChatMessage(Component message, ChatType.Bound boundChatType) { -@@ -1725,6 +_,17 @@ + public void sendDisguisedChatMessage(final Component content, final ChatType.Bound chatType) { +@@ -1781,6 +_,17 @@ return this.connection.getRemoteAddress(); } @@ -1809,14 +1795,14 @@ public void switchToConfig() { this.waitingForSwitchToConfig = true; this.removePlayerFromWorld(); -@@ -1740,9 +_,16 @@ +@@ -1796,20 +_,27 @@ @Override - public void handleInteract(ServerboundInteractPacket packet) { + public void handleAttack(final ServerboundAttackPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + if (this.player.isImmobile()) return; // CraftBukkit if (this.hasClientLoaded()) { - final ServerLevel serverLevel = this.player.level(); - final Entity target = packet.getTarget(serverLevel); + ServerLevel level = this.player.level(); + Entity target = level.getEntityOrPart(packet.entityId()); + // Spigot start + if (target == this.player && !this.player.isSpectator()) { + this.disconnect(Component.literal("Cannot interact with self!"), org.bukkit.event.player.PlayerKickEvent.Cause.SELF_INTERACTION); // Paper - kick event cause @@ -1824,147 +1810,134 @@ + } + // Spigot end this.player.resetLastActionTime(); - this.player.setShiftKeyDown(packet.isUsingSecondaryAction()); - if (target != null) { -@@ -1751,16 +_,63 @@ + if (target != null && level.getWorldBorder().isWithinBounds(target.blockPosition())) { + AABB targetBounds = target.getBoundingBox(); + ItemStack mainHandItem = this.player.getMainHandItem(); +- if (this.player.isWithinAttackRange(mainHandItem, targetBounds, 3.0)) { ++ if (this.player.isWithinAttackRange(mainHandItem, targetBounds, io.papermc.paper.configuration.GlobalConfiguration.get().misc.clientInteractionLeniencyDistance.or(3.0))) { // Paper - configurable lenience + if (!mainHandItem.has(DataComponents.PIERCING_WEAPON)) { + if (target instanceof ItemEntity + || target instanceof ExperienceOrb + || target == this.player + || target instanceof AbstractArrow abstractArrow && !abstractArrow.isAttackable()) { +- this.disconnect(Component.translatable("multiplayer.disconnect.invalid_entity_attacked")); ++ this.disconnect(Component.translatable("multiplayer.disconnect.invalid_entity_attacked"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_ENTITY_ATTACKED); // Paper - add cause + LOGGER.warn("Player {} tried to attack an invalid entity", this.player.getPlainTextName()); + } else if (mainHandItem.isItemEnabled(level.enabledFeatures())) { + if (!this.player.cannotAttackWithItem(mainHandItem, 5)) { +@@ -1819,25 +_,88 @@ + } } + } ++ // Paper start - PlayerUseUnknownEntityEvent ++ else if (target == null) { ++ CraftEventFactory.callPlayerUseUnknownEntityEvent(ServerGamePacketListenerImpl.this.player, packet.entityId(), true, net.minecraft.world.InteractionHand.MAIN_HAND, null); ++ } ++ // Paper end - PlayerUseUnknownEntityEvent ++ if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes. + } + } - AABB boundingBox = target.getBoundingBox(); -- if (packet.isWithinRange(this.player, boundingBox, 3.0)) { -+ if (packet.isWithinRange(this.player, boundingBox, io.papermc.paper.configuration.GlobalConfiguration.get().misc.clientInteractionLeniencyDistance.or(3.0))) { // Paper - configurable lenience value for interact range - packet.dispatch( - new ServerboundInteractPacket.Handler() { -- private void performInteraction(InteractionHand hand, ServerGamePacketListenerImpl.EntityInteraction entityInteraction) { -+ private void performInteraction(InteractionHand hand, ServerGamePacketListenerImpl.EntityInteraction entityInteraction, PlayerInteractEntityEvent event) { // CraftBukkit - ItemStack itemInHand = ServerGamePacketListenerImpl.this.player.getItemInHand(hand); - if (itemInHand.isItemEnabled(serverLevel.enabledFeatures())) { - ItemStack itemStack = itemInHand.copy(); -- if (entityInteraction.run(ServerGamePacketListenerImpl.this.player, target, hand) instanceof InteractionResult.Success success -- ) -- { -+ // CraftBukkit start -+ final Item itemType = itemInHand.getItem(); -+ boolean triggerLeashUpdate = itemStack.is(Items.LEAD) && target instanceof net.minecraft.world.entity.Leashable; -+ -+ ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event); -+ final boolean resendData = event.isCancelled() || !ServerGamePacketListenerImpl.this.player.getItemInHand(hand).is(itemType); -+ -+ // Entity in bucket - SPIGOT-4048 and SPIGOT-6859 -+ if (itemType == Items.WATER_BUCKET && target instanceof net.minecraft.world.entity.animal.Bucketable && target instanceof LivingEntity && resendData) { -+ target.resendPossiblyDesyncedEntityData(ServerGamePacketListenerImpl.this.player); // Paper - The entire mob gets deleted, so resend it -+ } + @Override + public void handleInteract(final ServerboundInteractPacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); ++ if (this.player.isImmobile()) return; // CraftBukkit + if (this.hasClientLoaded()) { + ServerLevel level = this.player.level(); + Entity target = level.getEntityOrPart(packet.entityId()); ++ // Spigot start ++ if (target == this.player && !this.player.isSpectator()) { ++ this.disconnect(Component.literal("Cannot interact with self!"), org.bukkit.event.player.PlayerKickEvent.Cause.SELF_INTERACTION); // Paper - kick event cause ++ return; ++ } ++ // Spigot end + this.player.resetLastActionTime(); + this.player.setShiftKeyDown(packet.usingSecondaryAction()); + if (target != null && level.getWorldBorder().isWithinBounds(target.blockPosition())) { + AABB targetBounds = target.getBoundingBox(); +- if (this.player.isWithinEntityInteractionRange(targetBounds, 3.0)) { ++ if (this.player.isWithinEntityInteractionRange(targetBounds, io.papermc.paper.configuration.GlobalConfiguration.get().misc.clientInteractionLeniencyDistance.or(3.0))) { // Paper - configurable lenience value for interact range + InteractionHand hand = packet.hand(); + Vec3 location = packet.location(); + ItemStack tool = this.player.getItemInHand(hand); + if (tool.isItemEnabled(level.enabledFeatures())) { + ItemStack usedItemStack = tool.copy(); ++ // CraftBukkit start ++ final Item itemType = tool.getItem(); ++ boolean triggerLeashUpdate = usedItemStack.is(Items.LEAD) && target instanceof net.minecraft.world.entity.Leashable; ++ ++ final PlayerInteractAtEntityEvent event = new PlayerInteractAtEntityEvent( ++ ServerGamePacketListenerImpl.this.getCraftPlayer(), target.getBukkitEntity(), ++ org.bukkit.craftbukkit.util.CraftVector.toBukkit(location), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand) ++ ); ++ ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event); ++ final boolean resendData = event.isCancelled() || !ServerGamePacketListenerImpl.this.player.getItemInHand(hand).is(itemType); ++ ++ // Entity in bucket - SPIGOT-4048 and SPIGOT-6859 ++ if (itemType == Items.WATER_BUCKET && target instanceof net.minecraft.world.entity.animal.Bucketable && target instanceof LivingEntity && resendData) { ++ target.resendPossiblyDesyncedEntityData(ServerGamePacketListenerImpl.this.player); // Paper - The entire mob gets deleted, so resend it ++ } + -+ // Paper start - Fix inventory desync -+ if (resendData) { -+ if (io.papermc.paper.util.MCUtil.clientPredictsInteraction(ServerGamePacketListenerImpl.this.player, target, itemInHand)) { -+ ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote(); -+ } else { -+ ServerGamePacketListenerImpl.this.player.containerMenu.forceHeldSlot(hand); -+ } -+ } -+ // Paper end - Fix inventory desync ++ // Paper start - Fix inventory desync ++ if (resendData) { ++ if (io.papermc.paper.util.MCUtil.clientPredictsInteraction(ServerGamePacketListenerImpl.this.player, target, tool)) { ++ ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote(); ++ } else { ++ ServerGamePacketListenerImpl.this.player.containerMenu.forceHeldSlot(hand); ++ } ++ } ++ // Paper end - Fix inventory desync + -+ if (triggerLeashUpdate && resendData) { -+ // Refresh the current leash state -+ ServerGamePacketListenerImpl.this.send(new net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket(target, ((net.minecraft.world.entity.Leashable) target).getLeashHolder())); -+ } ++ if (triggerLeashUpdate && resendData) { ++ // Refresh the current leash state ++ ServerGamePacketListenerImpl.this.send(new net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket(target, ((net.minecraft.world.entity.Leashable) target).getLeashHolder())); ++ } + -+ if (resendData) { -+ // Refresh the current entity metadata -+ target.refreshEntityData(ServerGamePacketListenerImpl.this.player); -+ // SPIGOT-7136 - Allays -+ if (target instanceof net.minecraft.world.entity.animal.allay.Allay || target instanceof net.minecraft.world.entity.animal.equine.AbstractHorse) { // Paper - Fix horse armor desync -+ ServerGamePacketListenerImpl.this.send(new net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket( -+ target.getId(), java.util.Arrays.stream(net.minecraft.world.entity.EquipmentSlot.values()) -+ .map((slot) -> com.mojang.datafixers.util.Pair.of(slot, ((LivingEntity) target).getItemBySlot(slot).copy())) -+ .collect(Collectors.toList()), true)); // Paper - sanitize -+ player.containerMenu.sendAllDataToRemote(); -+ } else { -+ ServerGamePacketListenerImpl.this.player.containerMenu.forceHeldSlot(hand); // Paper - fix slot desync (is this needed?) -+ } -+ } ++ if (resendData) { ++ // Refresh the current entity metadata ++ target.refreshEntityData(ServerGamePacketListenerImpl.this.player); ++ // SPIGOT-7136 - Allays ++ if (target instanceof net.minecraft.world.entity.animal.allay.Allay || target instanceof net.minecraft.world.entity.animal.equine.AbstractHorse) { // Paper - Fix horse armor desync ++ ServerGamePacketListenerImpl.this.send(new net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket( ++ target.getId(), java.util.Arrays.stream(net.minecraft.world.entity.EquipmentSlot.values()) ++ .map((slot) -> com.mojang.datafixers.util.Pair.of(slot, ((LivingEntity) target).getItemBySlot(slot).copy())) ++ .collect(Collectors.toList()), true)); // Paper - sanitize ++ player.containerMenu.sendAllDataToRemote(); ++ } else { ++ ServerGamePacketListenerImpl.this.player.containerMenu.forceHeldSlot(hand); // Paper - fix slot desync (is this needed?) ++ } ++ } + -+ if (event.isCancelled()) { -+ return; -+ } -+ // CraftBukkit end -+ InteractionResult result = entityInteraction.run(ServerGamePacketListenerImpl.this.player, target, hand); -+ -+ if (result instanceof InteractionResult.Success success // CraftBukkit -+ ) { - ItemStack itemStack1 = success.wasItemInteraction() ? itemStack : ItemStack.EMPTY; - CriteriaTriggers.PLAYER_INTERACTED_WITH_ENTITY.trigger(ServerGamePacketListenerImpl.this.player, itemStack1, target); - if (success.swingSource() == InteractionResult.SwingSource.SERVER) { -@@ -1772,13 +_,13 @@ - - @Override - public void onInteraction(InteractionHand hand) { -- this.performInteraction(hand, Player::interactOn); -+ this.performInteraction(hand, Player::interactOn, new PlayerInteractEntityEvent(ServerGamePacketListenerImpl.this.getCraftPlayer(), target.getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand))); // CraftBukkit - } - - @Override - public void onInteraction(InteractionHand hand, Vec3 interactionLocation) { - this.performInteraction( -- hand, (player, entity, interactionHand) -> entity.interactAt(player, interactionLocation, interactionHand) -+ hand, (player, entity, interactionHand) -> entity.interactAt(player, interactionLocation, interactionHand), new PlayerInteractAtEntityEvent(ServerGamePacketListenerImpl.this.getCraftPlayer(), target.getBukkitEntity(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(interactionLocation), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand)) // CraftBukkit - ); - } - -@@ -1786,16 +_,22 @@ - public void onAttack() { - if (!(target instanceof ItemEntity) - && !(target instanceof ExperienceOrb) -- && target != ServerGamePacketListenerImpl.this.player -+ && (target != ServerGamePacketListenerImpl.this.player || ServerGamePacketListenerImpl.this.player.isSpectator()) // CraftBukkit - && !(target instanceof AbstractArrow abstractArrow && !abstractArrow.isAttackable())) { - ItemStack itemInHand = ServerGamePacketListenerImpl.this.player.getItemInHand(InteractionHand.MAIN_HAND); - if (itemInHand.isItemEnabled(serverLevel.enabledFeatures())) { - if (!ServerGamePacketListenerImpl.this.player.cannotAttackWithItem(itemInHand, 5)) { -+ // Paper start - redirect attacks with piercing weapon -+ PiercingWeapon piercingWeapon = itemInHand.get(DataComponents.PIERCING_WEAPON); -+ if (piercingWeapon != null) { -+ piercingWeapon.attack(ServerGamePacketListenerImpl.this.player, EquipmentSlot.MAINHAND); -+ } else -+ // Paper end - redirect attacks with piercing weapon - ServerGamePacketListenerImpl.this.player.attack(target); - } - } - } else { -- ServerGamePacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.invalid_entity_attacked")); -+ ServerGamePacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.invalid_entity_attacked"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_ENTITY_ATTACKED); // Paper - add cause - ServerGamePacketListenerImpl.LOGGER - .warn("Player {} tried to attack an invalid entity", ServerGamePacketListenerImpl.this.player.getPlainTextName()); - } -@@ -1804,6 +_,27 @@ - ); ++ if (event.isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + if (this.player.interactOn(target, hand, location) instanceof InteractionResult.Success success) { + ItemStack awardedForStack = success.wasItemInteraction() ? usedItemStack : ItemStack.EMPTY; + CriteriaTriggers.PLAYER_INTERACTED_WITH_ENTITY.trigger(this.player, awardedForStack, target); +@@ -1848,6 +_,12 @@ + } } } + // Paper start - PlayerUseUnknownEntityEvent -+ else { -+ packet.dispatch(new net.minecraft.network.protocol.game.ServerboundInteractPacket.Handler() { -+ @Override -+ public void onInteraction(net.minecraft.world.InteractionHand hand) { -+ CraftEventFactory.callPlayerUseUnknownEntityEvent(ServerGamePacketListenerImpl.this.player, packet, hand, null); -+ } -+ -+ @Override -+ public void onInteraction(net.minecraft.world.InteractionHand hand, net.minecraft.world.phys.Vec3 pos) { -+ CraftEventFactory.callPlayerUseUnknownEntityEvent(ServerGamePacketListenerImpl.this.player, packet, hand, pos); -+ } -+ -+ @Override -+ public void onAttack() { -+ CraftEventFactory.callPlayerUseUnknownEntityEvent(ServerGamePacketListenerImpl.this.player, packet, net.minecraft.world.InteractionHand.MAIN_HAND, null); -+ } -+ }); ++ else if (target == null) { ++ CraftEventFactory.callPlayerUseUnknownEntityEvent(ServerGamePacketListenerImpl.this.player, packet.entityId(), false, packet.hand(), packet.location()); + } + // Paper end - PlayerUseUnknownEntityEvent + if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes. } } -@@ -1816,7 +_,7 @@ +@@ -1859,7 +_,7 @@ + ServerLevel level = this.player.level(); + Entity target = level.getEntityOrPart(packet.entityId()); + if (target != null && level.getWorldBorder().isWithinBounds(target.blockPosition())) { +- if (this.player.isWithinEntityInteractionRange(target.getBoundingBox(), 3.0)) { ++ if (this.player.isWithinEntityInteractionRange(target.getBoundingBox(), io.papermc.paper.configuration.GlobalConfiguration.get().misc.clientInteractionLeniencyDistance.or(3.0))) { // Paper - configurable lenience + if (target.isPickable()) { + this.player.setCamera(target); + } +@@ -1877,7 +_,7 @@ case PERFORM_RESPAWN: if (this.player.wonGame) { this.player.wonGame = false; @@ -1973,7 +1946,7 @@ this.resetPosition(); this.restartClientLoadTimerAfterRespawn(); CriteriaTriggers.CHANGED_DIMENSION.trigger(this.player, Level.END, Level.OVERWORLD); -@@ -1825,12 +_,12 @@ +@@ -1886,12 +_,12 @@ return; } @@ -1989,10 +1962,19 @@ } } break; -@@ -1841,16 +_,27 @@ +@@ -1904,7 +_,7 @@ + } + + private void sendGameRuleValues() { +- if (!this.player.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER)) { ++ if (!this.player.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER) && !this.player.getBukkitEntity().hasPermission("minecraft.command.gamerule")) { // Paper - add permission check + LOGGER.warn("Player {} tried to request game rule values without required permissions", this.player.getGameProfile().name()); + } else { + GameRules gameRules = this.player.level().getGameRules(); +@@ -1920,16 +_,27 @@ @Override - public void handleContainerClose(ServerboundContainerClosePacket packet) { + public void handleContainerClose(final ServerboundContainerClosePacket packet) { + // Paper start - Inventory close reason + this.handleContainerClose(packet, org.bukkit.event.inventory.InventoryCloseEvent.Reason.PLAYER); + } @@ -2007,7 +1989,7 @@ } @Override - public void handleContainerClick(ServerboundContainerClickPacket packet) { + public void handleContainerClick(final ServerboundContainerClickPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + if (this.player.isImmobile()) return; // CraftBukkit this.player.resetLastActionTime(); @@ -2019,24 +2001,24 @@ this.player.containerMenu.sendAllDataToRemote(); } else if (!this.player.containerMenu.stillValid(this.player)) { LOGGER.debug("Player {} interacted with invalid menu {}", this.player, this.player.containerMenu); -@@ -1866,7 +_,298 @@ +@@ -1945,7 +_,298 @@ } else { - boolean flag = packet.stateId() != this.player.containerMenu.getStateId(); + boolean fullResyncNeeded = packet.stateId() != this.player.containerMenu.getStateId(); this.player.containerMenu.suppressRemoteUpdates(); -- this.player.containerMenu.clicked(slotNum, packet.buttonNum(), packet.clickType(), this.player); +- this.player.containerMenu.clicked(slotIndex, packet.buttonNum(), packet.containerInput(), this.player); + // CraftBukkit start - Call InventoryClickEvent -+ if (slotNum < -1 && slotNum != net.minecraft.world.inventory.AbstractContainerMenu.SLOT_CLICKED_OUTSIDE) { ++ if (slotIndex < -1 && slotIndex != net.minecraft.world.inventory.AbstractContainerMenu.SLOT_CLICKED_OUTSIDE) { + return; + } + + org.bukkit.inventory.InventoryView inventory = this.player.containerMenu.getBukkitView(); -+ SlotType type = inventory.getSlotType(slotNum); ++ SlotType type = inventory.getSlotType(slotIndex); + + InventoryClickEvent event; + ClickType click = ClickType.UNKNOWN; + InventoryAction action = InventoryAction.UNKNOWN; + -+ switch (packet.clickType()) { ++ switch (packet.containerInput()) { + case PICKUP: + if (packet.buttonNum() == 0) { + click = ClickType.LEFT; @@ -2045,14 +2027,14 @@ + } + if (packet.buttonNum() == 0 || packet.buttonNum() == 1) { + action = InventoryAction.NOTHING; // Don't want to repeat ourselves -+ if (slotNum == net.minecraft.world.inventory.AbstractContainerMenu.SLOT_CLICKED_OUTSIDE) { ++ if (slotIndex == net.minecraft.world.inventory.AbstractContainerMenu.SLOT_CLICKED_OUTSIDE) { + if (!this.player.containerMenu.getCarried().isEmpty()) { + action = packet.buttonNum() == 0 ? InventoryAction.DROP_ALL_CURSOR : InventoryAction.DROP_ONE_CURSOR; + } -+ } else if (slotNum < 0) { ++ } else if (slotIndex < 0) { + action = InventoryAction.NOTHING; + } else { -+ Slot slot = this.player.containerMenu.getSlot(slotNum); ++ Slot slot = this.player.containerMenu.getSlot(slotIndex); + if (slot != null) { + ItemStack clickedItem = slot.getItem(); + ItemStack cursor = this.player.containerMenu.getCarried(); @@ -2129,10 +2111,10 @@ + click = ClickType.SHIFT_RIGHT; + } + if (packet.buttonNum() == 0 || packet.buttonNum() == 1) { -+ if (slotNum < 0) { ++ if (slotIndex < 0) { + action = InventoryAction.NOTHING; + } else { -+ Slot slot = this.player.containerMenu.getSlot(slotNum); ++ Slot slot = this.player.containerMenu.getSlot(slotIndex); + if (slot != null && slot.mayPickup(this.player) && slot.hasItem()) { + action = InventoryAction.MOVE_TO_OTHER_INVENTORY; + } else { @@ -2144,13 +2126,13 @@ + case SWAP: + if ((packet.buttonNum() >= 0 && packet.buttonNum() < 9) || packet.buttonNum() == Inventory.SLOT_OFFHAND) { + // Paper start - Add slot sanity checks to container clicks -+ if (slotNum < 0) { ++ if (slotIndex < 0) { + action = InventoryAction.NOTHING; + break; + } + // Paper end - Add slot sanity checks to container clicks + click = (packet.buttonNum() == Inventory.SLOT_OFFHAND) ? ClickType.SWAP_OFFHAND : ClickType.NUMBER_KEY; -+ Slot clickedSlot = this.player.containerMenu.getSlot(slotNum); ++ Slot clickedSlot = this.player.containerMenu.getSlot(slotIndex); + if (clickedSlot.mayPickup(this.player)) { + ItemStack hotbar = this.player.getInventory().getItem(packet.buttonNum()); + if ((!hotbar.isEmpty() && clickedSlot.mayPlace(hotbar)) || (hotbar.isEmpty() && clickedSlot.hasItem())) { // Paper - modernify this logic (no such thing as a "hotbar move and readd" @@ -2166,10 +2148,10 @@ + case CLONE: + if (packet.buttonNum() == 2) { + click = ClickType.MIDDLE; -+ if (slotNum < 0) { ++ if (slotIndex < 0) { + action = InventoryAction.NOTHING; + } else { -+ Slot slot = this.player.containerMenu.getSlot(slotNum); ++ Slot slot = this.player.containerMenu.getSlot(slotIndex); + if (slot != null && slot.hasItem() && this.player.getAbilities().instabuild && this.player.containerMenu.getCarried().isEmpty()) { + action = InventoryAction.CLONE_STACK; + } else { @@ -2182,10 +2164,10 @@ + } + break; + case THROW: -+ if (slotNum >= 0) { ++ if (slotIndex >= 0) { + if (packet.buttonNum() == 0) { + click = ClickType.DROP; -+ Slot slot = this.player.containerMenu.getSlot(slotNum); ++ Slot slot = this.player.containerMenu.getSlot(slotIndex); + if (slot != null && slot.hasItem() && slot.mayPickup(this.player) && !slot.getItem().isEmpty() && slot.getItem().getItem() != Items.AIR) { + action = InventoryAction.DROP_ONE_SLOT; + } else { @@ -2193,7 +2175,7 @@ + } + } else if (packet.buttonNum() == 1) { + click = ClickType.CONTROL_DROP; -+ Slot slot = this.player.containerMenu.getSlot(slotNum); ++ Slot slot = this.player.containerMenu.getSlot(slotIndex); + if (slot != null && slot.hasItem() && slot.mayPickup(this.player) && !slot.getItem().isEmpty() && slot.getItem().getItem() != Items.AIR) { + action = InventoryAction.DROP_ALL_SLOT; + } else { @@ -2223,18 +2205,18 @@ + if (this.player.containerMenu.quickcraftSlots.size() == 1) { + int index = containerMenu.quickcraftSlots.iterator().next().index; + containerMenu.resetQuickCraft(); -+ this.handleContainerClick(new ServerboundContainerClickPacket(packet.containerId(), packet.stateId(), (short) index, (byte) containerMenu.quickcraftType, net.minecraft.world.inventory.ClickType.PICKUP, packet.changedSlots(), packet.carriedItem())); ++ this.handleContainerClick(new ServerboundContainerClickPacket(packet.containerId(), packet.stateId(), (short) index, (byte) containerMenu.quickcraftType, net.minecraft.world.inventory.ContainerInput.PICKUP, packet.changedSlots(), packet.carriedItem())); + return; + } + } + } + // Paper end - Fix CraftBukkit drag system -+ this.player.containerMenu.clicked(slotNum, packet.buttonNum(), packet.clickType(), this.player); ++ this.player.containerMenu.clicked(slotIndex, packet.buttonNum(), packet.containerInput(), this.player); + break; + case PICKUP_ALL: + click = ClickType.DOUBLE_CLICK; + action = InventoryAction.NOTHING; -+ if (slotNum >= 0 && !this.player.containerMenu.getCarried().isEmpty()) { ++ if (slotIndex >= 0 && !this.player.containerMenu.getCarried().isEmpty()) { + ItemStack cursor = this.player.containerMenu.getCarried(); + action = InventoryAction.NOTHING; + // Quick check for if we have any of the item @@ -2247,7 +2229,7 @@ + break; + } + -+ if (packet.clickType() != net.minecraft.world.inventory.ClickType.QUICK_CRAFT) { ++ if (packet.containerInput() != net.minecraft.world.inventory.ContainerInput.QUICK_CRAFT) { + // If a non quick craft packet is emitted by the client while in quick crafting, the server will reset quick crafting and + // discard any further packet action. + if (this.player.containerMenu.quickcraftStatus != 0) { @@ -2255,42 +2237,42 @@ + } + + if (click == ClickType.NUMBER_KEY) { -+ event = new InventoryClickEvent(inventory, type, slotNum, click, action, packet.buttonNum()); ++ event = new InventoryClickEvent(inventory, type, slotIndex, click, action, packet.buttonNum()); + } else { -+ event = new InventoryClickEvent(inventory, type, slotNum, click, action); ++ event = new InventoryClickEvent(inventory, type, slotIndex, click, action); + } + + org.bukkit.inventory.Inventory top = inventory.getTopInventory(); -+ if (slotNum == 0 && top instanceof final org.bukkit.inventory.CraftingInventory craftingInv) { ++ if (slotIndex == 0 && top instanceof final org.bukkit.inventory.CraftingInventory craftingInv) { + org.bukkit.inventory.Recipe recipe = craftingInv.getRecipe(); + if (recipe != null) { + if (click == ClickType.NUMBER_KEY) { -+ event = new CraftItemEvent(recipe, inventory, type, slotNum, click, action, packet.buttonNum()); ++ event = new CraftItemEvent(recipe, inventory, type, slotIndex, click, action, packet.buttonNum()); + } else { -+ event = new CraftItemEvent(recipe, inventory, type, slotNum, click, action); ++ event = new CraftItemEvent(recipe, inventory, type, slotIndex, click, action); + } + } + } + -+ if (slotNum == 3 && top instanceof final org.bukkit.inventory.SmithingInventory smithingInv) { ++ if (slotIndex == 3 && top instanceof final org.bukkit.inventory.SmithingInventory smithingInv) { + org.bukkit.inventory.ItemStack result = smithingInv.getResult(); + if (result != null) { + if (click == ClickType.NUMBER_KEY) { -+ event = new SmithItemEvent(inventory, type, slotNum, click, action, packet.buttonNum()); ++ event = new SmithItemEvent(inventory, type, slotIndex, click, action, packet.buttonNum()); + } else { -+ event = new SmithItemEvent(inventory, type, slotNum, click, action); ++ event = new SmithItemEvent(inventory, type, slotIndex, click, action); + } + } + } + + // Paper start - cartography item event -+ if (slotNum == net.minecraft.world.inventory.CartographyTableMenu.RESULT_SLOT && top instanceof org.bukkit.inventory.CartographyInventory cartographyInventory) { ++ if (slotIndex == net.minecraft.world.inventory.CartographyTableMenu.RESULT_SLOT && top instanceof org.bukkit.inventory.CartographyInventory cartographyInventory) { + org.bukkit.inventory.ItemStack result = cartographyInventory.getResult(); + if (result != null && !result.isEmpty()) { + if (click == ClickType.NUMBER_KEY) { -+ event = new io.papermc.paper.event.player.CartographyItemEvent(inventory, type, slotNum, click, action, packet.buttonNum()); ++ event = new io.papermc.paper.event.player.CartographyItemEvent(inventory, type, slotIndex, click, action, packet.buttonNum()); + } else { -+ event = new io.papermc.paper.event.player.CartographyItemEvent(inventory, type, slotNum, click, action); ++ event = new io.papermc.paper.event.player.CartographyItemEvent(inventory, type, slotIndex, click, action); + } + } + } @@ -2306,7 +2288,7 @@ + } + + if (event.getResult() != org.bukkit.event.Event.Result.DENY) { // if denied, synchronizing is fully handled by broadcastChanges at the end -+ this.player.containerMenu.clicked(slotNum, packet.buttonNum(), packet.clickType(), this.player); ++ this.player.containerMenu.clicked(slotIndex, packet.buttonNum(), packet.containerInput(), this.player); + } + + if (event instanceof CraftItemEvent || event instanceof SmithItemEvent) { @@ -2317,9 +2299,9 @@ + } + // CraftBukkit end - for (Entry entry : Int2ObjectMaps.fastIterable(packet.changedSlots())) { - this.player.containerMenu.setRemoteSlotUnsafe(entry.getIntKey(), entry.getValue()); -@@ -1879,6 +_,8 @@ + for (Entry e : Int2ObjectMaps.fastIterable(packet.changedSlots())) { + this.player.containerMenu.setRemoteSlotUnsafe(e.getIntKey(), e.getValue()); +@@ -1958,6 +_,8 @@ } else { this.player.containerMenu.broadcastChanges(); } @@ -2328,10 +2310,10 @@ } } } -@@ -1886,6 +_,14 @@ +@@ -1965,6 +_,14 @@ @Override - public void handlePlaceRecipe(ServerboundPlaceRecipePacket packet) { + public void handlePlaceRecipe(final ServerboundPlaceRecipePacket packet) { + // Paper start - auto recipe limit + if (!org.bukkit.Bukkit.isPrimaryThread()) { + if (!this.recipeSpamPackets.isIncrementAndUnderThreshold()) { @@ -2343,12 +2325,12 @@ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); this.player.resetLastActionTime(); if (!this.player.isSpectator() && this.player.containerMenu.containerId == packet.containerId()) { -@@ -1902,9 +_,44 @@ +@@ -1981,9 +_,44 @@ return; } + // Paper start - Add PlayerRecipeBookClickEvent -+ NamespacedKey recipeName = org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(recipeHolder.id().identifier()); ++ NamespacedKey recipeName = org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(recipe.id().identifier()); + boolean makeAll = packet.useMaxItems(); + com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent paperEvent = new com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent( + this.player.getBukkitEntity(), recipeName, makeAll @@ -2361,12 +2343,12 @@ + if (org.bukkit.event.player.PlayerRecipeBookClickEvent.getHandlerList().getRegisteredListeners().length > 0) { + // Paper end - Add PlayerRecipeBookClickEvent + // CraftBukkit start - implement PlayerRecipeBookClickEvent -+ org.bukkit.inventory.Recipe recipe = this.cserver.getRecipe(recipeName); // Paper - Add PlayerRecipeBookClickEvent - forward to legacy event -+ if (recipe == null) { ++ org.bukkit.inventory.Recipe bukkitRecipe = this.cserver.getRecipe(recipeName); // Paper - Add PlayerRecipeBookClickEvent - forward to legacy event ++ if (bukkitRecipe == null) { + return; + } + // Paper start - Add PlayerRecipeBookClickEvent - forward to legacy event -+ org.bukkit.event.player.PlayerRecipeBookClickEvent event = CraftEventFactory.callRecipeBookClickEvent(this.player, recipe, makeAll); ++ org.bukkit.event.player.PlayerRecipeBookClickEvent event = CraftEventFactory.callRecipeBookClickEvent(this.player, bukkitRecipe, makeAll); + recipeName = ((org.bukkit.Keyed) event.getRecipe()).getKey(); + makeAll = event.isShiftClick(); + } @@ -2376,46 +2358,46 @@ + // Paper end - Add PlayerRecipeBookClickEvent - forward to legacy event + + // Cast to keyed should be safe as the recipe will never be a MerchantRecipe. -+ recipeHolder = this.server.getRecipeManager().byKey(net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.RECIPE, org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(recipeName))).orElse(null); // Paper - Add PlayerRecipeBookClickEvent - forward to legacy event -+ if (recipeHolder == null) { ++ recipe = this.server.getRecipeManager().byKey(net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.RECIPE, org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(recipeName))).orElse(null); // Paper - Add PlayerRecipeBookClickEvent - forward to legacy event ++ if (recipe == null) { + return; + } + RecipeBookMenu.PostPlaceAction postPlaceAction = recipeBookMenu.handlePlacement( -- packet.useMaxItems(), this.player.isCreative(), recipeHolder, this.player.level(), this.player.getInventory() -+ makeAll, this.player.isCreative(), recipeHolder, this.player.level(), this.player.getInventory() +- packet.useMaxItems(), this.player.isCreative(), recipe, this.player.level(), this.player.getInventory() ++ makeAll, this.player.isCreative(), recipe, this.player.level(), this.player.getInventory() ); + // CraftBukkit end if (postPlaceAction == RecipeBookMenu.PostPlaceAction.PLACE_GHOST_RECIPE) { - this.send(new ClientboundPlaceGhostRecipePacket(this.player.containerMenu.containerId, recipeFromDisplay.display().display())); + this.send(new ClientboundPlaceGhostRecipePacket(this.player.containerMenu.containerId, displayInfo.display().display())); } -@@ -1918,6 +_,7 @@ +@@ -1997,6 +_,7 @@ @Override - public void handleContainerButtonClick(ServerboundContainerButtonClickPacket packet) { + public void handleContainerButtonClick(final ServerboundContainerButtonClickPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + if (this.player.isImmobile()) return; // CraftBukkit this.player.resetLastActionTime(); if (this.player.containerMenu.containerId == packet.containerId() && !this.player.isSpectator()) { if (!this.player.containerMenu.stillValid(this.player)) { -@@ -1927,6 +_,7 @@ - if (flag) { +@@ -2006,6 +_,7 @@ + if (clickAccepted) { this.player.containerMenu.broadcastChanges(); } + if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes. } } } -@@ -1943,10 +_,48 @@ +@@ -2022,10 +_,48 @@ - boolean flag1 = packet.slotNum() >= 1 && packet.slotNum() <= 45; - boolean flag2 = itemStack.isEmpty() || itemStack.getCount() <= itemStack.getMaxStackSize(); -+ if (flag || (flag1 && !ItemStack.matches(this.player.inventoryMenu.getSlot(packet.slotNum()).getItem(), packet.itemStack()))) { // Insist on valid slot + boolean validSlot = packet.slotNum() >= 1 && packet.slotNum() <= 45; + boolean validData = itemStack.isEmpty() || itemStack.getCount() <= itemStack.getMaxStackSize(); ++ if (drop || (validSlot && !ItemStack.matches(this.player.inventoryMenu.getSlot(packet.slotNum()).getItem(), packet.itemStack()))) { // Insist on valid slot + // CraftBukkit start - Call click event + org.bukkit.inventory.InventoryView inventory = this.player.inventoryMenu.getBukkitView(); + org.bukkit.inventory.ItemStack item = CraftItemStack.asBukkitCopy(packet.itemStack()); + + SlotType type = SlotType.QUICKBAR; -+ if (flag) { ++ if (drop) { + type = SlotType.OUTSIDE; + } else if (packet.slotNum() < 36) { + if (packet.slotNum() >= 5 && packet.slotNum() < 9) { @@ -2424,7 +2406,7 @@ + type = SlotType.CONTAINER; + } + } -+ InventoryCreativeEvent event = new InventoryCreativeEvent(inventory, type, flag ? net.minecraft.world.inventory.AbstractContainerMenu.SLOT_CLICKED_OUTSIDE : packet.slotNum(), item); ++ InventoryCreativeEvent event = new InventoryCreativeEvent(inventory, type, drop ? net.minecraft.world.inventory.AbstractContainerMenu.SLOT_CLICKED_OUTSIDE : packet.slotNum(), item); + this.cserver.getPluginManager().callEvent(event); + + itemStack = CraftItemStack.asNMSCopy(event.getCursor()); @@ -2432,7 +2414,7 @@ + switch (event.getResult()) { + case ALLOW: + // Plugin cleared the id / stacksize checks -+ flag2 = true; ++ validData = true; + break; + case DEFAULT: + break; @@ -2446,57 +2428,57 @@ + } + } + // CraftBukkit end - if (flag1 && flag2) { + if (validSlot && validData) { this.player.inventoryMenu.getSlot(packet.slotNum()).setByPlayer(itemStack); this.player.inventoryMenu.setRemoteSlot(packet.slotNum(), itemStack); this.player.inventoryMenu.broadcastChanges(); + if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes. - } else if (flag && flag2) { + } else if (drop && validData) { if (this.dropSpamThrottler.isUnderThreshold()) { this.dropSpamThrottler.increment(); -@@ -1960,15 +_,38 @@ +@@ -2039,15 +_,38 @@ @Override - public void handleSignUpdate(ServerboundSignUpdatePacket packet) { -- List list = Stream.of(packet.getLines()).map(ChatFormatting::stripFormatting).collect(Collectors.toList()); + public void handleSignUpdate(final ServerboundSignUpdatePacket packet) { +- List lines = Stream.of(packet.getLines()).map(ChatFormatting::stripFormatting).collect(Collectors.toList()); + // Paper start - Limit client sign length -+ String[] lines = packet.getLines(); -+ for (int i = 0; i < lines.length; ++i) { -+ if (MAX_SIGN_LINE_LENGTH > 0 && lines[i].length() > MAX_SIGN_LINE_LENGTH) { ++ String[] linesArray = packet.getLines(); ++ for (int i = 0; i < linesArray.length; ++i) { ++ if (MAX_SIGN_LINE_LENGTH > 0 && linesArray[i].length() > MAX_SIGN_LINE_LENGTH) { + // This handles multibyte characters as 1 -+ int offset = lines[i].codePoints().limit(MAX_SIGN_LINE_LENGTH).map(Character::charCount).sum(); -+ if (offset < lines[i].length()) { -+ lines[i] = lines[i].substring(0, offset); // this will break any filtering, but filtering is NYI as of 1.17 ++ int offset = linesArray[i].codePoints().limit(MAX_SIGN_LINE_LENGTH).map(Character::charCount).sum(); ++ if (offset < linesArray[i].length()) { ++ linesArray[i] = linesArray[i].substring(0, offset); // this will break any filtering, but filtering is NYI as of 1.17 + } + } + } -+ List list = Stream.of(lines).map(ChatFormatting::stripFormatting).collect(Collectors.toList()); ++ List lines = Stream.of(linesArray).map(ChatFormatting::stripFormatting).collect(Collectors.toList()); + // Paper end - Limit client sign length - this.filterTextPacket(list).thenAcceptAsync(texts -> this.updateSignText(packet, (List)texts), this.server); + this.filterTextPacket(lines).thenAcceptAsync(filteredLines -> this.updateSignText(packet, (List)filteredLines), this.server); } - private void updateSignText(ServerboundSignUpdatePacket packet, List filteredText) { + private void updateSignText(final ServerboundSignUpdatePacket packet, final List lines) { + if (this.player.isImmobile()) return; // CraftBukkit this.player.resetLastActionTime(); - ServerLevel serverLevel = this.player.level(); + ServerLevel level = this.player.level(); BlockPos pos = packet.getPos(); - if (serverLevel.hasChunkAt(pos)) { + if (level.hasChunkAt(pos)) { + // Paper start - Add API for client-side signs + if (!new io.papermc.paper.event.packet.UncheckedSignChangeEvent( + this.player.getBukkitEntity(), + io.papermc.paper.util.MCUtil.toPosition(pos), + packet.isFrontText() ? org.bukkit.block.sign.Side.FRONT : org.bukkit.block.sign.Side.BACK, -+ filteredText.stream().map(line -> net.kyori.adventure.text.Component.text(line.raw())).toList()) ++ lines.stream().map(line -> net.kyori.adventure.text.Component.text(line.raw())).toList()) + .callEvent()) { + return; + } + // Paper end - Add API for client-side signs - if (!(serverLevel.getBlockEntity(pos) instanceof SignBlockEntity signBlockEntity)) { + if (!(level.getBlockEntity(pos) instanceof SignBlockEntity sign)) { return; } -@@ -1980,14 +_,32 @@ +@@ -2059,14 +_,32 @@ @Override - public void handlePlayerAbilities(ServerboundPlayerAbilitiesPacket packet) { + public void handlePlayerAbilities(final ServerboundPlayerAbilitiesPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); - this.player.getAbilities().flying = packet.isFlying() && this.player.getAbilities().mayfly; + // CraftBukkit start @@ -2513,7 +2495,7 @@ } @Override - public void handleClientInformation(ServerboundClientInformationPacket packet) { + public void handleClientInformation(final ServerboundClientInformationPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + // Paper start - do not accept invalid information + if (packet.information().viewDistance() < 0) { @@ -2522,13 +2504,13 @@ + return; + } + // Paper end - do not accept invalid information - boolean isModelPartShown = this.player.isModelPartShown(PlayerModelPart.HAT); + boolean wasHatShown = this.player.isModelPartShown(PlayerModelPart.HAT); this.player.updateOptions(packet.information()); + this.connection.channel.attr(io.papermc.paper.adventure.PaperAdventure.LOCALE_ATTRIBUTE).set(net.kyori.adventure.translation.Translator.parseLocale(packet.information().language())); // Paper - if (this.player.isModelPartShown(PlayerModelPart.HAT) != isModelPartShown) { + if (this.player.isModelPartShown(PlayerModelPart.HAT) != wasHatShown) { this.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT, this.player)); } -@@ -2003,7 +_,7 @@ +@@ -2082,7 +_,7 @@ packet.difficulty().getDisplayName() ); } else { @@ -2537,7 +2519,7 @@ } } -@@ -2017,7 +_,7 @@ +@@ -2096,7 +_,7 @@ packet.mode().getShortDisplayName().getString() ); } else { @@ -2546,18 +2528,18 @@ } } -@@ -2037,7 +_,7 @@ - ProfilePublicKey.Data data2 = data.profilePublicKey(); - if (!Objects.equals(data1, data2)) { - if (data1 != null && data2.expiresAt().isBefore(data1.expiresAt())) { +@@ -2116,7 +_,7 @@ + ProfilePublicKey.Data newProfileKey = newChatSession.profilePublicKey(); + if (!Objects.equals(oldProfileKey, newProfileKey)) { + if (oldProfileKey != null && newProfileKey.expiresAt().isBefore(oldProfileKey.expiresAt())) { - this.disconnect(ProfilePublicKey.EXPIRED_PROFILE_PUBLIC_KEY); + this.disconnect(ProfilePublicKey.EXPIRED_PROFILE_PUBLIC_KEY, org.bukkit.event.player.PlayerKickEvent.Cause.EXPIRED_PROFILE_PUBLIC_KEY); // Paper - kick event causes } else { try { - SignatureValidator signatureValidator = this.server.services().profileKeySignatureValidator(); -@@ -2048,8 +_,8 @@ + SignatureValidator profileKeySignatureValidator = this.server.services().profileKeySignatureValidator(); +@@ -2127,8 +_,8 @@ - this.resetPlayerChatState(data.validate(this.player.getGameProfile(), signatureValidator)); + this.resetPlayerChatState(newChatSession.validate(this.player.getGameProfile(), profileKeySignatureValidator)); } catch (ProfilePublicKey.ValidationException var6) { - LOGGER.error("Failed to validate profile key: {}", var6.getMessage()); - this.disconnect(var6.getComponent()); @@ -2566,7 +2548,7 @@ } } } -@@ -2060,11 +_,13 @@ +@@ -2139,11 +_,13 @@ if (!this.waitingForSwitchToConfig) { throw new IllegalStateException("Client acknowledged config, but none was requested"); } else { @@ -2581,16 +2563,16 @@ } } -@@ -2082,27 +_,32 @@ +@@ -2161,27 +_,32 @@ - private void resetPlayerChatState(RemoteChatSession chatSession) { + private void resetPlayerChatState(final RemoteChatSession chatSession) { this.chatSession = chatSession; + this.hasLoggedExpiry = false; // Paper - Prevent causing expired keys from impacting new joins this.signedMessageDecoder = chatSession.createMessageDecoder(this.player.getUUID()); this.chatMessageChain .append( () -> { -+ server.executeBlocking(() -> { // Paper - Broadcast chat session update sync ++ this.server.executeBlocking(() -> { // Paper - Broadcast chat session update sync this.player.setChatSession(chatSession); this.server .getPlayerList() @@ -2604,18 +2586,18 @@ } @Override - public void handleCustomPayload(ServerboundCustomPayloadPacket packet) { + public void handleCustomPayload(final ServerboundCustomPayloadPacket packet) { + super.handleCustomPayload(packet); // Paper } @Override - public void handleClientTickEnd(ServerboundClientTickEndPacket packet) { + public void handleClientTickEnd(final ServerboundClientTickEndPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); + this.tickEndEvent.callEvent(); // Paper - add client tick end event if (!this.receivedMovementThisTick) { this.player.setKnownMovement(Vec3.ZERO); } -@@ -2134,12 +_,23 @@ +@@ -2213,12 +_,23 @@ } public void tickClientLoadTimeout() { @@ -2641,9 +2623,9 @@ this.clientLoadedTimeoutTimer = 0; } -@@ -2156,4 +_,80 @@ - interface EntityInteraction { - InteractionResult run(ServerPlayer player, Entity entity, InteractionHand hand); +@@ -2230,4 +_,80 @@ + this.waitingForRespawn = false; + this.clientLoadedTimeoutTimer = 60; } + + // Paper start - Add fail move event diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java.patch index 54a47d3a4673..6d17de399e7b 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java.patch @@ -18,13 +18,13 @@ private final Connection connection; + - public ServerHandshakePacketListenerImpl(MinecraftServer server, Connection connection) { + public ServerHandshakePacketListenerImpl(final MinecraftServer server, final Connection connection) { this.server = server; this.connection = connection; @@ -24,6 +_,7 @@ @Override - public void handleIntention(ClientIntentionPacket packet) { + public void handleIntention(final ClientIntentionPacket packet) { + this.connection.hostname = packet.hostName() + ":" + packet.port(); // CraftBukkit - set hostname switch (packet.intention()) { case LOGIN: @@ -40,7 +40,8 @@ + // Paper end } - private void beginLogin(ClientIntentionPacket packet, boolean transferred) { +- private void beginLogin(final ClientIntentionPacket packet, final boolean transfer) { ++ private void beginLogin(ClientIntentionPacket packet, final boolean transfer) { // Paper - remove packet final this.connection.setupOutboundProtocol(LoginProtocols.CLIENTBOUND); + // Paper start - Connection throttle + try { @@ -74,22 +75,22 @@ + } + // Paper end - Connection throttle if (packet.protocolVersion() != SharedConstants.getCurrentVersion().protocolVersion()) { -- Component component; +- Component reason; - if (packet.protocolVersion() < 754) { -- component = Component.translatable("multiplayer.disconnect.outdated_client", SharedConstants.getCurrentVersion().name()); -+ net.kyori.adventure.text.Component adventureComponent; // Paper - Fix hex colors not working in some kick messages +- reason = Component.translatable("multiplayer.disconnect.outdated_client", SharedConstants.getCurrentVersion().name()); ++ net.kyori.adventure.text.Component adventureReason; // Paper - Fix hex colors not working in some kick messages + if (packet.protocolVersion() < SharedConstants.getCurrentVersion().protocolVersion()) { // Spigot - SPIGOT-7546: Handle version check correctly for outdated client message -+ adventureComponent = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(java.text.MessageFormat.format(org.spigotmc.SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), SharedConstants.getCurrentVersion().name())); // Spigot // Paper - Fix hex colors not working in some kick messages ++ adventureReason = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(java.text.MessageFormat.format(org.spigotmc.SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), SharedConstants.getCurrentVersion().name())); // Spigot // Paper - Fix hex colors not working in some kick messages } else { -- component = Component.translatable("multiplayer.disconnect.incompatible", SharedConstants.getCurrentVersion().name()); -+ adventureComponent = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(java.text.MessageFormat.format(org.spigotmc.SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), SharedConstants.getCurrentVersion().name())); // Spigot // Paper - Fix hex colors not working in some kick messages +- reason = Component.translatable("multiplayer.disconnect.incompatible", SharedConstants.getCurrentVersion().name()); ++ adventureReason = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(java.text.MessageFormat.format(org.spigotmc.SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), SharedConstants.getCurrentVersion().name())); // Spigot // Paper - Fix hex colors not working in some kick messages } -+ Component component = io.papermc.paper.adventure.PaperAdventure.asVanilla(adventureComponent); // Paper - Fix hex colors not working in some kick messages ++ Component reason = io.papermc.paper.adventure.PaperAdventure.asVanilla(adventureReason); // Paper - Fix hex colors not working in some kick messages - this.connection.send(new ClientboundLoginDisconnectPacket(component)); - this.connection.disconnect(component); + this.connection.send(new ClientboundLoginDisconnectPacket(reason)); + this.connection.disconnect(reason); } else { - this.connection.setupInboundProtocol(LoginProtocols.SERVERBOUND, new ServerLoginPacketListenerImpl(this.server, this.connection, transferred)); + this.connection.setupInboundProtocol(LoginProtocols.SERVERBOUND, new ServerLoginPacketListenerImpl(this.server, this.connection, transfer)); + // Paper start - PlayerHandshakeEvent + boolean proxyLogicEnabled = org.spigotmc.SpigotConfig.bungee; + boolean handledByEvent = false; diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch index f7a4161d565d..cc8a081147a0 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch @@ -12,16 +12,16 @@ + public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, TickablePacketListener { private static final AtomicInteger UNIQUE_THREAD_ID = new AtomicInteger(0); - static final Logger LOGGER = LogUtils.getLogger(); + private static final Logger LOGGER = LogUtils.getLogger(); + private static final java.util.concurrent.ExecutorService authenticatorPool = java.util.concurrent.Executors.newCachedThreadPool(new com.google.common.util.concurrent.ThreadFactoryBuilder().setNameFormat("User Authenticator #%d").setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)).build()); // Paper - Cache authenticator threads private static final int MAX_TICKS_BEFORE_LOGIN = 600; private final byte[] challenge; - final MinecraftServer server; + private final MinecraftServer server; @@ -56,10 +_,15 @@ - final ServerActivityMonitor serverActivityMonitor; + private final ServerActivityMonitor serverActivityMonitor; public volatile ServerLoginPacketListenerImpl.State state = ServerLoginPacketListenerImpl.State.HELLO; private int tick; -- @Nullable String requestedUsername; +- private @Nullable String requestedUsername; + public @Nullable String requestedUsername; // Paper - public public @Nullable GameProfile authenticatedProfile; private final String serverId = ""; @@ -33,8 +33,8 @@ + private final io.papermc.paper.connection.PaperPlayerLoginConnection paperLoginConnection; // Paper - Config API + private volatile boolean disconnecting = false; // Paper - Fix disconnect still ticking login - public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) { - this.server = server; + public ServerLoginPacketListenerImpl(final MinecraftServer minecraftserver, final Connection connection, final boolean transferred) { + this.server = minecraftserver; @@ -67,10 +_,20 @@ this.serverActivityMonitor = this.server.getServerActivityMonitor(); this.challenge = Ints.toByteArray(RandomSource.create().nextInt()); @@ -81,16 +81,16 @@ public boolean isAcceptingMessages() { return this.connection.isConnected(); @@ -95,6 +_,7 @@ - LOGGER.info("Disconnecting {}: {}", this.getUserName(), reason.getString()); - this.connection.send(new ClientboundLoginDisconnectPacket(reason)); - this.connection.disconnect(reason); + LOGGER.info("Disconnecting {}: {}", this.getUserName(), component.getString()); + this.connection.send(new ClientboundLoginDisconnectPacket(component)); + this.connection.disconnect(component); + this.disconnecting = true; // Paper - Fix disconnect still ticking login } catch (Exception var3) { LOGGER.error("Error whilst disconnecting player", (Throwable)var3); } @@ -117,7 +_,14 @@ @Override - public void handleHello(ServerboundHelloPacket packet) { + public void handleHello(final ServerboundHelloPacket packet) { Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet"); - Validate.validState(StringUtil.isValidPlayerName(packet.name()), "Invalid characters in username"); + // Paper start - Validate usernames @@ -140,27 +140,27 @@ } @@ -139,7 +_,7 @@ - private void verifyLoginAndFinishConnectionSetup(GameProfile profile) { + private void verifyLoginAndFinishConnectionSetup(final GameProfile profile) { PlayerList playerList = this.server.getPlayerList(); -- Component component = playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(profile)); -+ Component component = org.bukkit.craftbukkit.event.CraftEventFactory.handleLoginResult(playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(profile)), this.paperLoginConnection, this.connection, profile, this.server, true); // Paper - if (component != null) { - this.disconnect(component); +- Component error = playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(profile)); ++ Component error = org.bukkit.craftbukkit.event.CraftEventFactory.handleLoginResult(playerList.canPlayerLogin(this.connection.getRemoteAddress(), new NameAndId(profile)), this.paperLoginConnection, this.connection, profile, this.server, true); // Paper + if (error != null) { + this.disconnect(error); } else { @@ -151,7 +_,7 @@ ); } -- boolean flag = playerList.disconnectAllPlayersWithProfile(profile.id()); -+ boolean flag = playerList.disconnectAllPlayersWithProfile(profile); // Paper - validate usernames - if (flag) { +- boolean waitForDisconnection = playerList.disconnectAllPlayersWithProfile(profile.id()); ++ boolean waitForDisconnection = playerList.disconnectAllPlayersWithProfile(profile); // Paper - validate usernames + if (waitForDisconnection) { this.state = ServerLoginPacketListenerImpl.State.WAITING_FOR_DUPE_DISCONNECT; } else { @@ -163,6 +_,7 @@ - private void finishLoginAndWaitForClient(GameProfile profile) { + private void finishLoginAndWaitForClient(final GameProfile gameProfile) { this.state = ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING; - this.connection.send(new ClientboundLoginFinishedPacket(profile)); -+ this.server.services().paper().filledProfileCache().add(profile); // Paper - update profile cache + this.connection.send(new ClientboundLoginFinishedPacket(gameProfile)); ++ this.server.services().paper().filledProfileCache().add(gameProfile); // Paper - update profile cache } @Override @@ -171,35 +171,35 @@ - Thread thread = new Thread("User Authenticator #" + UNIQUE_THREAD_ID.incrementAndGet()) { + // Paper start - Cache authenticator threads + authenticatorPool.execute(new Runnable() { - @Override - public void run() { - String string1 = Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized"); -@@ -198,12 +_,18 @@ - .hasJoinedServer(string1, string, this.getAddress()); - if (profileResult != null) { - GameProfile gameProfile = profileResult.profile(); + { + Objects.requireNonNull(ServerLoginPacketListenerImpl.this); + } +@@ -202,12 +_,18 @@ + .hasJoinedServer(name, digest, this.getAddress()); + if (result != null) { + GameProfile profile = result.profile(); + // CraftBukkit start - fire PlayerPreLoginEvent + if (!ServerLoginPacketListenerImpl.this.connection.isConnected()) { + return; + } -+ gameProfile = ServerLoginPacketListenerImpl.this.callPlayerPreLoginEvents(gameProfile); // Paper - Add more fields to AsyncPlayerPreLoginEvent ++ profile = ServerLoginPacketListenerImpl.this.callPlayerPreLoginEvents(profile); // Paper - Add more fields to AsyncPlayerPreLoginEvent + // CraftBukkit end - ServerLoginPacketListenerImpl.LOGGER.info("UUID of player {} is {}", gameProfile.name(), gameProfile.id()); + ServerLoginPacketListenerImpl.LOGGER.info("UUID of player {} is {}", profile.name(), profile.id()); ServerLoginPacketListenerImpl.this.serverActivityMonitor.reportLoginActivity(); - ServerLoginPacketListenerImpl.this.startClientVerification(gameProfile); + ServerLoginPacketListenerImpl.this.startClientVerification(profile); } else if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) { ServerLoginPacketListenerImpl.LOGGER.warn("Failed to verify username but will let them in anyway!"); -- ServerLoginPacketListenerImpl.this.startClientVerification(UUIDUtil.createOfflineProfile(string1)); -+ ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(string1)); // Spigot +- ServerLoginPacketListenerImpl.this.startClientVerification(UUIDUtil.createOfflineProfile(name)); ++ ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(name)); // Spigot } else { ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.unverified_username")); - ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", string1); -@@ -211,11 +_,16 @@ + ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", name); +@@ -215,11 +_,16 @@ } catch (AuthenticationUnavailableException var4) { if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) { ServerLoginPacketListenerImpl.LOGGER.warn("Authentication servers are down but will let them in anyway!"); -- ServerLoginPacketListenerImpl.this.startClientVerification(UUIDUtil.createOfflineProfile(string1)); -+ ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(string1)); // Spigot +- ServerLoginPacketListenerImpl.this.startClientVerification(UUIDUtil.createOfflineProfile(name)); ++ ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(name)); // Spigot } else { - ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.authservers_down")); + ServerLoginPacketListenerImpl.this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.authenticationServersDown)); // Paper - Configurable kick message @@ -208,12 +208,12 @@ + // CraftBukkit start - catch all exceptions + } catch (Exception ex) { + ServerLoginPacketListenerImpl.this.disconnect("Failed to verify username!"); -+ ServerLoginPacketListenerImpl.LOGGER.warn("Exception verifying {}", string1, ex); ++ ServerLoginPacketListenerImpl.LOGGER.warn("Exception verifying {}", name, ex); + // CraftBukkit end } } -@@ -225,18 +_,123 @@ +@@ -229,18 +_,123 @@ ? ((InetSocketAddress)remoteAddress).getAddress() : null; } @@ -277,7 +277,7 @@ + // CraftBukkit end @Override - public void handleCustomQueryPacket(ServerboundCustomQueryAnswerPacket packet) { + public void handleCustomQueryPacket(final ServerboundCustomQueryAnswerPacket packet) { + // Paper start - Add Velocity IP Forwarding Support + try { + this.handleCustomQueryPacket0(packet); @@ -288,7 +288,7 @@ + } + } + } -+ private void handleCustomQueryPacket0(ServerboundCustomQueryAnswerPacket packet) { ++ private void handleCustomQueryPacket0(final ServerboundCustomQueryAnswerPacket packet) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled && packet.transactionId() == this.velocityLoginMessageId) { + ServerboundCustomQueryAnswerPacket.QueryAnswerPayload payload = (ServerboundCustomQueryAnswerPacket.QueryAnswerPayload)packet.payload(); + if (payload == null) { @@ -336,15 +336,15 @@ } @Override - public void handleLoginAcknowledgement(ServerboundLoginAcknowledgedPacket packet) { + public void handleLoginAcknowledgement(final ServerboundLoginAcknowledgedPacket packet) { + net.minecraft.network.protocol.PacketUtils.ensureRunningOnSameThread(packet, this, this.server.packetProcessor()); // CraftBukkit Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet"); this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); - CommonListenerCookie commonListenerCookie = CommonListenerCookie.createInitial(Objects.requireNonNull(this.authenticatedProfile), this.transferred); -@@ -255,8 +_,31 @@ + CommonListenerCookie cookie = CommonListenerCookie.createInitial(Objects.requireNonNull(this.authenticatedProfile), this.transferred); +@@ -257,8 +_,31 @@ @Override - public void handleCookieResponse(ServerboundCookieResponsePacket packet) { + public void handleCookieResponse(final ServerboundCookieResponsePacket packet) { + if (this.paperLoginConnection.handleCookieResponse(packet)) return; // Paper this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); } diff --git a/paper-server/patches/sources/net/minecraft/server/network/config/PrepareSpawnTask.java.patch b/paper-server/patches/sources/net/minecraft/server/network/config/PrepareSpawnTask.java.patch index 616349a9566f..dcb6933c8c1f 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/config/PrepareSpawnTask.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/config/PrepareSpawnTask.java.patch @@ -1,18 +1,19 @@ --- a/net/minecraft/server/network/config/PrepareSpawnTask.java +++ b/net/minecraft/server/network/config/PrepareSpawnTask.java -@@ -36,10 +_,17 @@ - final LevelLoadListener loadListener; +@@ -37,10 +_,18 @@ + private final LevelLoadListener loadListener; private PrepareSpawnTask.@Nullable State state; -- public PrepareSpawnTask(MinecraftServer server, NameAndId nameAndId) { +- public PrepareSpawnTask(final MinecraftServer server, final NameAndId nameAndId) { + // Paper start - passthrough profile and packet listener + private final com.mojang.authlib.GameProfile profile; + private final net.minecraft.server.network.ServerConfigurationPacketListenerImpl listener; + private boolean newPlayer; -+ public PrepareSpawnTask(MinecraftServer server, com.mojang.authlib.GameProfile profile, net.minecraft.server.network.ServerConfigurationPacketListenerImpl listener) { ++ ++ public PrepareSpawnTask(final MinecraftServer server, final com.mojang.authlib.GameProfile profile, final net.minecraft.server.network.ServerConfigurationPacketListenerImpl listener) { + this.profile = profile; + this.listener = listener; -+ // Paper end - passthrough profile and packet listener ++ // Paper end - passthrough profile and packet listener this.server = server; - this.nameAndId = nameAndId; - this.loadListener = server.getLevelLoadListener(); @@ -21,18 +22,18 @@ } @Override -@@ -49,15 +_,57 @@ +@@ -50,16 +_,58 @@ .getPlayerList() .loadPlayerData(this.nameAndId) - .map(compoundTag -> TagValueInput.create(scopedCollector, this.server.registryAccess(), compoundTag)); + .map(tag -> TagValueInput.create(reporter, this.server.registryAccess(), tag)); + // Paper start - move logic in Entity to here, to use bukkit supplied world UUID & reset to main world spawn if no valid world is found -+ this.newPlayer = optional.isEmpty(); // New players don't have saved data! ++ this.newPlayer = loadedData.isEmpty(); // New players don't have saved data! + net.minecraft.resources.ResourceKey resourceKey = null; // Paper + boolean[] invalidPlayerWorld = {false}; -+ bukkitData: if (optional.isPresent()) { ++ bukkitData: if (loadedData.isPresent()) { + // The main way for bukkit worlds to store the world is the world UUID despite mojang adding custom worlds + final org.bukkit.World bWorld; -+ final ValueInput playerData = optional.get(); ++ final ValueInput playerData = loadedData.get(); + // TODO maybe convert this to a codec and use compoundTag#read, we need silent variants of that method first. + final Optional worldUUIDMost = playerData.getLong("WorldUUIDMost"); + final Optional worldUUIDLeast = playerData.getLong("WorldUUIDLeast"); @@ -51,42 +52,44 @@ + invalidPlayerWorld[0] = true; + } + } - ServerPlayer.SavedPosition savedPosition = optional.flatMap( - valueInput -> valueInput.read(ServerPlayer.SavedPosition.MAP_CODEC) - ) + ServerPlayer.SavedPosition loadedPosition = loadedData.flatMap(tag -> tag.read(ServerPlayer.SavedPosition.MAP_CODEC)) .orElse(ServerPlayer.SavedPosition.EMPTY); LevelData.RespawnData respawnData = this.server.getWorldData().overworldData().getRespawnData(); -- ServerLevel serverLevel = savedPosition.dimension().map(this.server::getLevel).orElseGet(() -> { -- ServerLevel level = this.server.getLevel(respawnData.dimension()); -- return level != null ? level : this.server.overworld(); +- ServerLevel spawnLevel = loadedPosition.dimension().map(this.server::getLevel).orElseGet(() -> { +- ServerLevel spawnDataLevel = this.server.getLevel(respawnData.dimension()); +- return spawnDataLevel != null ? spawnDataLevel : this.server.overworld(); - }); + if (resourceKey == null) { // only run the vanilla logic if we haven't found a world from the bukkit data + // Below is the vanilla way of getting the dimension, this is for migration from vanilla servers -+ resourceKey = savedPosition.dimension().orElse(null); ++ resourceKey = loadedPosition.dimension().orElse(null); + } -+ ServerLevel vanillaDefaultLevel = this.server.getLevel(respawnData.dimension()); -+ if (vanillaDefaultLevel == null) { -+ vanillaDefaultLevel = this.server.overworld(); ++ ServerLevel spawnDataLevel = this.server.getLevel(respawnData.dimension()); ++ if (spawnDataLevel == null) { ++ spawnDataLevel = this.server.overworld(); + } -+ ServerLevel serverLevel1; ++ ServerLevel spawnLevel; + if (resourceKey == null) { -+ serverLevel1 = vanillaDefaultLevel; ++ spawnLevel = spawnDataLevel; + } else { -+ serverLevel1 = this.server.getLevel(resourceKey); -+ if (serverLevel1 == null) { ++ spawnLevel = this.server.getLevel(resourceKey); ++ if (spawnLevel == null) { + LOGGER.warn("Unknown respawn dimension {}, defaulting to overworld", resourceKey); -+ serverLevel1 = vanillaDefaultLevel; ++ spawnLevel = spawnDataLevel; + } + } -+ final ServerLevel serverLevel = serverLevel1; -+ // Paper end - move logic in Entity to here, to use bukkit supplied world UUID & reset to main world spawn if no valid world is found - CompletableFuture completableFuture = savedPosition.position() ++ final ServerLevel finalSpawnLevel = spawnLevel; + CompletableFuture spawnPosition = loadedPosition.position() .map(CompletableFuture::completedFuture) - .orElseGet(() -> PlayerSpawnFinder.findSpawn(serverLevel, respawnData.pos())); -@@ -112,10 +_,11 @@ +- .orElseGet(() -> PlayerSpawnFinder.findSpawn(spawnLevel, respawnData.pos())); ++ .orElseGet(() -> PlayerSpawnFinder.findSpawn(finalSpawnLevel, respawnData.pos())); ++ // Paper end - move logic in Entity to here, to use bukkit supplied world UUID & reset to main world spawn if no valid world is found + Vec2 spawnAngle = loadedPosition.rotation().orElse(new Vec2(respawnData.yaw(), respawnData.pitch())); + this.state = new PrepareSpawnTask.Preparing(spawnLevel, spawnPosition, spawnAngle); + } +@@ -111,10 +_,11 @@ } - final class Preparing implements PrepareSpawnTask.State { + private final class Preparing implements PrepareSpawnTask.State { - private final ServerLevel spawnLevel; - private final CompletableFuture spawnPosition; - private final Vec2 spawnAngle; @@ -95,40 +98,44 @@ + private Vec2 spawnAngle; // Paper - remove final private @Nullable CompletableFuture chunkLoadFuture; + private @Nullable CompletableFuture eventFuture; // Paper - private final ChunkLoadCounter chunkLoadCounter = new ChunkLoadCounter(); + private final ChunkLoadCounter chunkLoadCounter; - Preparing(final ServerLevel spawnLevel, final CompletableFuture spawnPosition, final Vec2 spawnAngle) { -@@ -134,6 +_,50 @@ + private Preparing(final ServerLevel spawnLevel, final CompletableFuture spawnPosition, final Vec2 spawnAngle) { +@@ -136,6 +_,54 @@ } else { - Vec3 vec3 = this.spawnPosition.join(); + Vec3 spawnPosition = this.spawnPosition.join(); if (this.chunkLoadFuture == null) { + // Paper start - PlayerSpawnLocationEvent + if (this.eventFuture == null && org.spigotmc.event.player.PlayerSpawnLocationEvent.getHandlerList().getRegisteredListeners().length != 0) { -+ ServerPlayer serverPlayer; ++ ServerPlayer player; + if (PrepareSpawnTask.this.listener.connection.savedPlayerForLegacyEvents != null) { -+ serverPlayer = PrepareSpawnTask.this.listener.connection.savedPlayerForLegacyEvents; ++ player = PrepareSpawnTask.this.listener.connection.savedPlayerForLegacyEvents; + } else { -+ serverPlayer = new ServerPlayer( -+ PrepareSpawnTask.this.server, PrepareSpawnTask.this.server.overworld(), PrepareSpawnTask.this.profile, net.minecraft.server.level.ClientInformation.createDefault()); -+ PrepareSpawnTask.this.listener.connection.savedPlayerForLegacyEvents = serverPlayer; ++ player = new ServerPlayer( ++ PrepareSpawnTask.this.server, ++ PrepareSpawnTask.this.server.overworld(), ++ PrepareSpawnTask.this.profile, ++ net.minecraft.server.level.ClientInformation.createDefault() ++ ); ++ PrepareSpawnTask.this.listener.connection.savedPlayerForLegacyEvents = player; + } + org.spigotmc.event.player.PlayerSpawnLocationEvent ev = new org.spigotmc.event.player.PlayerSpawnLocationEvent( -+ serverPlayer.getBukkitEntity(), -+ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(vec3, this.spawnLevel, this.spawnAngle.x, this.spawnAngle.y) ++ player.getBukkitEntity(), ++ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(spawnPosition, this.spawnLevel, this.spawnAngle.x, this.spawnAngle.y) + ); + ev.callEvent(); -+ vec3 = io.papermc.paper.util.MCUtil.toVec3(ev.getSpawnLocation()); ++ spawnPosition = io.papermc.paper.util.MCUtil.toVec3(ev.getSpawnLocation()); + this.spawnLevel = ((org.bukkit.craftbukkit.CraftWorld) ev.getSpawnLocation().getWorld()).getHandle(); -+ this.spawnPosition = CompletableFuture.completedFuture(vec3); ++ this.spawnPosition = CompletableFuture.completedFuture(spawnPosition); + this.spawnAngle = new Vec2(ev.getSpawnLocation().getYaw(), ev.getSpawnLocation().getPitch()); + } + + if (this.eventFuture == null && io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent.getHandlerList().getRegisteredListeners().length != 0) { -+ final Vec3 vec3final = vec3; ++ final Vec3 spawnPositionFinal = spawnPosition; + this.eventFuture = CompletableFuture.supplyAsync(() -> { + io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent ev = new io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent( + PrepareSpawnTask.this.listener.paperConnection, -+ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(vec3final, this.spawnLevel, this.spawnAngle.x, this.spawnAngle.y), ++ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(spawnPositionFinal, this.spawnLevel, this.spawnAngle.x, this.spawnAngle.y), + PrepareSpawnTask.this.newPlayer + ); + ev.callEvent(); @@ -140,62 +147,62 @@ + return null; + } + org.bukkit.Location location = this.eventFuture.join(); -+ vec3 = io.papermc.paper.util.MCUtil.toVec3(location); ++ spawnPosition = io.papermc.paper.util.MCUtil.toVec3(location); + this.spawnLevel = ((org.bukkit.craftbukkit.CraftWorld) location.getWorld()).getHandle(); -+ this.spawnPosition = CompletableFuture.completedFuture(vec3); ++ this.spawnPosition = CompletableFuture.completedFuture(spawnPosition); + this.spawnAngle = new Vec2(location.getYaw(), location.getPitch()); + } + // Paper end - PlayerSpawnLocationEvent - ChunkPos chunkPos = new ChunkPos(BlockPos.containing(vec3)); + ChunkPos spawnChunk = ChunkPos.containing(BlockPos.containing(spawnPosition)); this.chunkLoadCounter .track( -@@ -174,15 +_,48 @@ - public ServerPlayer spawn(Connection connection, CommonListenerCookie cookie) { - ChunkPos chunkPos = new ChunkPos(BlockPos.containing(this.spawnPosition)); - this.spawnLevel.waitForEntities(chunkPos, 3); -- ServerPlayer serverPlayer = new ServerPlayer(PrepareSpawnTask.this.server, this.spawnLevel, cookie.gameProfile(), cookie.clientInformation()); +@@ -178,15 +_,48 @@ + public ServerPlayer spawn(final Connection connection, final CommonListenerCookie cookie) { + ChunkPos spawnChunk = ChunkPos.containing(BlockPos.containing(this.spawnPosition)); + this.spawnLevel.waitForEntities(spawnChunk, 3); +- ServerPlayer player = new ServerPlayer(PrepareSpawnTask.this.server, this.spawnLevel, cookie.gameProfile(), cookie.clientInformation()); + // Paper start - configuration api - possibly use legacy saved server player instance -+ ServerPlayer serverPlayer; ++ ServerPlayer player; + if (connection.savedPlayerForLegacyEvents != null) { -+ serverPlayer = connection.savedPlayerForLegacyEvents; ++ player = connection.savedPlayerForLegacyEvents; + connection.savedPlayerForLegacyEvents = null; + // Update the existing instance -+ serverPlayer.gameProfile = cookie.gameProfile(); -+ serverPlayer.updateOptionsNoEvents(cookie.clientInformation()); -+ serverPlayer.setServerLevel(this.spawnLevel); ++ player.gameProfile = cookie.gameProfile(); ++ player.updateOptionsNoEvents(cookie.clientInformation()); ++ player.setServerLevel(this.spawnLevel); + } else { -+ serverPlayer = new ServerPlayer(PrepareSpawnTask.this.server, this.spawnLevel, cookie.gameProfile(), cookie.clientInformation()); ++ player = new ServerPlayer(PrepareSpawnTask.this.server, this.spawnLevel, cookie.gameProfile(), cookie.clientInformation()); + } + // Paper end - configuration api - possibly use legacy saved server player instance ServerPlayer var7; - try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(serverPlayer.problemPath(), PrepareSpawnTask.LOGGER)) { - Optional optional = PrepareSpawnTask.this.server + try (ProblemReporter.ScopedCollector reporter = new ProblemReporter.ScopedCollector(player.problemPath(), PrepareSpawnTask.LOGGER)) { + Optional input = PrepareSpawnTask.this.server .getPlayerList() .loadPlayerData(PrepareSpawnTask.this.nameAndId) + // CraftBukkit start + .map(tag -> { -+ org.bukkit.craftbukkit.entity.CraftPlayer craftPlayer = serverPlayer.getBukkitEntity(); ++ org.bukkit.craftbukkit.entity.CraftPlayer craftPlayer = player.getBukkitEntity(); + // Only update first played if it is older than the one we have -+ long modified = new java.io.File(net.minecraft.server.network.config.PrepareSpawnTask.this.server.playerDataStorage.getPlayerDir(), serverPlayer.getStringUUID() + ".dat").lastModified(); ++ long modified = new java.io.File(net.minecraft.server.network.config.PrepareSpawnTask.this.server.playerDataStorage.getPlayerDir(), player.getStringUUID() + ".dat").lastModified(); + if (modified < craftPlayer.getFirstPlayed()) { + craftPlayer.setFirstPlayed(modified); + } + return tag; + }) + // CraftBukkit end - .map(compoundTag -> TagValueInput.create(scopedCollector, PrepareSpawnTask.this.server.registryAccess(), compoundTag)); - optional.ifPresent(serverPlayer::load); + .map(tag -> TagValueInput.create(reporter, PrepareSpawnTask.this.server.registryAccess(), tag)); + input.ifPresent(player::load); + // CraftBukkit start - Better rename detection -+ if (optional.isPresent()) { -+ serverPlayer.lastKnownName = optional.flatMap(t -> t.child("bukkit")).flatMap(t -> t.getString("lastKnownName")).orElse(null); ++ if (input.isPresent()) { ++ player.lastKnownName = input.flatMap(t -> t.child("bukkit")).flatMap(t -> t.getString("lastKnownName")).orElse(null); + } + // CraftBukkit end - Better rename detection + // Paper start - Entity#getEntitySpawnReason -+ if (optional.isEmpty()) { -+ serverPlayer.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT; // set Player SpawnReason to DEFAULT on first login ++ if (input.isEmpty()) { ++ player.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT; // set Player SpawnReason to DEFAULT on first login + } + // Paper end - Entity#getEntitySpawnReason - serverPlayer.snapTo(this.spawnPosition, this.spawnAngle.x, this.spawnAngle.y); - PrepareSpawnTask.this.server.getPlayerList().placeNewPlayer(connection, serverPlayer, cookie); - optional.ifPresent(valueInput -> { + player.snapTo(this.spawnPosition, this.spawnAngle.x, this.spawnAngle.y); + PrepareSpawnTask.this.server.getPlayerList().placeNewPlayer(connection, player, cookie); + input.ifPresent(tag -> { diff --git a/paper-server/patches/sources/net/minecraft/server/notifications/EmptyNotificationService.java.patch b/paper-server/patches/sources/net/minecraft/server/notifications/EmptyNotificationService.java.patch index 83ebfe93a472..5f5327fe09bc 100644 --- a/paper-server/patches/sources/net/minecraft/server/notifications/EmptyNotificationService.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/notifications/EmptyNotificationService.java.patch @@ -4,8 +4,8 @@ } @Override -- public void onGameRuleChanged(GameRule rule, T value) { -+ public void onGameRuleChanged(net.minecraft.server.level.ServerLevel level, GameRule rule, T value) { // Paper - per-world game rules (add level param) +- public void onGameRuleChanged(final GameRule gameRule, final T value) { ++ public void onGameRuleChanged(net.minecraft.server.level.ServerLevel level, final GameRule gameRule, final T value) { // Paper - per-world game rules (add level param) } @Override diff --git a/paper-server/patches/sources/net/minecraft/server/notifications/NotificationManager.java.patch b/paper-server/patches/sources/net/minecraft/server/notifications/NotificationManager.java.patch index 9caace376939..fedd4f653880 100644 --- a/paper-server/patches/sources/net/minecraft/server/notifications/NotificationManager.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/notifications/NotificationManager.java.patch @@ -4,10 +4,10 @@ } @Override -- public void onGameRuleChanged(GameRule rule, T value) { -- this.notificationServices.forEach(notificationService -> notificationService.onGameRuleChanged(rule, value)); -+ public void onGameRuleChanged(net.minecraft.server.level.ServerLevel level, GameRule rule, T value) { // Paper - per-world game rules (add level param) -+ this.notificationServices.forEach(notificationService -> notificationService.onGameRuleChanged(level, rule, value)); // Paper - per-world game rules (add level param) +- public void onGameRuleChanged(final GameRule gameRule, final T value) { +- this.notificationServices.forEach(notificationService -> notificationService.onGameRuleChanged(gameRule, value)); ++ public void onGameRuleChanged(net.minecraft.server.level.ServerLevel level, final GameRule gameRule, final T value) { // Paper - per-world game rules (add level param) ++ this.notificationServices.forEach(notificationService -> notificationService.onGameRuleChanged(level, gameRule, value)); // Paper - per-world game rules (add level param) } @Override diff --git a/paper-server/patches/sources/net/minecraft/server/notifications/NotificationService.java.patch b/paper-server/patches/sources/net/minecraft/server/notifications/NotificationService.java.patch index 735ae3988d81..245395a1a69b 100644 --- a/paper-server/patches/sources/net/minecraft/server/notifications/NotificationService.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/notifications/NotificationService.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/server/notifications/NotificationService.java @@ -38,7 +_,7 @@ - void playerUnbanned(NameAndId nameAndId); + void playerUnbanned(NameAndId player); -- void onGameRuleChanged(GameRule rule, T value); -+ void onGameRuleChanged(net.minecraft.server.level.ServerLevel level, GameRule rule, T value); // Paper - per-world game rules (add level param) +- void onGameRuleChanged(GameRule gameRule, T value); ++ void onGameRuleChanged(net.minecraft.server.level.ServerLevel level, GameRule gameRule, T value); // Paper - per-world game rules (add level param) void statusHeartbeat(); } diff --git a/paper-server/patches/sources/net/minecraft/server/packs/PathPackResources.java.patch b/paper-server/patches/sources/net/minecraft/server/packs/PathPackResources.java.patch deleted file mode 100644 index 95ae2eebfd30..000000000000 --- a/paper-server/patches/sources/net/minecraft/server/packs/PathPackResources.java.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- a/net/minecraft/server/packs/PathPackResources.java -+++ b/net/minecraft/server/packs/PathPackResources.java -@@ -120,6 +_,12 @@ - try (DirectoryStream directoryStream = Files.newDirectoryStream(path)) { - for (Path path1 : directoryStream) { - String string = path1.getFileName().toString(); -+ // Paper start - Improve logging and errors -+ if (!Files.isDirectory(path1)) { -+ LOGGER.error("Invalid directory entry: {} in {}.", string, this.root, new java.nio.file.NotDirectoryException(string)); -+ continue; -+ } -+ // Paper end - Improve logging and errors - if (Identifier.isValidNamespace(string)) { - set.add(string); - } else { diff --git a/paper-server/patches/sources/net/minecraft/server/packs/repository/PackRepository.java.patch b/paper-server/patches/sources/net/minecraft/server/packs/repository/PackRepository.java.patch index 8a7d513c121f..4a1a6588aaa1 100644 --- a/paper-server/patches/sources/net/minecraft/server/packs/repository/PackRepository.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/packs/repository/PackRepository.java.patch @@ -1,21 +1,19 @@ --- a/net/minecraft/server/packs/repository/PackRepository.java +++ b/net/minecraft/server/packs/repository/PackRepository.java -@@ -21,9 +_,13 @@ +@@ -21,8 +_,12 @@ private final Set sources; private Map available = ImmutableMap.of(); private List selected = ImmutableList.of(); + private final net.minecraft.world.level.validation.DirectoryValidator validator; // Paper - add validator -- public PackRepository(RepositorySource... sources) { -- this.sources = ImmutableSet.copyOf(sources); +- public PackRepository(final RepositorySource... sources) { + // Paper start - add validator -+ public PackRepository(net.minecraft.world.level.validation.DirectoryValidator validator, RepositorySource... providers) { ++ public PackRepository(final net.minecraft.world.level.validation.DirectoryValidator validator, final RepositorySource... sources) { + this.validator = validator; + // Paper end - add validator -+ this.sources = ImmutableSet.copyOf(providers); + this.sources = ImmutableSet.copyOf(sources); } - public static String displayPackList(Collection packs) { @@ -31,9 +_,14 @@ } @@ -25,20 +23,20 @@ + } + public void reload(final boolean pendingReload) { + // Paper end - add pendingReload flag to determine required pack loading - List list = this.selected.stream().map(Pack::getId).collect(ImmutableList.toImmutableList()); + List currentlySelectedNames = this.selected.stream().map(Pack::getId).collect(ImmutableList.toImmutableList()); this.available = this.discoverAvailable(); -- this.selected = this.rebuildSelected(list); -+ this.selected = this.rebuildSelected(list, pendingReload); // Paper - add pendingReload flag to determine required pack loading +- this.selected = this.rebuildSelected(currentlySelectedNames); ++ this.selected = this.rebuildSelected(currentlySelectedNames, pendingReload); // Paper - add pendingReload flag to determine required pack loading } private Map discoverAvailable() { @@ -43,16 +_,23 @@ - repositorySource.loadPacks(pack -> map.put(pack.getId(), pack)); + source.loadPacks(pack -> discovered.put(pack.getId(), pack)); } -- return ImmutableMap.copyOf(map); +- return ImmutableMap.copyOf(discovered); + // Paper start - custom plugin-loaded datapacks -+ final io.papermc.paper.datapack.PaperDatapackRegistrar registrar = new io.papermc.paper.datapack.PaperDatapackRegistrar(this.validator, map); ++ final io.papermc.paper.datapack.PaperDatapackRegistrar registrar = new io.papermc.paper.datapack.PaperDatapackRegistrar(this.validator, discovered); + io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callStaticRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.DATAPACK_DISCOVERY, + registrar, + io.papermc.paper.plugin.bootstrap.BootstrapContext.class @@ -48,29 +46,29 @@ } public boolean isAbleToClearAnyPack() { -- List list = this.rebuildSelected(List.of()); -+ List list = this.rebuildSelected(List.of(), false); // Paper - add pendingReload flag to determine required pack loading - return !this.selected.equals(list); +- List newSelected = this.rebuildSelected(List.of()); ++ List newSelected = this.rebuildSelected(List.of(), false); // Paper - add pendingReload flag to determine required pack loading + return !this.selected.equals(newSelected); } -- public void setSelected(Collection ids) { -- this.selected = this.rebuildSelected(ids); -+ public void setSelected(Collection ids, final boolean pendingReload) { // Paper - add pendingReload flag to determine required pack loading -+ this.selected = this.rebuildSelected(ids, pendingReload); // Paper - add pendingReload flag to determine required pack loading +- public void setSelected(final Collection packs) { +- this.selected = this.rebuildSelected(packs); ++ public void setSelected(final Collection packs, final boolean pendingReload) { // Paper - add pendingReload flag to determine required pack loading ++ this.selected = this.rebuildSelected(packs, pendingReload); // Paper - add pendingReload flag to determine required pack loading } - public boolean addPack(String id) { + public boolean addPack(final String packId) { @@ -79,11 +_,11 @@ } } -- private List rebuildSelected(Collection ids) { -+ private List rebuildSelected(Collection ids, boolean pendingReload) { // Paper - add pendingReload flag to determine required pack loading - List list = this.getAvailablePacks(ids).collect(Util.toMutableList()); +- private List rebuildSelected(final Collection selectedNames) { ++ private List rebuildSelected(final Collection selectedNames, final boolean pendingReload) { // Paper - add pendingReload flag to determine required pack loading + List selectedAndPresent = this.getAvailablePacks(selectedNames).collect(Util.toMutableList()); for (Pack pack : this.available.values()) { -- if (pack.isRequired() && !list.contains(pack)) { -+ if (pack.isRequired() && !list.contains(pack) && pendingReload) { // Paper - add pendingReload flag to determine required pack loading - pack.getDefaultPosition().insert(list, pack, Pack::selectionConfig, false); +- if (pack.isRequired() && !selectedAndPresent.contains(pack)) { ++ if (pack.isRequired() && !selectedAndPresent.contains(pack) && pendingReload) { // Paper - add pendingReload flag to determine required pack loading + pack.getDefaultPosition().insert(selectedAndPresent, pack, Pack::selectionConfig, false); } } diff --git a/paper-server/patches/sources/net/minecraft/server/packs/repository/ServerPacksSource.java.patch b/paper-server/patches/sources/net/minecraft/server/packs/repository/ServerPacksSource.java.patch index dbb1f23f29a0..2e6f9eefb06b 100644 --- a/paper-server/patches/sources/net/minecraft/server/packs/repository/ServerPacksSource.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/packs/repository/ServerPacksSource.java.patch @@ -12,25 +12,25 @@ @@ -66,15 +_,26 @@ @Override - protected @Nullable Pack createBuiltinPack(String id, Pack.ResourcesSupplier resources, Component title) { -- return Pack.readMetaAndCreate(createBuiltInPackLocation(id, title), resources, PackType.SERVER_DATA, FEATURE_SELECTION_CONFIG); + protected @Nullable Pack createBuiltinPack(final String id, final Pack.ResourcesSupplier resources, final Component name) { +- return Pack.readMetaAndCreate(createBuiltInPackLocation(id, name), resources, PackType.SERVER_DATA, FEATURE_SELECTION_CONFIG); + // Paper start - custom built-in pack + final PackLocationInfo info; + final PackSelectionConfig packConfig; + if ("paper".equals(id)) { -+ info = new PackLocationInfo(id, title, PackSource.BUILT_IN, Optional.empty()); ++ info = new PackLocationInfo(id, name, PackSource.BUILT_IN, Optional.empty()); + packConfig = new PackSelectionConfig(true, Pack.Position.TOP, true); + } else { -+ info = createBuiltInPackLocation(id, title); ++ info = createBuiltInPackLocation(id, name); + packConfig = FEATURE_SELECTION_CONFIG; + } + return Pack.readMetaAndCreate(info, resources, PackType.SERVER_DATA, packConfig); + // Paper end - custom built-in pack } - public static PackRepository createPackRepository(Path folder, DirectoryValidator validator) { -- return new PackRepository(new ServerPacksSource(validator), new FolderRepositorySource(folder, PackType.SERVER_DATA, PackSource.WORLD, validator)); -+ return new PackRepository(validator, new ServerPacksSource(validator), new FolderRepositorySource(folder, PackType.SERVER_DATA, PackSource.WORLD, validator)); // Paper - add validator + public static PackRepository createPackRepository(final Path datapackDir, final DirectoryValidator validator) { +- return new PackRepository(new ServerPacksSource(validator), new FolderRepositorySource(datapackDir, PackType.SERVER_DATA, PackSource.WORLD, validator)); ++ return new PackRepository(validator, new ServerPacksSource(validator), new FolderRepositorySource(datapackDir, PackType.SERVER_DATA, PackSource.WORLD, validator)); // Paper - add validator } public static PackRepository createVanillaTrustedRepository() { @@ -38,4 +38,4 @@ + return new PackRepository(new DirectoryValidator(path -> true), new ServerPacksSource(new DirectoryValidator(path -> true))); // Paper - add validator } - public static PackRepository createPackRepository(LevelStorageSource.LevelStorageAccess level) { + public static PackRepository createPackRepository(final LevelStorageSource.LevelStorageAccess levelSourceAccess) { diff --git a/paper-server/patches/sources/net/minecraft/server/permissions/PermissionProviderCheck.java.patch b/paper-server/patches/sources/net/minecraft/server/permissions/PermissionProviderCheck.java.patch index 205aa6425bd4..4c9ecf6a297d 100644 --- a/paper-server/patches/sources/net/minecraft/server/permissions/PermissionProviderCheck.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/permissions/PermissionProviderCheck.java.patch @@ -7,14 +7,14 @@ -public record PermissionProviderCheck(PermissionCheck test) implements Predicate { +public record PermissionProviderCheck(PermissionCheck test, java.util.concurrent.atomic.AtomicReference> vanillaNode) implements Predicate { // Paper - Vanilla Command permission checking @Override - public boolean test(T permissionsSupplier) { + public boolean test(final T t) { + // Paper start - Vanilla Command permission checking + com.mojang.brigadier.tree.CommandNode currentCommand = vanillaNode.get(); -+ if (currentCommand != null && permissionsSupplier instanceof net.minecraft.commands.CommandSourceStack commandSourceStack && this.test instanceof net.minecraft.server.permissions.PermissionCheck.Require req) { ++ if (currentCommand != null && t instanceof net.minecraft.commands.CommandSourceStack commandSourceStack && this.test instanceof net.minecraft.server.permissions.PermissionCheck.Require req) { + return commandSourceStack.hasPermission(req.permission(), org.bukkit.craftbukkit.command.VanillaCommandWrapper.getPermission(currentCommand)); + } + // Paper end - Vanilla Command permission checking - return this.test.check(permissionsSupplier.permissions()); + return this.test.check(t.permissions()); } + + // Paper start - Vanilla Command permission checking diff --git a/paper-server/patches/sources/net/minecraft/server/players/BanListEntry.java.patch b/paper-server/patches/sources/net/minecraft/server/players/BanListEntry.java.patch index adebe9c189b2..3509a5098176 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/BanListEntry.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/BanListEntry.java.patch @@ -1,15 +1,15 @@ --- a/net/minecraft/server/players/BanListEntry.java +++ b/net/minecraft/server/players/BanListEntry.java -@@ -26,7 +_,7 @@ +@@ -28,7 +_,7 @@ } - protected BanListEntry(@Nullable T user, JsonObject entryData) { + protected BanListEntry(final @Nullable T user, final JsonObject object) { - super(user); -+ super(BanListEntry.checkExpiry(user, entryData)); // CraftBukkit ++ super(BanListEntry.checkExpiry(user, object)); // CraftBukkit - Date date; + Date created; try { -@@ -99,4 +_,22 @@ +@@ -101,4 +_,22 @@ return false; } } diff --git a/paper-server/patches/sources/net/minecraft/server/players/CachedUserNameToIdResolver.java.patch b/paper-server/patches/sources/net/minecraft/server/players/CachedUserNameToIdResolver.java.patch index cdbf2f1a6c97..8da762b0a07c 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/CachedUserNameToIdResolver.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/CachedUserNameToIdResolver.java.patch @@ -9,12 +9,12 @@ + protected final java.util.concurrent.locks.ReentrantLock lookupLock = new java.util.concurrent.locks.ReentrantLock(); + // Paper end - Fix GameProfileCache concurrency - public CachedUserNameToIdResolver(GameProfileRepository profileRepository, File file) { + public CachedUserNameToIdResolver(final GameProfileRepository profileRepository, final File file) { this.profileRepository = profileRepository; @@ -54,23 +_,27 @@ } - private void safeAdd(CachedUserNameToIdResolver.GameProfileInfo profileInfo) { + private void safeAdd(final CachedUserNameToIdResolver.GameProfileInfo profileInfo) { + try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency NameAndId nameAndId = profileInfo.nameAndId(); profileInfo.setLastAccess(this.getNextOperation()); @@ -23,31 +23,31 @@ + } finally { this.stateLock.unlock(); } // Paper - Fix GameProfileCache concurrency } - private Optional lookupGameProfile(GameProfileRepository profileRepository, String name) { + private Optional lookupGameProfile(final GameProfileRepository profileRepository, final String name) { if (!StringUtil.isValidPlayerName(name)) { return this.createUnknownProfile(name); } else { -- Optional optional = profileRepository.findProfileByName(name).map(NameAndId::new); -+ final boolean shouldLookup = !org.apache.commons.lang3.StringUtils.isBlank(name) // Paper - Don't lookup a profile with a blank name +- Optional profile = profileRepository.findProfileByName(name).map(NameAndId::new); ++ final boolean shouldLookup = !org.apache.commons.lang3.StringUtils.isBlank(name) // Paper - Don't lookup a profile with a blank name + && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode(); // Paper - Add setting for proxy online mode status -+ Optional optional = shouldLookup ? profileRepository.findProfileByName(name).map(NameAndId::new) : Optional.empty(); // Paper - Don't lookup a profile with a blank name - return optional.isEmpty() ? this.createUnknownProfile(name) : optional; ++ Optional profile = shouldLookup ? profileRepository.findProfileByName(name).map(NameAndId::new) : Optional.empty(); // Paper - Don't lookup a profile with a blank name + return profile.isEmpty() ? this.createUnknownProfile(name) : profile; } } - private Optional createUnknownProfile(String name) { + private Optional createUnknownProfile(final String name) { - return this.resolveOfflineUsers ? Optional.of(NameAndId.createOffline(name)) : Optional.empty(); -+ return !io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ? Optional.of(NameAndId.createOffline(name)) : Optional.empty(); // Paper - Add setting for proxy online mode status ++ return this.resolveOfflineUsers && !io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ? Optional.of(NameAndId.createOffline(name)) : Optional.empty(); // Paper - Add setting for proxy online mode status } @Override @@ -90,7 +_,7 @@ - Date time = instance.getTime(); - CachedUserNameToIdResolver.GameProfileInfo gameProfileInfo = new CachedUserNameToIdResolver.GameProfileInfo(nameAndId, time); - this.safeAdd(gameProfileInfo); + Date expirationDate = c.getTime(); + CachedUserNameToIdResolver.GameProfileInfo profileInfo = new CachedUserNameToIdResolver.GameProfileInfo(profile, expirationDate); + this.safeAdd(profileInfo); - this.save(); + if (!org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) this.save(true); // Spigot - skip saving if disabled // Paper - Perf: Async GameProfileCache saving - return gameProfileInfo; + return profileInfo; } @@ -98,9 +_,24 @@ @@ -56,7 +56,7 @@ + // Paper start + @Override -+ public @javax.annotation.Nullable NameAndId getIfCached(String name) { ++ public @org.jspecify.annotations.Nullable NameAndId getIfCached(final String name) { + try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency + CachedUserNameToIdResolver.GameProfileInfo entry = this.profilesByName.get(name.toLowerCase(Locale.ROOT)); + if (entry == null) { @@ -69,50 +69,50 @@ + // Paper end + @Override - public Optional get(String name) { - String string = name.toLowerCase(Locale.ROOT); + public Optional get(final String name) { + String userName = name.toLowerCase(Locale.ROOT); + boolean stateLocked = true; try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency - CachedUserNameToIdResolver.GameProfileInfo gameProfileInfo = this.profilesByName.get(string); - boolean flag = false; - if (gameProfileInfo != null && new Date().getTime() >= gameProfileInfo.expirationDate.getTime()) { + CachedUserNameToIdResolver.GameProfileInfo profileInfo = this.profilesByName.get(userName); + boolean needsSave = false; + if (profileInfo != null && new Date().getTime() >= profileInfo.expirationDate.getTime()) { @@ -114,8 +_,13 @@ - if (gameProfileInfo != null) { - gameProfileInfo.setLastAccess(this.getNextOperation()); - optional = Optional.of(gameProfileInfo.nameAndId()); + if (profileInfo != null) { + profileInfo.setLastAccess(this.getNextOperation()); + result = Optional.of(profileInfo.nameAndId()); + stateLocked = false; this.stateLock.unlock(); // Paper - Fix GameProfileCache concurrency } else { -- Optional optional1 = this.lookupGameProfile(this.profileRepository, string); -+ Optional optional1; +- Optional profile = this.lookupGameProfile(this.profileRepository, userName); ++ Optional profile; + stateLocked = false; this.stateLock.unlock(); // Paper - Fix GameProfileCache concurrency + try { this.lookupLock.lock(); // Paper - Fix GameProfileCache concurrency -+ optional1 = this.lookupGameProfile(this.profileRepository, name); // CraftBukkit - use correct case for offline players ++ profile = this.lookupGameProfile(this.profileRepository, name); // CraftBukkit - use correct case for offline players + } finally { this.lookupLock.unlock(); } // Paper - Fix GameProfileCache concurrency - if (optional1.isPresent()) { - optional = Optional.of(this.addInternal(optional1.get()).nameAndId()); - flag = false; + if (profile.isPresent()) { + result = Optional.of(this.addInternal(profile.get()).nameAndId()); + needsSave = false; @@ -124,15 +_,17 @@ } } -- if (flag) { +- if (needsSave) { - this.save(); -+ if (flag && !org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) { // Spigot - skip saving if disabled ++ if (needsSave && !org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) { // Spigot - skip saving if disabled + this.save(true); // Paper - Perf: Async GameProfileCache saving } - return optional; + return result; + } finally { if (stateLocked) { this.stateLock.unlock(); } } // Paper - Fix GameProfileCache concurrency } @Override - public Optional get(UUID uuid) { + public Optional get(final UUID id) { + try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency - CachedUserNameToIdResolver.GameProfileInfo gameProfileInfo = this.profilesByUUID.get(uuid); - if (gameProfileInfo == null) { + CachedUserNameToIdResolver.GameProfileInfo profileInfo = this.profilesByUUID.get(id); + if (profileInfo == null) { return Optional.empty(); @@ -140,6 +_,7 @@ - gameProfileInfo.setLastAccess(this.getNextOperation()); - return Optional.of(gameProfileInfo.nameAndId()); + profileInfo.setLastAccess(this.getNextOperation()); + return Optional.of(profileInfo.nameAndId()); } + } finally { this.stateLock.unlock(); } // Paper - Fix GameProfileCache concurrency } @@ -139,17 +139,17 @@ + } + + @Override -+ public void save(boolean asyncSave) { ++ public void save(final boolean asyncSave) { + // Paper end - Perf: Async GameProfileCache saving - JsonArray jsonArray = new JsonArray(); + JsonArray entryList = new JsonArray(); DateFormat dateFormat = createDateFormat(); -- this.getTopMRUProfiles(1000).forEach(gameProfileInfo -> jsonArray.add(writeGameProfile(gameProfileInfo, dateFormat))); -+ this.listTopMRUProfiles(org.spigotmc.SpigotConfig.userCacheCap).forEach(gameProfileInfo -> jsonArray.add(writeGameProfile(gameProfileInfo, dateFormat))); // Spigot // Paper - Fix GameProfileCache concurrency - String string = this.gson.toJson((JsonElement)jsonArray); +- this.getTopMRUProfiles(1000).forEach(entry -> entryList.add(writeGameProfile(entry, dateFormat))); ++ this.listTopMRUProfiles(org.spigotmc.SpigotConfig.userCacheCap).forEach(entry -> entryList.add(writeGameProfile(entry, dateFormat))); // Spigot // Paper - Fix GameProfileCache concurrency + String toSave = this.gson.toJson((JsonElement)entryList); + Runnable save = () -> { // Paper - Perf: Async GameProfileCache saving try (Writer writer = Files.newWriter(this.file, StandardCharsets.UTF_8)) { - writer.write(string); + writer.write(toSave); } catch (IOException var9) { } + // Paper start - Perf: Async GameProfileCache saving @@ -162,7 +162,7 @@ + // Paper end - Perf: Async GameProfileCache saving } - private Stream getTopMRUProfiles(int limit) { + private Stream getTopMRUProfiles(final int limit) { - return ImmutableList.copyOf(this.profilesByUUID.values()) - .stream() - .sorted(Comparator.comparing(CachedUserNameToIdResolver.GameProfileInfo::lastAccess).reversed()) @@ -172,7 +172,7 @@ + return this.listTopMRUProfiles(limit).stream(); + } + -+ private List listTopMRUProfiles(int limit) { ++ private List listTopMRUProfiles(final int limit) { + try { + this.stateLock.lock(); + return this.profilesByUUID.values() @@ -186,5 +186,5 @@ + } + // Paper end - Fix GameProfileCache concurrency - private static JsonElement writeGameProfile(CachedUserNameToIdResolver.GameProfileInfo profileInfo, DateFormat format) { - JsonObject jsonObject = new JsonObject(); + private static JsonElement writeGameProfile(final CachedUserNameToIdResolver.GameProfileInfo src, final DateFormat dateFormat) { + JsonObject object = new JsonObject(); diff --git a/paper-server/patches/sources/net/minecraft/server/players/NameAndId.java.patch b/paper-server/patches/sources/net/minecraft/server/players/NameAndId.java.patch index d8dcdd205edc..fa72307779cf 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/NameAndId.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/NameAndId.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/server/players/NameAndId.java +++ b/net/minecraft/server/players/NameAndId.java @@ -48,4 +_,10 @@ - UUID uuid = UUIDUtil.createOfflinePlayerUUID(name); - return new NameAndId(uuid, name); + UUID id = UUIDUtil.createOfflinePlayerUUID(name); + return new NameAndId(id, name); } + + // Paper start - utility method for common conversion back to the game profile diff --git a/paper-server/patches/sources/net/minecraft/server/players/OldUsersConverter.java.patch b/paper-server/patches/sources/net/minecraft/server/players/OldUsersConverter.java.patch index e479bb8a83ce..02d4bec2ed83 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/OldUsersConverter.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/OldUsersConverter.java.patch @@ -2,59 +2,59 @@ +++ b/net/minecraft/server/players/OldUsersConverter.java @@ -50,7 +_,8 @@ - private static void lookupPlayers(MinecraftServer server, Collection names, ProfileLookupCallback callback) { - String[] strings = names.stream().filter(name -> !StringUtil.isNullOrEmpty(name)).toArray(String[]::new); + private static void lookupPlayers(final MinecraftServer server, final Collection names, final ProfileLookupCallback callback) { + String[] filteredNames = names.stream().filter(s -> !StringUtil.isNullOrEmpty(s)).toArray(String[]::new); - if (server.usesAuthentication()) { + if (server.usesAuthentication() || + (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode())) { // Spigot: bungee = online mode, for now. // Paper - Add setting for proxy online mode status - server.services().profileRepository().findProfilesByNames(strings, callback); + server.services().profileRepository().findProfilesByNames(filteredNames, callback); } else { - for (String string : strings) { + for (String name : filteredNames) { @@ -66,7 +_,7 @@ try { - userBanList.load(); + bans.load(); } catch (IOException var6) { -- LOGGER.warn("Could not load existing file {}", userBanList.getFile().getName(), var6); -+ LOGGER.warn("Could not load existing file {}", userBanList.getFile().getName()); // CraftBukkit - don't print stacktrace +- LOGGER.warn("Could not load existing file {}", bans.getFile().getName(), var6); ++ LOGGER.warn("Could not load existing file {}", bans.getFile().getName()); // CraftBukkit - don't print stacktrace } } @@ -122,7 +_,7 @@ try { - ipBanList.load(); + ipBans.load(); } catch (IOException var11) { -- LOGGER.warn("Could not load existing file {}", ipBanList.getFile().getName(), var11); -+ LOGGER.warn("Could not load existing file {}", ipBanList.getFile().getName()); // CraftBukkit - don't print stacktrace +- LOGGER.warn("Could not load existing file {}", ipBans.getFile().getName(), var11); ++ LOGGER.warn("Could not load existing file {}", ipBans.getFile().getName()); // CraftBukkit - don't print stacktrace } } @@ -158,7 +_,7 @@ try { - serverOpList.load(); + opsList.load(); } catch (IOException var6) { -- LOGGER.warn("Could not load existing file {}", serverOpList.getFile().getName(), var6); -+ LOGGER.warn("Could not load existing file {}", serverOpList.getFile().getName()); // CraftBukkit - don't print stacktrace +- LOGGER.warn("Could not load existing file {}", opsList.getFile().getName(), var6); ++ LOGGER.warn("Could not load existing file {}", opsList.getFile().getName()); // CraftBukkit - don't print stacktrace } } @@ -203,7 +_,7 @@ try { - userWhiteList.load(); + whitelist.load(); } catch (IOException var6) { -- LOGGER.warn("Could not load existing file {}", userWhiteList.getFile().getName(), var6); -+ LOGGER.warn("Could not load existing file {}", userWhiteList.getFile().getName()); // CraftBukkit - don't print stacktrace +- LOGGER.warn("Could not load existing file {}", whitelist.getFile().getName(), var6); ++ LOGGER.warn("Could not load existing file {}", whitelist.getFile().getName()); // CraftBukkit - don't print stacktrace } } @@ -317,6 +_,35 @@ - private void movePlayerFile(File file3, String oldFileName, String newFileName) { - File file4 = new File(worldPlayersDirectory, oldFileName + ".dat"); - File file5 = new File(file3, newFileName + ".dat"); + private void movePlayerFile(final File directory, final String oldName, final String newName) { + File oldFileName = new File(worldPlayerDirectory, oldName + ".dat"); + File newFileName = new File(directory, newName + ".dat"); + // CraftBukkit start - Use old file name to seed lastKnownName + net.minecraft.nbt.CompoundTag root = null; + + try { -+ root = net.minecraft.nbt.NbtIo.readCompressed(new java.io.FileInputStream(file4), net.minecraft.nbt.NbtAccounter.unlimitedHeap()); ++ root = net.minecraft.nbt.NbtIo.readCompressed(new java.io.FileInputStream(oldFileName), net.minecraft.nbt.NbtAccounter.unlimitedHeap()); + } catch (Exception exception) { + // Paper start + exception.printStackTrace(); @@ -67,10 +67,10 @@ + root.put("bukkit", new net.minecraft.nbt.CompoundTag()); + } + net.minecraft.nbt.CompoundTag data = root.getCompoundOrEmpty("bukkit"); -+ data.putString("lastKnownName", oldFileName); ++ data.putString("lastKnownName", oldName); + + try { -+ net.minecraft.nbt.NbtIo.writeCompressed(root, new java.io.FileOutputStream(file1)); ++ net.minecraft.nbt.NbtIo.writeCompressed(root, new java.io.FileOutputStream(oldFileName)); + } catch (Exception exception) { + // Paper start + exception.printStackTrace(); @@ -79,6 +79,6 @@ + } + } + // CraftBukkit end - OldUsersConverter.ensureDirectoryExists(file3); - if (!file4.renameTo(file5)) { - throw new OldUsersConverter.ConversionError("Could not convert file for " + oldFileName); + OldUsersConverter.ensureDirectoryExists(directory); + if (!oldFileName.renameTo(newFileName)) { + throw new OldUsersConverter.ConversionError("Could not convert file for " + oldName); diff --git a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch index 7ba449bd2628..4ffdf8e6e430 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch @@ -20,7 +20,7 @@ public final PlayerDataStorage playerIo; private final LayeredRegistryAccess registries; private int viewDistance; -@@ -129,9 +_,17 @@ +@@ -129,12 +_,20 @@ private boolean allowCommandsForAllPlayers; private int sendAllPlayerInfoIn; @@ -30,7 +30,10 @@ + public @Nullable String collideRuleTeamName; // Paper - Configurable player collision + public PlayerList( - MinecraftServer server, LayeredRegistryAccess registries, PlayerDataStorage playerIo, NotificationService notificationService + final MinecraftServer server, + final LayeredRegistryAccess registries, + final PlayerDataStorage playerIo, + final NotificationService notificationService ) { + this.cserver = server.server = new org.bukkit.craftbukkit.CraftServer((net.minecraft.server.dedicated.DedicatedServer) server, this); + server.console = new com.destroystokyo.paper.console.TerminalConsoleCommandSender(); // Paper @@ -38,63 +41,63 @@ this.server = server; this.registries = registries; this.playerIo = playerIo; -@@ -141,23 +_,19 @@ +@@ -144,23 +_,19 @@ this.ipBans = new IpBanList(IPBANLIST_FILE, notificationService); } + abstract public void loadAndSaveFiles(); // Paper - fix converting txt to json file; moved from DedicatedPlayerList constructor + - public void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie cookie) { + public void placeNewPlayer(final Connection connection, final ServerPlayer player, final CommonListenerCookie cookie) { + player.isRealPlayer = true; // Paper + player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed - NameAndId nameAndId = player.nameAndId(); - UserNameToIdResolver userNameToIdResolver = this.server.services().nameToIdCache(); - Optional optional = userNameToIdResolver.get(nameAndId.id()); - String string = optional.map(NameAndId::name).orElse(nameAndId.name()); -+ if (player.lastKnownName != null) { string = player.lastKnownName; player.lastKnownName = null; } // CraftBukkit - Better rename detection - userNameToIdResolver.add(nameAndId); - ServerLevel serverLevel = player.level(); - String loggableAddress = connection.getLoggableAddress(this.server.logIPs()); + NameAndId gameProfile = player.nameAndId(); + UserNameToIdResolver profileCache = this.server.services().nameToIdCache(); + Optional oldProfile = profileCache.get(gameProfile.id()); + String oldName = oldProfile.map(NameAndId::name).orElse(gameProfile.name()); ++ if (player.lastKnownName != null) { oldName = player.lastKnownName; player.lastKnownName = null; } // CraftBukkit - Better rename detection + profileCache.add(gameProfile); + ServerLevel level = player.level(); + String address = connection.getLoggableAddress(this.server.logIPs()); - LOGGER.info( - "{}[{}] logged in with entity id {} at ({}, {}, {})", - player.getPlainTextName(), -- loggableAddress, +- address, - player.getId(), - player.getX(), - player.getY(), - player.getZ() - ); - LevelData levelData = serverLevel.getLevelData(); - ServerGamePacketListenerImpl serverGamePacketListenerImpl = new ServerGamePacketListenerImpl(this.server, connection, player, cookie); + LevelData levelData = level.getLevelData(); + ServerGamePacketListenerImpl playerConnection = new ServerGamePacketListenerImpl(this.server, connection, player, cookie); connection.setupInboundProtocol( -@@ -175,8 +_,8 @@ +@@ -177,8 +_,8 @@ levelData.isHardcore(), this.server.levelKeys(), this.getMaxPlayers(), - this.getViewDistance(), - this.getSimulationDistance(), -+ io.papermc.paper.FeatureHooks.getViewDistance(serverLevel), // Paper - view distance -+ io.papermc.paper.FeatureHooks.getSimulationDistance(serverLevel), // Paper - simulation distance - flag1, - !flag, - flag2, -@@ -184,6 +_,7 @@ ++ io.papermc.paper.FeatureHooks.getViewDistance(level), // Paper - view distance ++ io.papermc.paper.FeatureHooks.getSimulationDistance(level), // Paper - simulation distance + reducedDebugInfo, + !immediateRespawn, + doLimitedCrafting, +@@ -186,6 +_,7 @@ this.server.enforceSecureProfile() ) ); + player.getBukkitEntity().sendSupportedChannels(); // CraftBukkit - serverGamePacketListenerImpl.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked())); - serverGamePacketListenerImpl.send(new ClientboundPlayerAbilitiesPacket(player.getAbilities())); - serverGamePacketListenerImpl.send(new ClientboundSetHeldSlotPacket(player.getInventory().getSelectedSlot())); -@@ -203,24 +_,129 @@ - mutableComponent = Component.translatable("multiplayer.player.joined.renamed", player.getDisplayName(), string); + playerConnection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked())); + playerConnection.send(new ClientboundPlayerAbilitiesPacket(player.getAbilities())); + playerConnection.send(new ClientboundSetHeldSlotPacket(player.getInventory().getSelectedSlot())); +@@ -205,24 +_,129 @@ + component = Component.translatable("multiplayer.player.joined.renamed", player.getDisplayName(), oldName); } -- this.broadcastSystemMessage(mutableComponent.withStyle(ChatFormatting.YELLOW), false); +- this.broadcastSystemMessage(component.withStyle(ChatFormatting.YELLOW), false); + // CraftBukkit start -+ mutableComponent.withStyle(ChatFormatting.YELLOW); -+ Component joinMessage = mutableComponent; // Paper - Adventure - serverGamePacketListenerImpl.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot()); ++ component.withStyle(ChatFormatting.YELLOW); ++ Component joinMessage = component; // Paper - Adventure + playerConnection.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot()); ServerStatus status = this.server.getStatus(); if (status != null && !cookie.transferred()) { player.sendServerStatus(status); @@ -109,8 +112,8 @@ + // this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); // CraftBukkit - replaced with loop below + // Paper start - Fire PlayerJoinEvent when Player is actually ready; correctly register player BEFORE PlayerJoinEvent, so the entity is valid and doesn't require tick delay hacks + player.suppressTrackerForLogin = true; - this.sendLevelInfo(player, serverLevel); - serverLevel.addNewPlayer(player); + this.sendLevelInfo(player, level); + level.addNewPlayer(player); - this.server.getCustomBossEvents().onPlayerConnect(player); + this.server.getCustomBossEvents().onPlayerConnect(player); // see commented out section below serverLevel.addPlayerJoin(player); + // Paper end - Fire PlayerJoinEvent when Player is actually ready @@ -121,7 +124,7 @@ + // Ensure that player inventory is populated with its viewer + player.containerMenu.transferTo(player.containerMenu, bukkitPlayer); + -+ org.bukkit.event.player.PlayerJoinEvent playerJoinEvent = new org.bukkit.event.player.PlayerJoinEvent(bukkitPlayer, io.papermc.paper.adventure.PaperAdventure.asAdventure(mutableComponent)); // Paper - Adventure ++ org.bukkit.event.player.PlayerJoinEvent playerJoinEvent = new org.bukkit.event.player.PlayerJoinEvent(bukkitPlayer, io.papermc.paper.adventure.PaperAdventure.asAdventure(component)); // Paper - Adventure + this.cserver.getPluginManager().callEvent(playerJoinEvent); + + if (!player.connection.isAcceptingMessages()) { @@ -173,21 +176,21 @@ + + //player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn // Paper - THIS IS NOT NEEDED ANYMORE + -+ this.sendLevelInfo(player, serverLevel); ++ this.sendLevelInfo(player, level); + + // CraftBukkit start - Only add if the player wasn't moved in the event -+ if (player.level() == serverLevel && !serverLevel.players().contains(player)) { -+ serverLevel.addNewPlayer(player); ++ if (player.level() == level && !level.players().contains(player)) { ++ level.addNewPlayer(player); + this.server.getCustomBossEvents().onPlayerConnect(player); + } + -+ serverLevel = player.level(); // CraftBukkit - Update in case join event changed it ++ level = player.level(); // CraftBukkit - Update in case join event changed it + // CraftBukkit end this.sendActivePlayerEffects(player); + // Paper - move loading pearls / parent vehicle up player.initInventoryMenu(); this.server.notificationManager().playerJoined(player); - serverGamePacketListenerImpl.resumeFlushing(); + playerConnection.resumeFlushing(); + // Paper start - Configurable player collision; Add to collideRule team if needed + final net.minecraft.world.scores.Scoreboard scoreboard = this.getServer().getLevel(Level.OVERWORLD).getScoreboard(); + final PlayerTeam collideRuleTeam = scoreboard.getPlayerTeam(this.collideRuleTeamName); @@ -197,11 +200,11 @@ + // Paper end - Configurable player collision + // CraftBukkit start - moved down + LOGGER.info( -+ "{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", // CraftBukkit - add world name ++ "{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", // Paper - add world identifier + player.getPlainTextName(), -+ loggableAddress, ++ address, + player.getId(), -+ serverLevel.serverLevelData.getLevelName(), // CraftBukkit - add world name ++ level.dimension().identifier(), // Paper - add world identifier + player.getX(), + player.getY(), + player.getZ() @@ -209,18 +212,18 @@ + // CraftBukkit end - moved down + // Paper start - Send empty chunk, so players aren't stuck in the world loading screen with our chunk system not sending chunks when dead + if (player.isDeadOrDying()) { -+ net.minecraft.core.Holder plains = serverLevel.registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.BIOME) ++ net.minecraft.core.Holder plains = level.registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.BIOME) + .getOrThrow(net.minecraft.world.level.biome.Biomes.PLAINS); + player.connection.send(new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket( -+ new net.minecraft.world.level.chunk.EmptyLevelChunk(serverLevel, player.chunkPosition(), plains), -+ serverLevel.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null) ++ new net.minecraft.world.level.chunk.EmptyLevelChunk(level, player.chunkPosition(), plains), ++ level.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null) + ); + } + // Paper end - Send empty chunk } - public void updateEntireScoreboard(ServerScoreboard scoreboard, ServerPlayer player) { -@@ -242,31 +_,40 @@ + public void updateEntireScoreboard(final ServerScoreboard scoreboard, final ServerPlayer player) { +@@ -244,6 +_,15 @@ } } @@ -235,64 +238,67 @@ + // Paper end - virtual world border API public void addWorldborderListener(final ServerLevel level) { level.getWorldBorder().addListener(new BorderChangeListener() { + { +@@ -252,27 +_,27 @@ + @Override - public void onSetSize(WorldBorder border, double size) { + public void onSetSize(final WorldBorder border, final double newSize) { - PlayerList.this.broadcastAll(new ClientboundSetBorderSizePacket(border), level.dimension()); + PlayerList.this.broadcastWorldborder(new ClientboundSetBorderSizePacket(border), level.dimension()); // Paper - virtual world border API } @Override - public void onLerpSize(WorldBorder border, double oldSize, double newSize, long time, long startTime) { + public void onLerpSize(final WorldBorder border, final double fromSize, final double targetSize, final long ticks, final long gameTime) { - PlayerList.this.broadcastAll(new ClientboundSetBorderLerpSizePacket(border), level.dimension()); + PlayerList.this.broadcastWorldborder(new ClientboundSetBorderLerpSizePacket(border), level.dimension()); // Paper - virtual world border API } @Override - public void onSetCenter(WorldBorder border, double x, double z) { + public void onSetCenter(final WorldBorder border, final double x, final double z) { - PlayerList.this.broadcastAll(new ClientboundSetBorderCenterPacket(border), level.dimension()); + PlayerList.this.broadcastWorldborder(new ClientboundSetBorderCenterPacket(border), level.dimension()); // Paper - virtual world border API } @Override - public void onSetWarningTime(WorldBorder border, int warningTime) { + public void onSetWarningTime(final WorldBorder border, final int time) { - PlayerList.this.broadcastAll(new ClientboundSetBorderWarningDelayPacket(border), level.dimension()); + PlayerList.this.broadcastWorldborder(new ClientboundSetBorderWarningDelayPacket(border), level.dimension()); // Paper - virtual world border API } @Override - public void onSetWarningBlocks(WorldBorder border, int warningBlocks) { + public void onSetWarningBlocks(final WorldBorder border, final int blocks) { - PlayerList.this.broadcastAll(new ClientboundSetBorderWarningDistancePacket(border), level.dimension()); + PlayerList.this.broadcastWorldborder(new ClientboundSetBorderWarningDistancePacket(border), level.dimension()); // Paper - virtual world border API } @Override -@@ -290,56 +_,137 @@ +@@ -296,64 +_,148 @@ } - protected void save(ServerPlayer player) { + protected void save(final ServerPlayer player) { + if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit this.playerIo.save(player); -- ServerStatsCounter serverStatsCounter = this.stats.get(player.getUUID()); -+ ServerStatsCounter serverStatsCounter = player.getStats(); // CraftBukkit - if (serverStatsCounter != null) { - serverStatsCounter.save(); +- ServerStatsCounter stats = this.stats.get(player.getUUID()); ++ ServerStatsCounter stats = player.getStats(); // CraftBukkit + if (stats != null) { + stats.save(); } -- PlayerAdvancements playerAdvancements = this.advancements.get(player.getUUID()); -+ PlayerAdvancements playerAdvancements = player.getAdvancements(); // CraftBukkit - if (playerAdvancements != null) { - playerAdvancements.save(); +- PlayerAdvancements advancements = this.advancements.get(player.getUUID()); ++ PlayerAdvancements advancements = player.getAdvancements(); // CraftBukkit + if (advancements != null) { + advancements.save(); } } -- public void remove(ServerPlayer player) { -+ public net.kyori.adventure.text.@Nullable Component remove(ServerPlayer player) { // CraftBukkit - return string // Paper - return Component +- public void remove(final ServerPlayer player) { ++ public net.kyori.adventure.text.@Nullable Component remove(final ServerPlayer player) { // CraftBukkit - return string // Paper - return Component + // Paper start - Fix kick event leave message not being sent + return this.remove(player, net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? player.getBukkitEntity().displayName() : io.papermc.paper.adventure.PaperAdventure.asAdventure(player.getDisplayName()))); + } -+ public net.kyori.adventure.text.@Nullable Component remove(ServerPlayer player, net.kyori.adventure.text.Component leaveMessage) { ++ public net.kyori.adventure.text.@Nullable Component remove(final ServerPlayer player, final net.kyori.adventure.text.Component leaveMessage) { + // Paper end - Fix kick event leave message not being sent - ServerLevel serverLevel = player.level(); + ServerLevel level = player.level(); player.awardStat(Stats.LEAVE_GAME); + // CraftBukkit start - Quitting must be before we do final save of data, in case plugins need to modify it + // See SPIGOT-5799, SPIGOT-6145 @@ -326,37 +332,37 @@ + // Paper end - Drop carried item when player has disconnected this.save(player); if (player.isPassenger()) { - Entity rootVehicle = player.getRootVehicle(); - if (rootVehicle.hasExactlyOnePlayerPassenger()) { + Entity vehicle = player.getRootVehicle(); + if (vehicle.hasExactlyOnePlayerPassenger()) { LOGGER.debug("Removing player mount"); player.stopRiding(); -- rootVehicle.getPassengersAndSelf().forEach(entity -> entity.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER)); -+ rootVehicle.getPassengersAndSelf().forEach(entity -> { +- vehicle.getPassengersAndSelf().forEach(e -> e.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER)); ++ vehicle.getPassengersAndSelf().forEach(e -> { + // Paper start - Fix villager boat exploit -+ if (entity instanceof net.minecraft.world.entity.npc.villager.AbstractVillager villager) { ++ if (e instanceof net.minecraft.world.entity.npc.villager.AbstractVillager villager) { + final net.minecraft.world.entity.player.Player human = villager.getTradingPlayer(); + if (human != null) { + villager.setTradingPlayer(null); + } + } + // Paper end - Fix villager boat exploit -+ entity.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER, org.bukkit.event.entity.EntityRemoveEvent.Cause.PLAYER_QUIT); // CraftBukkit - add Bukkit remove cause ++ e.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER, org.bukkit.event.entity.EntityRemoveEvent.Cause.PLAYER_QUIT); // CraftBukkit - add Bukkit remove cause + }); } } player.unRide(); - for (ThrownEnderpearl thrownEnderpearl : player.getEnderPearls()) { -- thrownEnderpearl.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER); + for (ThrownEnderpearl enderpearl : player.getEnderPearls()) { +- enderpearl.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER); + // Paper start - Allow using old ender pearl behavior -+ if (!thrownEnderpearl.level().paperConfig().misc.legacyEnderPearlBehavior) { -+ thrownEnderpearl.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER, org.bukkit.event.entity.EntityRemoveEvent.Cause.PLAYER_QUIT); // CraftBukkit - add Bukkit remove cause ++ if (!enderpearl.level().paperConfig().misc.legacyEnderPearlBehavior) { ++ enderpearl.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER, org.bukkit.event.entity.EntityRemoveEvent.Cause.PLAYER_QUIT); // CraftBukkit - add Bukkit remove cause + } + // Paper end - Allow using old ender pearl behavior } - serverLevel.removePlayerImmediately(player, Entity.RemovalReason.UNLOADED_WITH_PLAYER); + level.removePlayerImmediately(player, Entity.RemovalReason.UNLOADED_WITH_PLAYER); + player.retireScheduler(); // Paper - Folia schedulers player.getAdvancements().stopListening(); this.players.remove(player); @@ -378,9 +384,9 @@ - this.broadcastAll(new ClientboundPlayerInfoRemovePacket(List.of(player.getUUID()))); - } - -- public @Nullable Component canPlayerLogin(SocketAddress socketAddress, NameAndId nameAndId) { +- public @Nullable Component canPlayerLogin(final SocketAddress address, final NameAndId nameAndId) { - if (this.bans.isBanned(nameAndId)) { -- UserBanListEntry userBanListEntry = this.bans.get(nameAndId); +- UserBanListEntry ban = this.bans.get(nameAndId); + // CraftBukkit start + // this.broadcastAll(new ClientboundPlayerInfoRemovePacket(List.of(player.getUUID()))); + ClientboundPlayerInfoRemovePacket packet = new ClientboundPlayerInfoRemovePacket(List.of(player.getUUID())); @@ -408,36 +414,35 @@ + } + } + // Paper end - PlayerLoginEvent -+ public LoginResult canPlayerLogin(SocketAddress socketAddress, NameAndId nameAndId) { // Paper - PlayerLoginEvent ++ public LoginResult canPlayerLogin(final SocketAddress address, final NameAndId nameAndId) { // Paper - PlayerLoginEvent + LoginResult whitelistEventResult; // Paper + // Paper start - Fix MC-158900 -+ UserBanListEntry userBanListEntry; -+ if (this.bans.isBanned(nameAndId) && (userBanListEntry = this.bans.get(nameAndId)) != null) { ++ UserBanListEntry ban1; ++ if (this.bans.isBanned(nameAndId) && (ban1 = this.bans.get(nameAndId)) != null) { ++ UserBanListEntry ban = ban1; + // Paper end - Fix MC-158900 - MutableComponent mutableComponent = Component.translatable("multiplayer.disconnect.banned.reason", userBanListEntry.getReasonMessage()); - if (userBanListEntry.getExpires() != null) { - mutableComponent.append( -@@ -347,9 +_,11 @@ - ); + MutableComponent reason = Component.translatable("multiplayer.disconnect.banned.reason", ban.getReasonMessage()); + if (ban.getExpires() != null) { + reason.append(Component.translatable("multiplayer.disconnect.banned.expiration", BAN_DATE_FORMAT.format(ban.getExpires()))); } -- return mutableComponent; +- return reason; - } else if (!this.isWhiteListed(nameAndId)) { - return Component.translatable("multiplayer.disconnect.not_whitelisted"); -+ return new LoginResult(mutableComponent, org.bukkit.event.player.PlayerLoginEvent.Result.KICK_BANNED); // Paper - PlayerLoginEvent ++ return new LoginResult(reason, org.bukkit.event.player.PlayerLoginEvent.Result.KICK_BANNED); // Paper - PlayerLoginEvent + // Paper start - whitelist event + } else if ((whitelistEventResult = this.isWhiteListedLogin(nameAndId)).result == org.bukkit.event.player.PlayerLoginEvent.Result.KICK_WHITELIST) { + return whitelistEventResult; + // Paper end - } else if (this.ipBans.isBanned(socketAddress)) { - IpBanListEntry ipBanListEntry = this.ipBans.get(socketAddress); - MutableComponent mutableComponent = Component.translatable("multiplayer.disconnect.banned_ip.reason", ipBanListEntry.getReasonMessage()); -@@ -359,19 +_,18 @@ - ); + } else if (this.ipBans.isBanned(address)) { + IpBanListEntry ban = this.ipBans.get(address); + MutableComponent reason = Component.translatable("multiplayer.disconnect.banned_ip.reason", ban.getReasonMessage()); +@@ -361,19 +_,18 @@ + reason.append(Component.translatable("multiplayer.disconnect.banned_ip.expiration", BAN_DATE_FORMAT.format(ban.getExpires()))); } -- return mutableComponent; -+ return new LoginResult(mutableComponent, org.bukkit.event.player.PlayerLoginEvent.Result.KICK_BANNED); // Paper - PlayerLoginEvent +- return reason; ++ return new LoginResult(reason, org.bukkit.event.player.PlayerLoginEvent.Result.KICK_BANNED); // Paper - PlayerLoginEvent } else { - return this.players.size() >= this.getMaxPlayers() && !this.canBypassPlayerLimit(nameAndId) - ? Component.translatable("multiplayer.disconnect.server_full") @@ -446,153 +451,153 @@ } } -- public boolean disconnectAllPlayersWithProfile(UUID profileId) { -+ public boolean disconnectAllPlayersWithProfile(GameProfile profile) { // Paper - validate usernames -+ UUID profileId = profile.id(); // Paper - validate usernames - Set set = Sets.newIdentityHashSet(); +- public boolean disconnectAllPlayersWithProfile(final UUID playerId) { ++ public boolean disconnectAllPlayersWithProfile(final GameProfile profile) { // Paper - validate usernames ++ UUID playerId = profile.id(); // Paper - validate usernames + Set dupes = Sets.newIdentityHashSet(); - for (ServerPlayer serverPlayer : this.players) { -- if (serverPlayer.getUUID().equals(profileId)) { -+ if (serverPlayer.getUUID().equals(profileId) || (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() && serverPlayer.getGameProfile().name().equalsIgnoreCase(profile.name()))) { // Paper - validate usernames - set.add(serverPlayer); + for (ServerPlayer player : this.players) { +- if (player.getUUID().equals(playerId)) { ++ if (player.getUUID().equals(playerId) || (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() && player.getGameProfile().name().equalsIgnoreCase(profile.name()))) { // Paper - validate usernames + dupes.add(player); } } -@@ -382,23 +_,31 @@ +@@ -384,23 +_,31 @@ } - for (ServerPlayer serverPlayer2 : set) { -- serverPlayer2.connection.disconnect(DUPLICATE_LOGIN_DISCONNECT_MESSAGE); -+ serverPlayer2.connection.disconnect(DUPLICATE_LOGIN_DISCONNECT_MESSAGE, io.papermc.paper.connection.DisconnectionReason.DUPLICATE_LOGIN_MESSAGE); // Paper - disconnect API + for (ServerPlayer playerx : dupes) { +- playerx.connection.disconnect(DUPLICATE_LOGIN_DISCONNECT_MESSAGE); ++ playerx.connection.disconnect(DUPLICATE_LOGIN_DISCONNECT_MESSAGE, io.papermc.paper.connection.DisconnectionReason.DUPLICATE_LOGIN_MESSAGE); // Paper - disconnect API } - return !set.isEmpty(); + return !dupes.isEmpty(); } -- public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason) { -- TeleportTransition teleportTransition = player.findRespawnPositionAndUseSpawnBlock(!keepInventory, TeleportTransition.DO_NOTHING); +- public ServerPlayer respawn(final ServerPlayer serverPlayer, final boolean keepAllPlayerData, final Entity.RemovalReason removalReason) { +- TeleportTransition respawnInfo = serverPlayer.findRespawnPositionAndUseSpawnBlock(!keepAllPlayerData, TeleportTransition.DO_NOTHING); + // Paper start - respawn event -+ public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason respawnReason) { -+ ServerPlayer.RespawnResult result = player.findRespawnPositionAndUseSpawnBlock0(!keepInventory, TeleportTransition.DO_NOTHING, respawnReason); ++ public ServerPlayer respawn(final ServerPlayer serverPlayer, final boolean keepAllPlayerData, final Entity.RemovalReason removalReason, final org.bukkit.event.player.PlayerRespawnEvent.RespawnReason respawnReason) { ++ ServerPlayer.RespawnResult result = serverPlayer.findRespawnPositionAndUseSpawnBlock0(!keepAllPlayerData, TeleportTransition.DO_NOTHING, respawnReason); + if (result == null) { // disconnected player during the respawn event -+ return player; ++ return serverPlayer; + } -+ TeleportTransition teleportTransition = result.transition(); -+ Level fromLevel = player.level(); ++ TeleportTransition respawnInfo = result.transition(); ++ Level fromLevel = serverPlayer.level(); + // Paper end - respawn event - this.players.remove(player); -+ this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Paper - player.level().removePlayerImmediately(player, reason); - ServerLevel level = teleportTransition.newLevel(); -- ServerPlayer serverPlayer = new ServerPlayer(this.server, level, player.getGameProfile(), player.clientInformation()); -+ ServerPlayer serverPlayer = player; // Paper - TODO - recreate instance - serverPlayer.connection = player.connection; - serverPlayer.restoreFrom(player, keepInventory); - serverPlayer.setId(player.getId()); - serverPlayer.setMainArm(player.getMainArm()); -- if (!teleportTransition.missingRespawnBlock()) { -+ if (false && !teleportTransition.missingRespawnBlock()) { // Paper - Once we not reuse the player entity, this can be flipped again but without the events being fired - serverPlayer.copyRespawnPosition(player); + this.players.remove(serverPlayer); ++ this.playersByName.remove(serverPlayer.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Paper + serverPlayer.level().removePlayerImmediately(serverPlayer, removalReason); + ServerLevel level = respawnInfo.newLevel(); +- ServerPlayer player = new ServerPlayer(this.server, level, serverPlayer.getGameProfile(), serverPlayer.clientInformation()); ++ ServerPlayer player = serverPlayer; // Paper - TODO - recreate instance + player.connection = serverPlayer.connection; + player.restoreFrom(serverPlayer, keepAllPlayerData); + player.setId(serverPlayer.getId()); + player.setMainArm(serverPlayer.getMainArm()); +- if (!respawnInfo.missingRespawnBlock()) { ++ if (false && !respawnInfo.missingRespawnBlock()) { // Paper - Once we not reuse the player entity, this can be flipped again but without the events being fired + player.copyRespawnPosition(serverPlayer); } -@@ -406,17 +_,26 @@ - serverPlayer.addTag(string); +@@ -408,17 +_,26 @@ + player.addTag(tag); } + // Paper start - Once we not reuse the player entity we can remove this. -+ if (!keepInventory) player.reset(); -+ serverPlayer.spawnIn(level); -+ serverPlayer.unsetRemoved(); -+ serverPlayer.setShiftKeyDown(false); ++ if (!keepAllPlayerData) serverPlayer.reset(); ++ player.spawnIn(level); ++ player.unsetRemoved(); ++ player.setShiftKeyDown(false); + // Paper end - Vec3 vec3 = teleportTransition.position(); - serverPlayer.snapTo(vec3.x, vec3.y, vec3.z, teleportTransition.yRot(), teleportTransition.xRot()); -+ serverPlayer.connection.resetPosition(); // Paper - Fix SPIGOT-1903, MC-98153 -+ level.getChunkSource().addTicketWithRadius(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(net.minecraft.util.Mth.floor(vec3.x()) >> 4, net.minecraft.util.Mth.floor(vec3.z()) >> 4), 1); // Paper - post teleport ticket type - if (teleportTransition.missingRespawnBlock()) { - serverPlayer.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.NO_RESPAWN_BLOCK_AVAILABLE, 0.0F)); -+ serverPlayer.setRespawnPosition(null, false, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.PLAYER_RESPAWN); // CraftBukkit - SPIGOT-5988: Clear respawn location when obstructed + Vec3 pos = respawnInfo.position(); + player.snapTo(pos.x, pos.y, pos.z, respawnInfo.yRot(), respawnInfo.xRot()); ++ player.connection.resetPosition(); // Paper - Fix SPIGOT-1903, MC-98153 ++ level.getChunkSource().addTicketWithRadius(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(net.minecraft.util.Mth.floor(pos.x()) >> 4, net.minecraft.util.Mth.floor(pos.z()) >> 4), 1); // Paper - post teleport ticket type + if (respawnInfo.missingRespawnBlock()) { + player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.NO_RESPAWN_BLOCK_AVAILABLE, 0.0F)); ++ player.setRespawnPosition(null, false, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.PLAYER_RESPAWN); // CraftBukkit - SPIGOT-5988: Clear respawn location when obstructed } - byte b = keepInventory ? ClientboundRespawnPacket.KEEP_ATTRIBUTE_MODIFIERS : 0; - ServerLevel serverLevel = serverPlayer.level(); - LevelData levelData = serverLevel.getLevelData(); - serverPlayer.connection.send(new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(serverLevel), b)); -- serverPlayer.connection.teleport(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ(), serverPlayer.getYRot(), serverPlayer.getXRot()); -+ serverPlayer.connection.internalTeleport(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ(), serverPlayer.getYRot(), serverPlayer.getXRot()); // Paper - serverPlayer.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getRespawnData())); - serverPlayer.connection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked())); - serverPlayer.connection -@@ -426,9 +_,15 @@ - this.sendPlayerPermissionLevel(serverPlayer); - level.addRespawnedPlayer(serverPlayer); - this.players.add(serverPlayer); -+ this.playersByName.put(serverPlayer.getScoreboardName().toLowerCase(java.util.Locale.ROOT), serverPlayer); // Paper - this.playersByUUID.put(serverPlayer.getUUID(), serverPlayer); - serverPlayer.initInventoryMenu(); - serverPlayer.setHealth(serverPlayer.getHealth()); + byte dataToKeep = keepAllPlayerData ? ClientboundRespawnPacket.KEEP_ATTRIBUTE_MODIFIERS : 0; + ServerLevel playerLevel = player.level(); + LevelData levelData = playerLevel.getLevelData(); + player.connection.send(new ClientboundRespawnPacket(player.createCommonSpawnInfo(playerLevel), dataToKeep)); +- player.connection.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot()); ++ player.connection.internalTeleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot()); // Paper + player.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getRespawnData())); + player.connection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked())); + player.connection.send(new ClientboundSetExperiencePacket(player.experienceProgress, player.totalExperience, player.experienceLevel)); +@@ -427,9 +_,15 @@ + this.sendPlayerPermissionLevel(player); + level.addRespawnedPlayer(player); + this.players.add(player); ++ this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Paper + this.playersByUUID.put(player.getUUID(), player); + player.initInventoryMenu(); + player.setHealth(player.getHealth()); + // Paper start - Once we not reuse the player entity we can remove this. + // But we have to resend the player info as it's not marked as dirty -+ this.sendAllPlayerInfo(player); // Update health -+ player.onUpdateAbilities(); // Update inventory, etc ++ this.sendAllPlayerInfo(serverPlayer); // Update health ++ serverPlayer.onUpdateAbilities(); // Update inventory, etc + // Paper end - ServerPlayer.RespawnConfig respawnConfig = serverPlayer.getRespawnConfig(); - if (!keepInventory && respawnConfig != null) { + ServerPlayer.RespawnConfig respawnConfig = player.getRespawnConfig(); + if (!keepAllPlayerData && respawnConfig != null) { LevelData.RespawnData respawnData = respawnConfig.respawnData(); -@@ -454,6 +_,29 @@ +@@ -455,6 +_,29 @@ } } + // Paper start + // Save player file again if they were disconnected -+ if (serverPlayer.connection.isDisconnected()) { -+ this.save(serverPlayer); ++ if (player.connection.isDisconnected()) { ++ this.save(player); + } + + // It's possible for respawn to be in a diff dimension + if (fromLevel != level) { -+ new org.bukkit.event.player.PlayerChangedWorldEvent(serverPlayer.getBukkitEntity(), fromLevel.getWorld()).callEvent(); -+ serverPlayer.triggerDimensionChangeTriggers(level); ++ new org.bukkit.event.player.PlayerChangedWorldEvent(player.getBukkitEntity(), fromLevel.getWorld()).callEvent(); ++ player.triggerDimensionChangeTriggers(level); + } + + // Call post respawn event + new com.destroystokyo.paper.event.player.PlayerPostRespawnEvent( -+ serverPlayer.getBukkitEntity(), -+ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(teleportTransition.position(), level, teleportTransition.yRot(), teleportTransition.xRot()), ++ player.getBukkitEntity(), ++ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(respawnInfo.position(), level, respawnInfo.yRot(), respawnInfo.xRot()), + result.isBedSpawn(), + result.isAnchorSpawn(), -+ teleportTransition.missingRespawnBlock(), ++ respawnInfo.missingRespawnBlock(), + respawnReason + ).callEvent(); + // Paper end + - return serverPlayer; + return player; } -@@ -462,23 +_,58 @@ +@@ -463,23 +_,58 @@ } - public void sendActiveEffects(LivingEntity entity, ServerGamePacketListenerImpl connection) { + public void sendActiveEffects(final LivingEntity livingEntity, final ServerGamePacketListenerImpl connection) { + // Paper start - collect packets -+ this.sendActiveEffects(entity, connection::send); ++ this.sendActiveEffects(livingEntity, connection::send); + } -+ public void sendActiveEffects(LivingEntity entity, java.util.function.Consumer> packetConsumer) { ++ public void sendActiveEffects(final LivingEntity livingEntity, final java.util.function.Consumer> packetConsumer) { + // Paper end - collect packets - for (MobEffectInstance mobEffectInstance : entity.getActiveEffects()) { -- connection.send(new ClientboundUpdateMobEffectPacket(entity.getId(), mobEffectInstance, false)); -+ packetConsumer.accept(new ClientboundUpdateMobEffectPacket(entity.getId(), mobEffectInstance, false)); // Paper - collect packets + for (MobEffectInstance effect : livingEntity.getActiveEffects()) { +- connection.send(new ClientboundUpdateMobEffectPacket(livingEntity.getId(), effect, false)); ++ packetConsumer.accept(new ClientboundUpdateMobEffectPacket(livingEntity.getId(), effect, false)); // Paper - collect packets } } - public void sendPlayerPermissionLevel(ServerPlayer player) { + public void sendPlayerPermissionLevel(final ServerPlayer player) { + // Paper start - avoid recalculating permissions if possible + this.sendPlayerPermissionLevel(player, true); + } + -+ public void sendPlayerPermissionLevel(ServerPlayer player, boolean recalculatePermissions) { ++ public void sendPlayerPermissionLevel(final ServerPlayer player, final boolean recalculatePermissions) { + // Paper end - avoid recalculating permissions if possible - LevelBasedPermissionSet profilePermissions = this.server.getProfilePermissions(player.nameAndId()); -- this.sendPlayerPermissionLevel(player, profilePermissions); -+ this.sendPlayerPermissionLevel(player, profilePermissions, recalculatePermissions); // Paper - avoid recalculating permissions if possible + LevelBasedPermissionSet permissions = this.server.getProfilePermissions(player.nameAndId()); +- this.sendPlayerPermissionLevel(player, permissions); ++ this.sendPlayerPermissionLevel(player, permissions, recalculatePermissions); // Paper - avoid recalculating permissions if possible } public void tick() { @@ -627,24 +632,24 @@ + } + // CraftBukkit end + - public void broadcastAll(Packet packet) { - for (ServerPlayer serverPlayer : this.players) { - serverPlayer.connection.send(packet); -@@ -564,6 +_,12 @@ + public void broadcastAll(final Packet packet) { + for (ServerPlayer player : this.players) { + player.connection.send(packet); +@@ -565,6 +_,12 @@ } - private void sendPlayerPermissionLevel(ServerPlayer player, LevelBasedPermissionSet permissions) { + private void sendPlayerPermissionLevel(final ServerPlayer player, final LevelBasedPermissionSet permissions) { + // Paper start - Add sendOpLevel API + this.sendPlayerPermissionLevel(player, permissions, true); + } + -+ public void sendPlayerPermissionLevel(ServerPlayer player, LevelBasedPermissionSet permissions, boolean recalculatePermissions) { ++ public void sendPlayerPermissionLevel(final ServerPlayer player, final LevelBasedPermissionSet permissions, final boolean recalculatePermissions) { + // Paper end - Add sendOpLevel API if (player.connection != null) { - byte b = switch (permissions.level()) { + byte eventId = switch (permissions.level()) { case ALL -> EntityEvent.PERMISSION_LEVEL_ALL; -@@ -575,9 +_,48 @@ - player.connection.send(new ClientboundEntityEventPacket(player, b)); +@@ -576,9 +_,48 @@ + player.connection.send(new ClientboundEntityEventPacket(player, eventId)); } + if (recalculatePermissions) { // Paper - Add sendOpLevel API @@ -691,38 +696,40 @@ + // Paper end + + @io.papermc.paper.annotation.DoNotUse // Paper - public boolean isWhiteListed(NameAndId nameAndId) { + public boolean isWhiteListed(final NameAndId nameAndId) { return !this.isUsingWhitelist() || this.ops.contains(nameAndId) || this.whitelist.contains(nameAndId); } -@@ -589,21 +_,17 @@ +@@ -590,16 +_,7 @@ } - public @Nullable ServerPlayer getPlayerByName(String username) { + public @Nullable ServerPlayer getPlayerByName(final String name) { - int size = this.players.size(); - - for (int i = 0; i < size; i++) { -- ServerPlayer serverPlayer = this.players.get(i); -- if (serverPlayer.getGameProfile().name().equalsIgnoreCase(username)) { -- return serverPlayer; +- ServerPlayer player = this.players.get(i); +- if (player.getGameProfile().name().equalsIgnoreCase(name)) { +- return player; - } - } - - return null; -+ return this.playersByName.get(username.toLowerCase(java.util.Locale.ROOT)); // Spigot ++ return this.playersByName.get(name.toLowerCase(java.util.Locale.ROOT)); // Spigot } - public void broadcast(@Nullable Player except, double x, double y, double z, double radius, ResourceKey dimension, Packet packet) { + public void broadcast( +@@ -613,6 +_,11 @@ + ) { for (int i = 0; i < this.players.size(); i++) { - ServerPlayer serverPlayer = this.players.get(i); + ServerPlayer player = this.players.get(i); + // CraftBukkit start - Test if player receiving packet can see the source of the packet -+ if (except != null && !serverPlayer.getBukkitEntity().canSee(except.getBukkitEntity())) { ++ if (except != null && !player.getBukkitEntity().canSee(except.getBukkitEntity())) { + continue; + } + // CraftBukkit end - if (serverPlayer != except && serverPlayer.level().dimension() == dimension) { - double d = x - serverPlayer.getX(); - double d1 = y - serverPlayer.getY(); -@@ -616,9 +_,11 @@ + if (player != except && player.level().dimension() == dimension) { + double xd = x - player.getX(); + double yd = y - player.getY(); +@@ -625,9 +_,11 @@ } public void saveAll() { @@ -734,9 +741,9 @@ } public UserWhiteList getWhiteList() { -@@ -645,10 +_,18 @@ +@@ -654,10 +_,18 @@ player.connection.send(new ClientboundInitializeBorderPacket(worldBorder)); - player.connection.send(new ClientboundSetTimePacket(level.getGameTime(), level.getDayTime(), level.getGameRules().get(GameRules.ADVANCE_TIME))); + player.connection.send(this.server.clockManager().createFullSyncPacket()); player.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getRespawnData())); + // Paper start - view distances + player.connection.send(new ClientboundSetChunkCacheRadiusPacket(io.papermc.paper.FeatureHooks.getViewDistance(level))); @@ -756,9 +763,9 @@ } player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.LEVEL_CHUNKS_LOAD_START, 0.0F)); -@@ -657,8 +_,21 @@ +@@ -666,8 +_,21 @@ - public void sendAllPlayerInfo(ServerPlayer player) { + public void sendAllPlayerInfo(final ServerPlayer player) { player.inventoryMenu.sendAllDataToRemote(); - player.resetSentInfo(); + // entityplayer.resetSentInfo(); @@ -779,7 +786,7 @@ } public int getPlayerCount() { -@@ -706,9 +_,26 @@ +@@ -711,9 +_,26 @@ } public void removeAll() { @@ -808,59 +815,59 @@ + // Paper end - Configurable player collision } - public void broadcastSystemMessage(Component message, boolean overlay) { -@@ -731,20 +_,39 @@ + public void broadcastSystemMessage(final Component message, final boolean overlay) { +@@ -736,20 +_,39 @@ } - public void broadcastChatMessage(PlayerChatMessage message, ServerPlayer sender, ChatType.Bound boundChatType) { -- this.broadcastChatMessage(message, sender::shouldFilterMessageTo, sender, boundChatType); + public void broadcastChatMessage(final PlayerChatMessage message, final ServerPlayer sender, final ChatType.Bound chatType) { +- this.broadcastChatMessage(message, sender::shouldFilterMessageTo, sender, chatType); + // Paper start -+ this.broadcastChatMessage(message, sender, boundChatType, null); ++ this.broadcastChatMessage(message, sender, chatType, null); + } -+ public void broadcastChatMessage(PlayerChatMessage message, ServerPlayer sender, ChatType.Bound boundChatType, @Nullable Function unsignedFunction) { ++ public void broadcastChatMessage(final PlayerChatMessage message, final ServerPlayer sender, final ChatType.Bound chatType, final @Nullable Function unsignedFunction) { + // Paper end -+ this.broadcastChatMessage(message, sender::shouldFilterMessageTo, sender, boundChatType, unsignedFunction); // Paper ++ this.broadcastChatMessage(message, sender::shouldFilterMessageTo, sender, chatType, unsignedFunction); // Paper } private void broadcastChatMessage( - PlayerChatMessage message, Predicate shouldFilterMessageTo, @Nullable ServerPlayer sender, ChatType.Bound boundChatType + final PlayerChatMessage message, final Predicate isFiltered, final @Nullable ServerPlayer senderPlayer, final ChatType.Bound chatType ) { + // Paper start -+ this.broadcastChatMessage(message, shouldFilterMessageTo, sender, boundChatType, null); ++ this.broadcastChatMessage(message, isFiltered, senderPlayer, chatType, null); + } -+ public void broadcastChatMessage(PlayerChatMessage message, Predicate shouldFilterMessageTo, @Nullable ServerPlayer sender, ChatType.Bound boundChatType, @Nullable Function unsignedFunction) { ++ public void broadcastChatMessage(final PlayerChatMessage message, final Predicate isFiltered, final @Nullable ServerPlayer senderPlayer, final ChatType.Bound chatType, final @Nullable Function unsignedFunction) { + // Paper end - boolean flag = this.verifyChatTrusted(message); -- this.server.logChatMessage(message.decoratedContent(), boundChatType, flag ? null : "Not Secure"); -+ this.server.logChatMessage((unsignedFunction == null ? message.decoratedContent() : unsignedFunction.apply(this.server.console)), boundChatType, flag ? null : "Not Secure"); // Paper - OutgoingChatMessage outgoingChatMessage = OutgoingChatMessage.create(message); - boolean flag1 = false; - -+ Packet disguised = sender != null && unsignedFunction == null ? new net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket(outgoingChatMessage.content(), boundChatType) : null; // Paper - don't send player chat packets from vanished players - for (ServerPlayer serverPlayer : this.players) { - boolean flag2 = shouldFilterMessageTo.test(serverPlayer); -- serverPlayer.sendChatMessage(outgoingChatMessage, flag2, boundChatType); + boolean trusted = this.verifyChatTrusted(message); +- this.server.logChatMessage(message.decoratedContent(), chatType, trusted ? null : "Not Secure"); ++ this.server.logChatMessage((unsignedFunction == null ? message.decoratedContent() : unsignedFunction.apply(this.server.console)), chatType, trusted ? null : "Not Secure"); // Paper + OutgoingChatMessage tracked = OutgoingChatMessage.create(message); + boolean wasFullyFiltered = false; + ++ Packet disguised = senderPlayer != null && unsignedFunction == null ? new net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket(tracked.content(), chatType) : null; // Paper - don't send player chat packets from vanished players + for (ServerPlayer player : this.players) { + boolean filtered = isFiltered.test(player); +- player.sendChatMessage(tracked, filtered, chatType); + // Paper start - don't send player chat packets from vanished players -+ if (sender != null && !serverPlayer.getBukkitEntity().canSee(sender.getBukkitEntity())) { -+ serverPlayer.connection.send(unsignedFunction != null -+ ? new net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket(unsignedFunction.apply(serverPlayer.getBukkitEntity()), boundChatType) ++ if (senderPlayer != null && !player.getBukkitEntity().canSee(senderPlayer.getBukkitEntity())) { ++ player.connection.send(unsignedFunction != null ++ ? new net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket(unsignedFunction.apply(player.getBukkitEntity()), chatType) + : disguised); + continue; + } -+ serverPlayer.sendChatMessage(outgoingChatMessage, flag2, boundChatType, unsignedFunction == null ? null : unsignedFunction.apply(serverPlayer.getBukkitEntity())); ++ player.sendChatMessage(tracked, filtered, chatType, unsignedFunction == null ? null : unsignedFunction.apply(player.getBukkitEntity())); + // Paper end - flag1 |= flag2 && message.isFullyFiltered(); + wasFullyFiltered |= filtered && message.isFullyFiltered(); } -@@ -757,13 +_,21 @@ +@@ -762,13 +_,21 @@ return message.hasSignature() && !message.hasExpiredServer(Instant.now()); } -- public ServerStatsCounter getPlayerStats(Player player) { +- public ServerStatsCounter getPlayerStats(final Player player) { + // CraftBukkit start -+ public ServerStatsCounter getPlayerStats(ServerPlayer player) { ++ public ServerStatsCounter getPlayerStats(final ServerPlayer player) { GameProfile gameProfile = player.getGameProfile(); -- return this.stats.computeIfAbsent(gameProfile.id(), uuid -> { +- return this.stats.computeIfAbsent(gameProfile.id(), id -> { + ServerStatsCounter playerStatsCounter = player.getStats(); + if (playerStatsCounter == null) { + return this.getPlayerStats(gameProfile); @@ -868,35 +875,35 @@ + return playerStatsCounter; + } + } -+ public ServerStatsCounter getPlayerStats(GameProfile gameProfile) { - Path path = this.locateStatsFile(gameProfile); - return new ServerStatsCounter(this.server, path); ++ public ServerStatsCounter getPlayerStats(final GameProfile gameProfile) { + Path targetFile = this.locateStatsFile(gameProfile); + return new ServerStatsCounter(this.server, targetFile); - }); } + // CraftBukkit end - private Path locateStatsFile(GameProfile profile) { - Path worldPath = this.server.getWorldPath(LevelResource.PLAYER_STATS_DIR); -@@ -790,11 +_,11 @@ + private Path locateStatsFile(final GameProfile gameProfile) { + Path statFolder = this.server.getWorldPath(LevelResource.PLAYER_STATS_DIR); +@@ -795,11 +_,11 @@ - public PlayerAdvancements getPlayerAdvancements(ServerPlayer player) { + public PlayerAdvancements getPlayerAdvancements(final ServerPlayer player) { UUID uuid = player.getUUID(); -- PlayerAdvancements playerAdvancements = this.advancements.get(uuid); -+ PlayerAdvancements playerAdvancements = player.getAdvancements(); // CraftBukkit - if (playerAdvancements == null) { - Path path = this.server.getWorldPath(LevelResource.PLAYER_ADVANCEMENTS_DIR).resolve(uuid + ".json"); - playerAdvancements = new PlayerAdvancements(this.server.getFixerUpper(), this, this.server.getAdvancements(), path, player); -- this.advancements.put(uuid, playerAdvancements); -+ // this.advancements.put(uuid, playerAdvancements); // CraftBukkit +- PlayerAdvancements result = this.advancements.get(uuid); ++ PlayerAdvancements result = player.getAdvancements(); // CraftBukkit + if (result == null) { + Path uuidStatsFile = this.server.getWorldPath(LevelResource.PLAYER_ADVANCEMENTS_DIR).resolve(uuid + ".json"); + result = new PlayerAdvancements(this.server.getFixerUpper(), this, this.server.getAdvancements(), uuidStatsFile, player); +- this.advancements.put(uuid, result); ++ // this.advancements.put(uuid, result); // CraftBukkit } - playerAdvancements.setPlayer(player); -@@ -842,11 +_,34 @@ + result.setPlayer(player); +@@ -847,11 +_,34 @@ } public void reloadResources() { -- for (PlayerAdvancements playerAdvancements : this.advancements.values()) { -- playerAdvancements.reload(this.server.getAdvancements()); +- for (PlayerAdvancements advancements : this.advancements.values()) { +- advancements.reload(this.server.getAdvancements()); + // Paper start - API for updating recipes on clients + this.reloadAdvancementData(); + this.reloadTagData(); @@ -926,5 +933,5 @@ + public void reloadRecipes() { + // CraftBukkit end RecipeManager recipeManager = this.server.getRecipeManager(); - ClientboundUpdateRecipesPacket clientboundUpdateRecipesPacket = new ClientboundUpdateRecipesPacket( + ClientboundUpdateRecipesPacket recipes = new ClientboundUpdateRecipesPacket( recipeManager.getSynchronizedItemProperties(), recipeManager.getSynchronizedStonecutterRecipes() diff --git a/paper-server/patches/sources/net/minecraft/server/players/ProfileResolver.java.patch b/paper-server/patches/sources/net/minecraft/server/players/ProfileResolver.java.patch index c5ba1378ddf3..0ea61aa2ca67 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/ProfileResolver.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/ProfileResolver.java.patch @@ -1,21 +1,21 @@ --- a/net/minecraft/server/players/ProfileResolver.java +++ b/net/minecraft/server/players/ProfileResolver.java -@@ -25,7 +_,7 @@ +@@ -26,7 +_,7 @@ private final LoadingCache> profileCacheByName; - final LoadingCache> profileCacheById; + private final LoadingCache> profileCacheById; -- public Cached(final MinecraftSessionService sessionService, final UserNameToIdResolver resolver) { -+ public Cached(final MinecraftSessionService sessionService, final UserNameToIdResolver resolver, final io.papermc.paper.profile.PaperFilledProfileCache paperCache) { // Paper - add paperCache +- public Cached(final MinecraftSessionService sessionService, final UserNameToIdResolver nameToIdCache) { ++ public Cached(final MinecraftSessionService sessionService, final UserNameToIdResolver nameToIdCache, final io.papermc.paper.profile.PaperFilledProfileCache paperCache) { // Paper - add paperCache this.profileCacheById = CacheBuilder.newBuilder() .expireAfterAccess(Duration.ofMinutes(10L)) .maximumSize(256L) -@@ -33,7 +_,13 @@ +@@ -38,7 +_,13 @@ @Override - public Optional load(UUID id) { - ProfileResult profileResult = sessionService.fetchProfile(id, true); -- return Optional.ofNullable(profileResult).map(ProfileResult::profile); + public Optional load(final UUID profileId) { + ProfileResult result = sessionService.fetchProfile(profileId, true); +- return Optional.ofNullable(result).map(ProfileResult::profile); + // Paper start - update paper cache -+ return Optional.ofNullable(profileResult).map(ProfileResult::profile) ++ return Optional.ofNullable(result).map(ProfileResult::profile) + .map(profile -> { + paperCache.add(profile); + return profile; diff --git a/paper-server/patches/sources/net/minecraft/server/players/SleepStatus.java.patch b/paper-server/patches/sources/net/minecraft/server/players/SleepStatus.java.patch index feab2a62643e..b8a4adbbbc4c 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/SleepStatus.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/SleepStatus.java.patch @@ -3,39 +3,39 @@ @@ -14,8 +_,11 @@ } - public boolean areEnoughDeepSleeping(int requiredSleepPercentage, List sleepingPlayers) { -- int i = (int)sleepingPlayers.stream().filter(Player::isSleepingLongEnough).count(); -- return i >= this.sleepersNeeded(requiredSleepPercentage); + public boolean areEnoughDeepSleeping(final int sleepPercentageNeeded, final List players) { +- int deepSleepers = (int)players.stream().filter(Player::isSleepingLongEnough).count(); +- return deepSleepers >= this.sleepersNeeded(sleepPercentageNeeded); + // CraftBukkit start -+ int i = (int) sleepingPlayers.stream().filter(player -> player.isSleepingLongEnough() || player.fauxSleeping).count(); -+ boolean anyDeepSleep = sleepingPlayers.stream().anyMatch(Player::isSleepingLongEnough); -+ return anyDeepSleep && i >= this.sleepersNeeded(requiredSleepPercentage); ++ int deepSleepers = (int)players.stream().filter(player -> player.isSleepingLongEnough() || player.fauxSleeping).count(); ++ boolean anyDeepSleep = players.stream().anyMatch(Player::isSleepingLongEnough); ++ return anyDeepSleep && deepSleepers >= this.sleepersNeeded(sleepPercentageNeeded); + // CraftBukkit end } - public int sleepersNeeded(int requiredSleepPercentage) { + public int sleepersNeeded(final int sleepPercentageNeeded) { @@ -35,16 +_,22 @@ - int i1 = this.sleepingPlayers; + int oldSleepingPlayers = this.sleepingPlayers; this.activePlayers = 0; this.sleepingPlayers = 0; + boolean anySleep = false; // CraftBukkit - for (ServerPlayer serverPlayer : players) { - if (!serverPlayer.isSpectator()) { + for (ServerPlayer player : players) { + if (!player.isSpectator()) { this.activePlayers++; -- if (serverPlayer.isSleeping()) { -+ if (serverPlayer.isSleeping() || serverPlayer.fauxSleeping) { // CraftBukkit +- if (player.isSleeping()) { ++ if (player.isSleeping() || player.fauxSleeping) { // CraftBukkit this.sleepingPlayers++; } + // CraftBukkit start -+ if (serverPlayer.isSleeping()) { ++ if (player.isSleeping()) { + anySleep = true; + } + // CraftBukkit end } } -- return (i1 > 0 || this.sleepingPlayers > 0) && (i != this.activePlayers || i1 != this.sleepingPlayers); -+ return anySleep && (i1 > 0 || this.sleepingPlayers > 0) && (i != this.activePlayers || i1 != this.sleepingPlayers); // CraftBukkit +- return (oldSleepingPlayers > 0 || this.sleepingPlayers > 0) && (oldActivePlayers != this.activePlayers || oldSleepingPlayers != this.sleepingPlayers); ++ return anySleep && (oldSleepingPlayers > 0 || this.sleepingPlayers > 0) && (oldActivePlayers != this.activePlayers || oldSleepingPlayers != this.sleepingPlayers); // CraftBukkit } } diff --git a/paper-server/patches/sources/net/minecraft/server/players/StoredUserList.java.patch b/paper-server/patches/sources/net/minecraft/server/players/StoredUserList.java.patch index b0d0b8a25fb1..ebcb9f5da52c 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/StoredUserList.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/StoredUserList.java.patch @@ -8,11 +8,11 @@ + private final Map map = Maps.newConcurrentMap(); // Paper - Use ConcurrentHashMap in StoredUserList protected final NotificationService notificationService; - public StoredUserList(File file, NotificationService notificationService) { + public StoredUserList(final File file, final NotificationService notificationService) { @@ -59,8 +_,11 @@ } - public @Nullable V get(K user) { + public @Nullable V get(final K user) { - this.removeExpired(); - return this.map.get(this.getKeyForUser(user)); + // Paper start - Use ConcurrentHashMap in StoredUserList @@ -22,41 +22,44 @@ + // Paper end - Use ConcurrentHashMap in StoredUserList } - public boolean remove(K user) { -@@ -105,21 +_,12 @@ + public boolean remove(final K user) { +@@ -105,22 +_,15 @@ } - protected boolean contains(K entry) { + protected boolean contains(final K user) { + this.removeExpired(); // CraftBukkit - SPIGOT-7589: Consistently remove expired entries to mirror .get(...) - return this.map.containsKey(this.getKeyForUser(entry)); + return this.map.containsKey(this.getKeyForUser(user)); } ++ // Paper start - Use ConcurrentHashMap in StoredUserList private void removeExpired() { -- List list = Lists.newArrayList(); +- List toRemove = Lists.newArrayList(); - -- for (V storedUserEntry : this.map.values()) { -- if (storedUserEntry.hasExpired()) { -- list.add(storedUserEntry.getUser()); +- for (V entry : this.map.values()) { +- if (entry.hasExpired()) { +- toRemove.add(entry.getUser()); - } - } - -- for (K object : list) { -- this.map.remove(this.getKeyForUser(object)); +- for (K user : toRemove) { +- this.map.remove(this.getKeyForUser(user)); - } -+ this.map.values().removeIf(StoredUserEntry::hasExpired); // Paper - Use ConcurrentHashMap in StoredUserList ++ this.map.values().removeIf(StoredUserEntry::hasExpired); } ++ // Paper end - Use ConcurrentHashMap in StoredUserList + + protected abstract StoredUserEntry createEntry(final JsonObject object); - protected abstract StoredUserEntry createEntry(JsonObject entryData); @@ -129,6 +_,7 @@ } public void save() throws IOException { + this.removeExpired(); // Paper - remove expired values before saving - JsonArray jsonArray = new JsonArray(); - this.map.values().stream().map(storedEntry -> Util.make(new JsonObject(), storedEntry::serialize)).forEach(jsonArray::add); + JsonArray result = new JsonArray(); + this.map.values().stream().map(entry -> Util.make(new JsonObject(), entry::serialize)).forEach(result::add); @@ -153,7 +_,14 @@ - this.map.put(this.getKeyForUser(storedUserEntry.getUser()), (V)storedUserEntry); + this.map.put(this.getKeyForUser(entry.getUser()), (V)entry); } } + // Spigot start diff --git a/paper-server/patches/sources/net/minecraft/server/players/UserBanListEntry.java.patch b/paper-server/patches/sources/net/minecraft/server/players/UserBanListEntry.java.patch index c4ab728e1369..ff61eda61ae4 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/UserBanListEntry.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/UserBanListEntry.java.patch @@ -1,17 +1,17 @@ --- a/net/minecraft/server/players/UserBanListEntry.java +++ b/net/minecraft/server/players/UserBanListEntry.java -@@ -17,7 +_,7 @@ +@@ -23,7 +_,7 @@ } - public UserBanListEntry(JsonObject entryData) { -- super(NameAndId.fromJson(entryData), entryData); -+ super(parseNameAndId(entryData), entryData); + public UserBanListEntry(final JsonObject object) { +- super(NameAndId.fromJson(object), object); ++ super(parseNameAndId(object), object); } @Override -@@ -33,4 +_,31 @@ - NameAndId nameAndId = this.getUser(); - return (Component)(nameAndId != null ? Component.literal(nameAndId.name()) : MESSAGE_UNKNOWN_USER); +@@ -39,4 +_,31 @@ + NameAndId user = this.getUser(); + return (Component)(user != null ? Component.literal(user.name()) : MESSAGE_UNKNOWN_USER); } + + // Spigot start diff --git a/paper-server/patches/sources/net/minecraft/server/players/UserNameToIdResolver.java.patch b/paper-server/patches/sources/net/minecraft/server/players/UserNameToIdResolver.java.patch index f19c3d3da1c0..1337cc1401bd 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/UserNameToIdResolver.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/UserNameToIdResolver.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/server/players/UserNameToIdResolver.java +++ b/net/minecraft/server/players/UserNameToIdResolver.java @@ -13,4 +_,8 @@ - void resolveOfflineUsers(boolean resolveOfflineUsers); + void resolveOfflineUsers(boolean value); void save(); + + void save(boolean async); // Paper + -+ @javax.annotation.Nullable NameAndId getIfCached(String name); // Paper ++ @org.jspecify.annotations.Nullable NameAndId getIfCached(String name); // Paper } diff --git a/paper-server/patches/sources/net/minecraft/server/players/UserWhiteList.java.patch b/paper-server/patches/sources/net/minecraft/server/players/UserWhiteList.java.patch index 5351b2790cf6..4b5b7e7d5f62 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/UserWhiteList.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/UserWhiteList.java.patch @@ -3,19 +3,19 @@ @@ -21,6 +_,11 @@ @Override - public boolean add(UserWhiteListEntry entry) { + public boolean add(final UserWhiteListEntry infos) { + // Paper start - Add whitelist events -+ if (!new io.papermc.paper.event.server.WhitelistStateUpdateEvent(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitCopy(entry.getUser().toUncompletedGameProfile()), io.papermc.paper.event.server.WhitelistStateUpdateEvent.WhitelistStatus.ADDED).callEvent()) { ++ if (!new io.papermc.paper.event.server.WhitelistStateUpdateEvent(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitCopy(infos.getUser().toUncompletedGameProfile()), io.papermc.paper.event.server.WhitelistStateUpdateEvent.WhitelistStatus.ADDED).callEvent()) { + return false; + } + // Paper end - Add whitelist events - if (super.add(entry)) { - if (entry.getUser() != null) { - this.notificationService.playerAddedToAllowlist(entry.getUser()); + if (super.add(infos)) { + if (infos.getUser() != null) { + this.notificationService.playerAddedToAllowlist(infos.getUser()); @@ -34,6 +_,11 @@ @Override - public boolean remove(NameAndId user) { + public boolean remove(final NameAndId user) { + // Paper start - Add whitelist events + if (!new io.papermc.paper.event.server.WhitelistStateUpdateEvent(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitCopy(user.toUncompletedGameProfile()), io.papermc.paper.event.server.WhitelistStateUpdateEvent.WhitelistStatus.REMOVED).callEvent()) { + return false; diff --git a/paper-server/patches/sources/net/minecraft/server/rcon/RconConsoleSource.java.patch b/paper-server/patches/sources/net/minecraft/server/rcon/RconConsoleSource.java.patch index ef6f7fc283cd..b190b42807ac 100644 --- a/paper-server/patches/sources/net/minecraft/server/rcon/RconConsoleSource.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/rcon/RconConsoleSource.java.patch @@ -8,8 +8,8 @@ + public final java.net.SocketAddress socketAddress; + private final org.bukkit.craftbukkit.command.CraftRemoteConsoleCommandSender remoteConsole = new org.bukkit.craftbukkit.command.CraftRemoteConsoleCommandSender(this); -- public RconConsoleSource(MinecraftServer server) { -+ public RconConsoleSource(MinecraftServer server, java.net.SocketAddress socketAddress) { +- public RconConsoleSource(final MinecraftServer server) { ++ public RconConsoleSource(final MinecraftServer server, java.net.SocketAddress socketAddress) { + this.socketAddress = socketAddress; + // CraftBukkit end this.server = server; @@ -32,4 +32,4 @@ + // CraftBukkit end @Override - public void sendSystemMessage(Component message) { + public void sendSystemMessage(final Component message) { diff --git a/paper-server/patches/sources/net/minecraft/server/rcon/thread/QueryThreadGs4.java.patch b/paper-server/patches/sources/net/minecraft/server/rcon/thread/QueryThreadGs4.java.patch index a2323578dd66..35ae01a2673f 100644 --- a/paper-server/patches/sources/net/minecraft/server/rcon/thread/QueryThreadGs4.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/rcon/thread/QueryThreadGs4.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/server/rcon/thread/QueryThreadGs4.java +++ b/net/minecraft/server/rcon/thread/QueryThreadGs4.java @@ -105,13 +_,32 @@ - NetworkDataOutputStream networkDataOutputStream = new NetworkDataOutputStream(1460); - networkDataOutputStream.write(0); - networkDataOutputStream.writeBytes(this.getIdentBytes(requestPacket.getSocketAddress())); -- networkDataOutputStream.writeString(this.serverName); + NetworkDataOutputStream dos = new NetworkDataOutputStream(1460); + dos.write(0); + dos.writeBytes(this.getIdentBytes(packet.getSocketAddress())); +- dos.writeString(this.serverName); + // Paper start + com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType queryType = + com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType.BASIC; @@ -19,27 +19,27 @@ + .serverVersion(org.bukkit.Bukkit.getServer().getName() + " on " + org.bukkit.Bukkit.getServer().getBukkitVersion()) + .build(); + com.destroystokyo.paper.event.server.GS4QueryEvent queryEvent = -+ new com.destroystokyo.paper.event.server.GS4QueryEvent(queryType, requestPacket.getAddress(), queryResponse); ++ new com.destroystokyo.paper.event.server.GS4QueryEvent(queryType, packet.getAddress(), queryResponse); + queryEvent.callEvent(); + queryResponse = queryEvent.getResponse(); + -+ networkDataOutputStream.writeString(queryResponse.getMotd()); - networkDataOutputStream.writeString("SMP"); -- networkDataOutputStream.writeString(this.worldName); -- networkDataOutputStream.writeString(Integer.toString(this.serverInterface.getPlayerCount())); -- networkDataOutputStream.writeString(Integer.toString(this.maxPlayers)); -- networkDataOutputStream.writeShort((short)this.serverPort); -- networkDataOutputStream.writeString(this.hostIp); -+ networkDataOutputStream.writeString(queryResponse.getMap()); -+ networkDataOutputStream.writeString(Integer.toString(queryResponse.getCurrentPlayers())); -+ networkDataOutputStream.writeString(Integer.toString(queryResponse.getMaxPlayers())); -+ networkDataOutputStream.writeShort((short) queryResponse.getPort()); -+ networkDataOutputStream.writeString(queryResponse.getHostname()); ++ dos.writeString(queryResponse.getMotd()); + dos.writeString("SMP"); +- dos.writeString(this.worldName); +- dos.writeString(Integer.toString(this.serverInterface.getPlayerCount())); +- dos.writeString(Integer.toString(this.maxPlayers)); +- dos.writeShort((short)this.serverPort); +- dos.writeString(this.hostIp); ++ dos.writeString(queryResponse.getMap()); ++ dos.writeString(Integer.toString(queryResponse.getCurrentPlayers())); ++ dos.writeString(Integer.toString(queryResponse.getMaxPlayers())); ++ dos.writeShort((short) queryResponse.getPort()); ++ dos.writeString(queryResponse.getHostname()); + // Paper end - this.sendTo(networkDataOutputStream.toByteArray(), requestPacket); + this.sendTo(dos.toByteArray(), packet); LOGGER.debug("Status [{}]", socketAddress); } -@@ -146,31 +_,75 @@ +@@ -146,31 +_,76 @@ this.rulesResponse.writeString("splitnum"); this.rulesResponse.write(128); this.rulesResponse.write(0); @@ -68,7 +68,7 @@ + com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType queryType = + com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType.FULL; + com.destroystokyo.paper.event.server.GS4QueryEvent queryEvent = -+ new com.destroystokyo.paper.event.server.GS4QueryEvent(queryType, requestPacket.getAddress(), queryResponse); ++ new com.destroystokyo.paper.event.server.GS4QueryEvent(queryType, packet.getAddress(), queryResponse); + queryEvent.callEvent(); + queryResponse = queryEvent.getResponse(); this.rulesResponse.writeString("hostname"); @@ -119,8 +119,9 @@ this.rulesResponse.write(1); this.rulesResponse.writeString("player_"); this.rulesResponse.write(0); -- String[] playerNames = this.serverInterface.getPlayerNames(); -+ String[] playerNames = queryResponse.getPlayers().toArray(String[]::new); +- String[] players = this.serverInterface.getPlayerNames(); ++ String[] players = queryResponse.getPlayers().toArray(String[]::new); ++ // Paper end - for (String string : playerNames) { - this.rulesResponse.writeString(string); + for (String player : players) { + this.rulesResponse.writeString(player); diff --git a/paper-server/patches/sources/net/minecraft/server/rcon/thread/RconClient.java.patch b/paper-server/patches/sources/net/minecraft/server/rcon/thread/RconClient.java.patch index e84fb83f9d27..65e879ba64fc 100644 --- a/paper-server/patches/sources/net/minecraft/server/rcon/thread/RconClient.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/rcon/thread/RconClient.java.patch @@ -10,11 +10,11 @@ + private final net.minecraft.server.rcon.RconConsoleSource rconConsoleSource; + // CraftBukkit end - RconClient(ServerInterface serverInterface, String rconPassword, Socket client) { - super("RCON Client " + client.getInetAddress()); + RconClient(final ServerInterface serverInterface, final String rconPassword, final Socket socket) { + super("RCON Client " + socket.getInetAddress()); - this.serverInterface = serverInterface; + this.serverInterface = (net.minecraft.server.dedicated.DedicatedServer) serverInterface; // CraftBukkit - this.client = client; + this.client = socket; try { @@ -37,6 +_,7 @@ @@ -26,11 +26,11 @@ @Override @@ -67,7 +_,7 @@ - String string1 = PktUtils.stringFromByteArray(this.buf, i1, i); + String command = PktUtils.stringFromByteArray(this.buf, offset, read); try { -- this.sendCmdResponse(i3, this.serverInterface.runCommand(string1)); -+ this.sendCmdResponse(i3, this.serverInterface.runCommand(this.rconConsoleSource, string1)); // CraftBukkit +- this.sendCmdResponse(requestid, this.serverInterface.runCommand(command)); ++ this.sendCmdResponse(requestid, this.serverInterface.runCommand(this.rconConsoleSource, command)); // CraftBukkit } catch (Exception var15) { - this.sendCmdResponse(i3, "Error executing: " + string1 + " (" + var15.getMessage() + ")"); + this.sendCmdResponse(requestid, "Error executing: " + command + " (" + var15.getMessage() + ")"); } diff --git a/paper-server/patches/sources/net/minecraft/server/rcon/thread/RconThread.java.patch b/paper-server/patches/sources/net/minecraft/server/rcon/thread/RconThread.java.patch index bd7388039ad4..d2896e26aecb 100644 --- a/paper-server/patches/sources/net/minecraft/server/rcon/thread/RconThread.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/rcon/thread/RconThread.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/server/rcon/thread/RconThread.java @@ -56,7 +_,7 @@ - public static @Nullable RconThread create(ServerInterface serverInterface) { - DedicatedServerProperties properties = serverInterface.getProperties(); + public static @Nullable RconThread create(final ServerInterface serverInterface) { + DedicatedServerProperties settings = serverInterface.getProperties(); - String serverIp = serverInterface.getServerIp(); -+ String serverIp = properties.rconIp; // Paper - Configurable rcon ip ++ String serverIp = settings.rconIp; // Paper - Configurable rcon ip if (serverIp.isEmpty()) { serverIp = "0.0.0.0"; } @@ -22,5 +22,5 @@ + } + // Paper end - don't wait for remote connections - private void closeSocket(ServerSocket socket) { + private void closeSocket(final ServerSocket socket) { LOGGER.debug("closeSocket: {}", socket); diff --git a/paper-server/patches/sources/net/minecraft/server/waypoints/ServerWaypointManager.java.patch b/paper-server/patches/sources/net/minecraft/server/waypoints/ServerWaypointManager.java.patch new file mode 100644 index 000000000000..dc552adc08ca --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/server/waypoints/ServerWaypointManager.java.patch @@ -0,0 +1,72 @@ +--- a/net/minecraft/server/waypoints/ServerWaypointManager.java ++++ b/net/minecraft/server/waypoints/ServerWaypointManager.java +@@ -19,11 +_,19 @@ + private final Set waypoints = new HashSet<>(); + private final Set players = new HashSet<>(); + private final Table connections = HashBasedTable.create(); ++ // Paper start - optimize ServerWaypointManager with locator bar disabled ++ public boolean locatorBarEnabled; // Avoid a gamerule lookup for each player ++ ++ public ServerWaypointManager(net.minecraft.server.level.ServerLevel serverLevel) { ++ this.locatorBarEnabled = serverLevel.getGameRules().get(GameRules.LOCATOR_BAR); ++ } ++ // Paper end - optimize ServerWaypointManager with locator bar disabled + + @Override + public void trackWaypoint(final WaypointTransmitter waypoint) { + this.waypoints.add(waypoint); + ++ if (!this.locatorBarEnabled) return; // Paper - optimize ServerWaypointManager with locator bar disabled + for (ServerPlayer player : this.players) { + this.createConnection(player, waypoint); + } +@@ -31,6 +_,7 @@ + + @Override + public void updateWaypoint(final WaypointTransmitter waypoint) { ++ if (!this.locatorBarEnabled) return; // Paper - optimize ServerWaypointManager with locator bar disabled + if (this.waypoints.contains(waypoint)) { + Map playerConnection = Tables.transpose(this.connections).row(waypoint); + SetView potentialPlayers = Sets.difference(this.players, playerConnection.keySet()); +@@ -55,9 +_,11 @@ + public void addPlayer(final ServerPlayer player) { + this.players.add(player); + ++ if (this.locatorBarEnabled) { // Paper - optimize ServerWaypointManager with locator bar disabled + for (WaypointTransmitter waypoint : this.waypoints) { + this.createConnection(player, waypoint); + } ++ } // Paper - optimize ServerWaypointManager with locator bar disabled + + if (player.isTransmittingWaypoint()) { + this.trackWaypoint((WaypointTransmitter)player); +@@ -65,6 +_,7 @@ + } + + public void updatePlayer(final ServerPlayer player) { ++ if (!this.locatorBarEnabled) return; // Paper - optimize ServerWaypointManager with locator bar disabled + Map waypointConnections = this.connections.row(player); + SetView potentialWaypoints = Sets.difference(this.waypoints, waypointConnections.keySet()); + +@@ -92,6 +_,7 @@ + } + + public void remakeConnections(final WaypointTransmitter waypoint) { ++ if (!this.locatorBarEnabled) return; // Paper - optimize ServerWaypointManager with locator bar disabled + for (ServerPlayer player : this.players) { + this.createConnection(player, waypoint); + } +@@ -101,8 +_,11 @@ + return this.waypoints; + } + +- private static boolean isLocatorBarEnabledFor(final ServerPlayer player) { +- return player.level().getGameRules().get(GameRules.LOCATOR_BAR); ++ // Paper start - optimize ServerWaypointManager with locator bar disabled ++ // Avoid a gamerule lookup for each player ++ private boolean isLocatorBarEnabledFor(final ServerPlayer player) { ++ return locatorBarEnabled; ++ // Paper end - optimize ServerWaypointManager with locator bar disabled + } + + private void createConnection(final ServerPlayer player, final WaypointTransmitter waypoint) { diff --git a/paper-server/patches/sources/net/minecraft/stats/ServerRecipeBook.java.patch b/paper-server/patches/sources/net/minecraft/stats/ServerRecipeBook.java.patch index 2a7a85913140..79fcad6dbccc 100644 --- a/paper-server/patches/sources/net/minecraft/stats/ServerRecipeBook.java.patch +++ b/paper-server/patches/sources/net/minecraft/stats/ServerRecipeBook.java.patch @@ -1,35 +1,35 @@ --- a/net/minecraft/stats/ServerRecipeBook.java +++ b/net/minecraft/stats/ServerRecipeBook.java @@ -64,17 +_,21 @@ - for (RecipeHolder recipeHolder : recipes) { - ResourceKey> resourceKey = recipeHolder.id(); - if (!this.known.contains(resourceKey) && !recipeHolder.value().isSpecial()) { + for (RecipeHolder recipe : recipes) { + ResourceKey> id = recipe.id(); + if (!this.known.contains(id) && !recipe.value().isSpecial()) { + // Paper start - PlayerRecipeDiscoverEvent event -+ final org.bukkit.event.player.PlayerRecipeDiscoverEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerRecipeListUpdateEvent(player, recipeHolder); ++ final org.bukkit.event.player.PlayerRecipeDiscoverEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerRecipeListUpdateEvent(player, recipe); + if (event.isCancelled()) continue; + // Paper end - PlayerRecipeDiscoverEvent event - this.add(resourceKey); - this.addHighlight(resourceKey); + this.add(id); + this.addHighlight(id); this.displayResolver .displaysForRecipe( -- resourceKey, entry -> list.add(new ClientboundRecipeBookAddPacket.Entry(entry, recipeHolder.value().showNotification(), true)) -+ resourceKey, entry -> list.add(new ClientboundRecipeBookAddPacket.Entry(entry, event.shouldShowNotification(), true)) // Paper - set notification from the event +- id, display -> recipesToAdd.add(new ClientboundRecipeBookAddPacket.Entry(display, recipe.value().showNotification(), true)) ++ id, display -> recipesToAdd.add(new ClientboundRecipeBookAddPacket.Entry(display, event.shouldShowNotification(), true)) // Paper - set notification from the event ); - CriteriaTriggers.RECIPE_UNLOCKED.trigger(player, recipeHolder); + CriteriaTriggers.RECIPE_UNLOCKED.trigger(player, recipe); } } -- if (!list.isEmpty()) { -+ if (!list.isEmpty() && player.connection != null) { // SPIGOT-4478 during PlayerLoginEvent - player.connection.send(new ClientboundRecipeBookAddPacket(list, false)); +- if (!recipesToAdd.isEmpty()) { ++ if (!recipesToAdd.isEmpty() && player.connection != null) { // SPIGOT-4478 during PlayerLoginEvent + player.connection.send(new ClientboundRecipeBookAddPacket(recipesToAdd, false)); } @@ -92,7 +_,7 @@ } } -- if (!list.isEmpty()) { -+ if (!list.isEmpty() && player.connection != null) { // SPIGOT-4478 during PlayerLoginEvent - player.connection.send(new ClientboundRecipeBookRemovePacket(list)); +- if (!recipesToRemove.isEmpty()) { ++ if (!recipesToRemove.isEmpty() && player.connection != null) { // SPIGOT-4478 during PlayerLoginEvent + player.connection.send(new ClientboundRecipeBookRemovePacket(recipesToRemove)); } diff --git a/paper-server/patches/sources/net/minecraft/stats/ServerStatsCounter.java.patch b/paper-server/patches/sources/net/minecraft/stats/ServerStatsCounter.java.patch index 5b95b1fbe6b1..a095b44e2e5a 100644 --- a/paper-server/patches/sources/net/minecraft/stats/ServerStatsCounter.java.patch +++ b/paper-server/patches/sources/net/minecraft/stats/ServerStatsCounter.java.patch @@ -25,9 +25,9 @@ @@ -91,6 +_,8 @@ @Override - public void setValue(Player player, Stat stat, int value) { + public void setValue(final Player player, final Stat stat, final int count) { + if (org.spigotmc.SpigotConfig.disableStatSaving) return; // Spigot + if (stat.getType() == Stats.CUSTOM && stat.getValue() instanceof final net.minecraft.resources.Identifier key && org.spigotmc.SpigotConfig.forcedStats.get(key) != null) return; // Paper - disable saving forced stats - super.setValue(player, stat, value); + super.setValue(player, stat, count); this.dirty.add(stat); } diff --git a/paper-server/patches/sources/net/minecraft/stats/StatsCounter.java.patch b/paper-server/patches/sources/net/minecraft/stats/StatsCounter.java.patch index a1a5baa22994..f9a20c28d116 100644 --- a/paper-server/patches/sources/net/minecraft/stats/StatsCounter.java.patch +++ b/paper-server/patches/sources/net/minecraft/stats/StatsCounter.java.patch @@ -2,14 +2,14 @@ +++ b/net/minecraft/stats/StatsCounter.java @@ -14,6 +_,12 @@ - public void increment(Player player, Stat stat, int amount) { - int i = (int)Math.min((long)this.getValue(stat) + amount, 2147483647L); + public void increment(final Player player, final Stat stat, final int count) { + int result = (int)Math.min((long)this.getValue(stat) + count, 2147483647L); + // CraftBukkit start - fire Statistic events -+ org.bukkit.event.Cancellable cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.handleStatisticsIncrease(player, stat, this.getValue(stat), i); ++ org.bukkit.event.Cancellable cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.handleStatisticsIncrease(player, stat, this.getValue(stat), result); + if (cancellable != null && cancellable.isCancelled()) { + return; + } + // CraftBukkit end - this.setValue(player, stat, i); + this.setValue(player, stat, result); } diff --git a/paper-server/patches/sources/net/minecraft/tags/TagLoader.java.patch b/paper-server/patches/sources/net/minecraft/tags/TagLoader.java.patch index 482a0e180c8e..09f37b6e02ff 100644 --- a/paper-server/patches/sources/net/minecraft/tags/TagLoader.java.patch +++ b/paper-server/patches/sources/net/minecraft/tags/TagLoader.java.patch @@ -1,68 +1,81 @@ --- a/net/minecraft/tags/TagLoader.java +++ b/net/minecraft/tags/TagLoader.java @@ -86,7 +_,10 @@ - return list.isEmpty() ? Either.right(List.copyOf(set)) : Either.left(list); + return missingElements.isEmpty() ? Either.right(List.copyOf(values)) : Either.left(missingElements); } -- public Map> build(Map> builders) { +- public Map> build(final Map> builders) { + // Paper start - fire tag registrar events + public Map> build(Map> builders, io.papermc.paper.tag.@Nullable TagEventConfig eventConfig) { + builders = io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.firePreFlattenEvent(builders, eventConfig); + // Paper end - final Map> map = new HashMap<>(); + final Map> newTags = new HashMap<>(); TagEntry.Lookup lookup = new TagEntry.Lookup() { - @Override -@@ -112,7 +_,7 @@ + { +@@ -116,7 +_,7 @@ ) - .ifRight(list -> map.put(path, (List)list)) + .ifRight(tag -> newTags.put(id, (List)tag)) ); -- return map; -+ return io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.firePostFlattenEvent(map, eventConfig); // Paper - fire tag registrar events +- return newTags; ++ return io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.firePostFlattenEvent(newTags, eventConfig); // Paper - fire tag registrar events } - public static void loadTagsFromNetwork(TagNetworkSerialization.NetworkPayload payload, WritableRegistry registry) { -@@ -120,16 +_,27 @@ + public static Map, List>> loadTagsFromNetwork(final TagNetworkSerialization.NetworkPayload tags, final Registry registry) { +@@ -124,18 +_,39 @@ } - public static List> loadTagsForExistingRegistries(ResourceManager resourceManager, RegistryAccess registryAccess) { + public static List> loadTagsForExistingRegistries(final ResourceManager manager, final RegistryAccess layer) { +- return layer.registries().map(entry -> loadPendingTags(manager, entry.value())).flatMap(Optional::stream).collect(Collectors.toUnmodifiableList()); + // Paper start - tag lifecycle - add cause -+ return loadTagsForExistingRegistries(resourceManager, registryAccess, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); ++ return loadTagsForExistingRegistries(manager, layer, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); + } + -+ public static List> loadTagsForExistingRegistries(ResourceManager resourceManager, RegistryAccess registryAccess, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause cause) { ++ public static List> loadTagsForExistingRegistries( ++ final ResourceManager manager, final RegistryAccess layer, final io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause cause ++ ) { ++ return layer.registries().map(entry -> loadPendingTags(manager, entry.value(), cause)).flatMap(Optional::stream).collect(Collectors.toUnmodifiableList()); + // Paper end - tag lifecycle - add cause - return registryAccess.registries() -- .map(registryEntry -> loadPendingTags(resourceManager, registryEntry.value())) -+ .map(registryEntry -> loadPendingTags(resourceManager, registryEntry.value(), cause)) // Paper - tag lifecycle - add cause - .flatMap(Optional::stream) - .collect(Collectors.toUnmodifiableList()); } - public static void loadTagsForRegistry(ResourceManager resourceManager, WritableRegistry registry) { + public static void loadTagsForRegistry(final ResourceManager manager, final WritableRegistry registry) { +- loadTagsForRegistry(manager, registry.key(), TagLoader.ElementLookup.fromWritableRegistry(registry)); + // Paper start - tag lifecycle - add registrar event cause -+ loadTagsForRegistry(resourceManager, registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); ++ loadTagsForRegistry(manager, registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); + } -+ public static void loadTagsForRegistry(ResourceManager resourceManager, WritableRegistry registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause cause) { ++ public static void loadTagsForRegistry(final ResourceManager manager, final WritableRegistry registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause cause) { ++ loadTagsForRegistry(manager, registry.key(), TagLoader.ElementLookup.fromWritableRegistry(registry), registry, cause); + // Paper end - tag lifecycle - add registrar event cause - ResourceKey> resourceKey = registry.key(); - TagLoader> tagLoader = new TagLoader<>(TagLoader.ElementLookup.fromWritableRegistry(registry), Registries.tagsDirPath(resourceKey)); -- tagLoader.build(tagLoader.load(resourceManager)) -+ tagLoader.build(tagLoader.load(resourceManager), io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.createEventConfig(registry, cause)) // Paper - tag lifecycle - add registrar event cause - .forEach((identifier, list) -> registry.bindTag(TagKey.create(resourceKey, identifier), (List>)list)); } -@@ -137,12 +_,12 @@ - return tags.entrySet().stream().collect(Collectors.toUnmodifiableMap(entry -> TagKey.create(registryKey, entry.getKey()), Entry::getValue)); + public static Map, List>> loadTagsForRegistry( + final ResourceManager manager, final ResourceKey> registryKey, final TagLoader.ElementLookup> lookup + ) { ++ // Paper start - tag lifecycle - add registrar event cause ++ return loadTagsForRegistry(manager, registryKey, lookup, null, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); ++ } ++ ++ public static Map, List>> loadTagsForRegistry( ++ final ResourceManager manager, final ResourceKey> registryKey, final TagLoader.ElementLookup> lookup, final WritableRegistry registry, final io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause cause ++ ) { ++ // Paper end - tag lifecycle - add registrar event cause + TagLoader> loader = new TagLoader<>(lookup, Registries.tagsDirPath(registryKey)); +- return wrapTags(registryKey, loader.build(loader.load(manager))); ++ return wrapTags(registryKey, loader.build(loader.load(manager), io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.createEventConfig(registry, cause))); // Paper - tag lifecycle + } + + private static Map, List>> wrapTags( +@@ -144,12 +_,12 @@ + return tags.entrySet().stream().collect(Collectors.toUnmodifiableMap(e -> TagKey.create(registryKey, e.getKey()), Entry::getValue)); } -- private static Optional> loadPendingTags(ResourceManager resourceManager, Registry registry) { -+ private static Optional> loadPendingTags(ResourceManager resourceManager, Registry registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause cause) { // Paper - add registrar event cause - ResourceKey> resourceKey = registry.key(); - TagLoader> tagLoader = new TagLoader<>( - (TagLoader.ElementLookup>)TagLoader.ElementLookup.fromFrozenRegistry(registry), Registries.tagsDirPath(resourceKey) +- private static Optional> loadPendingTags(final ResourceManager manager, final Registry registry) { ++ private static Optional> loadPendingTags(final ResourceManager manager, final Registry registry, final io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause cause) { // Paper - add registrar event cause + ResourceKey> key = registry.key(); + TagLoader> loader = new TagLoader<>( + (TagLoader.ElementLookup>)TagLoader.ElementLookup.fromFrozenRegistry(registry), Registries.tagsDirPath(key) ); -- TagLoader.LoadResult loadResult = new TagLoader.LoadResult<>(resourceKey, wrapTags(registry.key(), tagLoader.build(tagLoader.load(resourceManager)))); -+ TagLoader.LoadResult loadResult = new TagLoader.LoadResult<>(resourceKey, wrapTags(registry.key(), tagLoader.build(tagLoader.load(resourceManager), io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.createEventConfig(registry, cause)))); // Paper - add registrar event cause - return loadResult.tags().isEmpty() ? Optional.empty() : Optional.of(registry.prepareTagReload(loadResult)); +- TagLoader.LoadResult tags = new TagLoader.LoadResult<>(key, wrapTags(registry.key(), loader.build(loader.load(manager)))); ++ TagLoader.LoadResult tags = new TagLoader.LoadResult<>(key, wrapTags(registry.key(), loader.build(loader.load(manager), io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.createEventConfig(registry, cause)))); // Paper - add registrar event cause + return tags.tags().isEmpty() ? Optional.empty() : Optional.of(registry.prepareTagReload(tags)); } diff --git a/paper-server/patches/sources/net/minecraft/util/PlaceholderLookupProvider.java.patch b/paper-server/patches/sources/net/minecraft/util/PlaceholderLookupProvider.java.patch index 8b6037975a17..fd298fb998c3 100644 --- a/paper-server/patches/sources/net/minecraft/util/PlaceholderLookupProvider.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/PlaceholderLookupProvider.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/util/PlaceholderLookupProvider.java +++ b/net/minecraft/util/PlaceholderLookupProvider.java -@@ -52,6 +_,13 @@ +@@ -57,6 +_,13 @@ ) ); } diff --git a/paper-server/patches/sources/net/minecraft/util/SimpleBitStorage.java.patch b/paper-server/patches/sources/net/minecraft/util/SimpleBitStorage.java.patch index 29822af20e8d..8de07f4f8ce7 100644 --- a/paper-server/patches/sources/net/minecraft/util/SimpleBitStorage.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/SimpleBitStorage.java.patch @@ -10,54 +10,54 @@ + private final int divideAdd; private final long divideAddUnsigned; // Paper - Perf: Optimize SimpleBitStorage private final int divideShift; - public SimpleBitStorage(int bits, int size, int[] data) { + public SimpleBitStorage(final int bits, final int size, final int[] values) { @@ -248,8 +_,8 @@ this.mask = (1L << bits) - 1L; this.valuesPerLong = (char)(64 / bits); - int i = 3 * (this.valuesPerLong - 1); -- this.divideMul = MAGIC[i + 0]; -- this.divideAdd = MAGIC[i + 1]; -+ this.divideMul = MAGIC[i + 0]; this.divideMulUnsigned = Integer.toUnsignedLong(this.divideMul); // Paper - Perf: Optimize SimpleBitStorage -+ this.divideAdd = MAGIC[i + 1]; this.divideAddUnsigned = Integer.toUnsignedLong(this.divideAdd); // Paper - Perf: Optimize SimpleBitStorage - this.divideShift = MAGIC[i + 2]; - int i1 = (size + this.valuesPerLong - 1) / this.valuesPerLong; + int row = 3 * (this.valuesPerLong - 1); +- this.divideMul = MAGIC[row + 0]; +- this.divideAdd = MAGIC[row + 1]; ++ this.divideMul = MAGIC[row + 0]; this.divideMulUnsigned = Integer.toUnsignedLong(this.divideMul); // Paper - Perf: Optimize SimpleBitStorage ++ this.divideAdd = MAGIC[row + 1]; this.divideAddUnsigned = Integer.toUnsignedLong(this.divideAdd); // Paper - Perf: Optimize SimpleBitStorage + this.divideShift = MAGIC[row + 2]; + int requiredLength = (size + this.valuesPerLong - 1) / this.valuesPerLong; if (data != null) { @@ -264,15 +_,11 @@ } - private int cellIndex(int index) { -- long l = Integer.toUnsignedLong(this.divideMul); -- long l1 = Integer.toUnsignedLong(this.divideAdd); -- return (int)(index * l + l1 >> 32 >> this.divideShift); -+ return (int)(index * this.divideMulUnsigned + this.divideAddUnsigned >> 32 >> this.divideShift); // Paper - Perf: Optimize SimpleBitStorage + private int cellIndex(final int bitIndex) { +- long mul = Integer.toUnsignedLong(this.divideMul); +- long add = Integer.toUnsignedLong(this.divideAdd); +- return (int)(bitIndex * mul + add >> 32 >> this.divideShift); ++ return (int)(bitIndex * this.divideMulUnsigned + this.divideAddUnsigned >> 32 >> this.divideShift); // Paper - Perf: Optimize SimpleBitStorage } @Override -- public int getAndSet(int index, int value) { +- public int getAndSet(final int index, final int value) { - Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); - Validate.inclusiveBetween(0L, this.mask, (long)value); -+ public final int getAndSet(int index, int value) { // Paper - Perf: Optimize SimpleBitStorage - int i = this.cellIndex(index); - long l = this.data[i]; - int i1 = (index - i * this.valuesPerLong) * this.bits; ++ public final int getAndSet(final int index, final int value) { // Paper - Perf: Optimize SimpleBitStorage + int cellIndex = this.cellIndex(index); + long cellValue = this.data[cellIndex]; + int bitIndex = (index - cellIndex * this.valuesPerLong) * this.bits; @@ -282,9 +_,7 @@ } @Override -- public void set(int index, int value) { +- public void set(final int index, final int value) { - Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); - Validate.inclusiveBetween(0L, this.mask, (long)value); -+ public final void set(int index, int value) { // Paper - Perf: Optimize SimpleBitStorage - int i = this.cellIndex(index); - long l = this.data[i]; - int i1 = (index - i * this.valuesPerLong) * this.bits; ++ public final void set(final int index, final int value) { // Paper - Perf: Optimize SimpleBitStorage + int cellIndex = this.cellIndex(index); + long cellValue = this.data[cellIndex]; + int bitIndex = (index - cellIndex * this.valuesPerLong) * this.bits; @@ -292,8 +_,7 @@ } @Override -- public int get(int index) { +- public int get(final int index) { - Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); -+ public final int get(int index) { // Paper - Perf: Optimize SimpleBitStorage - int i = this.cellIndex(index); - long l = this.data[i]; - int i1 = (index - i * this.valuesPerLong) * this.bits; ++ public final int get(final int index) { // Paper - Perf: Optimize SimpleBitStorage + int cellIndex = this.cellIndex(index); + long cellValue = this.data[cellIndex]; + int bitIndex = (index - cellIndex * this.valuesPerLong) * this.bits; diff --git a/paper-server/patches/sources/net/minecraft/util/SpawnUtil.java.patch b/paper-server/patches/sources/net/minecraft/util/SpawnUtil.java.patch index f28bb2f44a3a..f4a2ad0d5ea7 100644 --- a/paper-server/patches/sources/net/minecraft/util/SpawnUtil.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/SpawnUtil.java.patch @@ -6,40 +6,40 @@ public class SpawnUtil { + public static Optional trySpawnMob( - EntityType entityType, - EntitySpawnReason spawnReason, + final EntityType entityType, + final EntitySpawnReason spawnReason, @@ -27,6 +_,24 @@ - SpawnUtil.Strategy strategy, - boolean checkCollision + final SpawnUtil.Strategy strategy, + final boolean checkCollisions ) { + // CraftBukkit start -+ return trySpawnMob(entityType, spawnReason, level, pos, attempts, range, yOffset, strategy, checkCollision, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT, null); // Paper - pre creature spawn event ++ return trySpawnMob(entityType, spawnReason, level, start, spawnAttempts, spawnRangeXZ, spawnRangeY, strategy, checkCollisions, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT, null); // Paper - pre creature spawn event + } + + public static Optional trySpawnMob( -+ EntityType entityType, -+ EntitySpawnReason spawnReason, -+ ServerLevel level, -+ BlockPos pos, -+ int attempts, -+ int range, -+ int yOffset, -+ SpawnUtil.Strategy strategy, -+ boolean checkCollision, -+ org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason, -+ @javax.annotation.Nullable Runnable onAbort // Paper - pre creature spawn event ++ final EntityType entityType, ++ final EntitySpawnReason spawnReason, ++ final ServerLevel level, ++ final BlockPos start, ++ final int spawnAttempts, ++ final int spawnRangeXZ, ++ final int spawnRangeY, ++ final SpawnUtil.Strategy strategy, ++ final boolean checkCollisions, ++ final org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason, ++ final @org.jspecify.annotations.Nullable Runnable onAbort // Paper - pre creature spawn event + ) { + // CraftBukkit end - BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); + BlockPos.MutableBlockPos searchPos = start.mutable(); + RandomSource random = level.getRandom(); - for (int i = 0; i < attempts; i++) { -@@ -39,15 +_,32 @@ - !checkCollision - || level.noCollision(entityType.getSpawnAABB(mutableBlockPos.getX() + 0.5, mutableBlockPos.getY(), mutableBlockPos.getZ() + 0.5)) - )) { +@@ -37,15 +_,32 @@ + if (level.getWorldBorder().isWithinBounds(searchPos) + && moveToPossibleSpawnPosition(level, spawnRangeY, searchPos, strategy) + && (!checkCollisions || level.noCollision(entityType.getSpawnAABB(searchPos.getX() + 0.5, searchPos.getY(), searchPos.getZ() + 0.5)))) { + // Paper start - PreCreatureSpawnEvent + final com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent( -+ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level), ++ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(start, level), + org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(entityType), + reason + ); @@ -53,7 +53,7 @@ + break; + } + // Paper end - PreCreatureSpawnEvent - T mob = (T)entityType.create(level, null, mutableBlockPos, spawnReason, false, false); + T mob = (T)entityType.create(level, null, searchPos, spawnReason, false, false); if (mob != null) { if (mob.checkSpawnRules(level, spawnReason) && mob.checkSpawnObstruction(level)) { - level.addFreshEntityWithPassengers(mob); diff --git a/paper-server/patches/sources/net/minecraft/util/StringUtil.java.patch b/paper-server/patches/sources/net/minecraft/util/StringUtil.java.patch index 99b603259661..3e115c789059 100644 --- a/paper-server/patches/sources/net/minecraft/util/StringUtil.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/StringUtil.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/util/StringUtil.java +++ b/net/minecraft/util/StringUtil.java @@ -85,6 +_,25 @@ - return stringBuilder.toString(); + return builder.toString(); } + // Paper start - Username validation @@ -23,6 +23,6 @@ + } + // Paper end - Username validation + - public static boolean isWhitespace(int character) { - return Character.isWhitespace(character) || Character.isSpaceChar(character); + public static boolean isWhitespace(final int codepoint) { + return Character.isWhitespace(codepoint) || Character.isSpaceChar(codepoint); } diff --git a/paper-server/patches/sources/net/minecraft/util/TickThrottler.java.patch b/paper-server/patches/sources/net/minecraft/util/TickThrottler.java.patch index b50d74a2be4f..32d86d8b6da4 100644 --- a/paper-server/patches/sources/net/minecraft/util/TickThrottler.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/TickThrottler.java.patch @@ -7,7 +7,7 @@ - private int count; + private final java.util.concurrent.atomic.AtomicInteger count = new java.util.concurrent.atomic.AtomicInteger(); // CraftBukkit - multithreaded field - public TickThrottler(int incrementStep, int threshold) { + public TickThrottler(final int incrementStep, final int threshold) { this.incrementStep = incrementStep; @@ -11,16 +_,31 @@ } diff --git a/paper-server/patches/sources/net/minecraft/util/Util.java.patch b/paper-server/patches/sources/net/minecraft/util/Util.java.patch index 02d529a2ced3..5e5da68f47bc 100644 --- a/paper-server/patches/sources/net/minecraft/util/Util.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/Util.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/util/Util.java +++ b/net/minecraft/util/Util.java -@@ -95,9 +_,25 @@ +@@ -96,9 +_,25 @@ private static final int DEFAULT_MAX_THREADS = 255; private static final int DEFAULT_SAFE_FILE_OPERATION_RETRIES = 10; private static final String MAX_THREADS_SYSTEM_PROPERTY = "max.bg.threads"; @@ -28,15 +28,15 @@ private static final DateTimeFormatter FILENAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT); public static final int LINEAR_LOOKUP_THRESHOLD = 8; private static final Set ALLOWED_UNTRUSTED_LINK_PROTOCOLS = Set.of("http", "https"); -@@ -116,6 +_,7 @@ +@@ -117,6 +_,7 @@ .findFirst() .orElseThrow(() -> new IllegalStateException("No jar file system provider found")); - private static Consumer thePauser = string -> {}; + private static Consumer thePauser = msg -> {}; + public static final double COLLISION_EPSILON = 1.0E-7; // Paper - Check distance in entity interactions public static Collector, ?, Map> toMap() { return Collectors.toMap(Entry::getKey, Entry::getValue); -@@ -138,7 +_,7 @@ +@@ -139,7 +_,7 @@ } public static long getNanos() { @@ -45,31 +45,31 @@ } public static long getEpochMillis() { -@@ -149,9 +_,10 @@ +@@ -150,9 +_,10 @@ return FILENAME_DATE_TIME_FORMATTER.format(ZonedDateTime.now()); } -- private static TracingExecutor makeExecutor(String name) { -+ private static TracingExecutor makeExecutor(String name, final int priorityModifier) { // Paper - Perf: add priority - int i = maxAllowedExecutorThreads(); -- ExecutorService directExecutorService; +- private static TracingExecutor makeExecutor(final String name) { ++ private static TracingExecutor makeExecutor(final String name, final int priorityModifier) { // Paper - Perf: add priority + int threads = maxAllowedExecutorThreads(); +- ExecutorService executor; + // Paper start - Perf: use simpler thread pool that allows 1 thread and reduce worldgen thread worker count for low core count CPUs -+ final ExecutorService directExecutorService; - if (i <= 0) { - directExecutorService = MoreExecutors.newDirectExecutorService(); ++ final ExecutorService executor; + if (threads <= 0) { + executor = MoreExecutors.newDirectExecutorService(); } else { -@@ -176,16 +_,30 @@ - super.onTermination(throwOnTermination); +@@ -177,16 +_,30 @@ + super.onTermination(exception); } }; -+ forkJoinWorkerThread.setPriority(Thread.NORM_PRIORITY + priorityModifier); // Paper - Deprioritize over main - forkJoinWorkerThread.setName(string); - return forkJoinWorkerThread; ++ thread.setPriority(Thread.NORM_PRIORITY + priorityModifier); // Paper - Deprioritize over main + thread.setName(threadName); + return thread; - }, Util::onThreadException, true); + }, Util::onThreadException, true, 0, Integer.MAX_VALUE, 1, null, 365, TimeUnit.DAYS); // Paper - do not expire threads } - return new TracingExecutor(directExecutorService); + return new TracingExecutor(executor); } public static int maxAllowedExecutorThreads() { @@ -91,7 +91,7 @@ } private static int getMaxThreads() { -@@ -235,6 +_,21 @@ +@@ -236,6 +_,21 @@ return thread; })); } @@ -111,5 +111,5 @@ + } + // Paper end - Separate dimension data IO pool - public static void throwAsRuntime(Throwable throwable) { + public static void throwAsRuntime(final Throwable throwable) { throw throwable instanceof RuntimeException ? (RuntimeException)throwable : new RuntimeException(throwable); diff --git a/paper-server/patches/sources/net/minecraft/util/ZeroBitStorage.java.patch b/paper-server/patches/sources/net/minecraft/util/ZeroBitStorage.java.patch index 93069aec3731..de6cbf1fea52 100644 --- a/paper-server/patches/sources/net/minecraft/util/ZeroBitStorage.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/ZeroBitStorage.java.patch @@ -4,28 +4,28 @@ } @Override -- public int getAndSet(int index, int value) { +- public int getAndSet(final int index, final int value) { - Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); - Validate.inclusiveBetween(0L, 0L, (long)value); -+ public final int getAndSet(int index, int value) { // Paper - Perf: Optimize SimpleBitStorage ++ public final int getAndSet(final int index, final int value) { // Paper - Perf: Optimize SimpleBitStorage + //Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); // Paper - Perf: Optimize SimpleBitStorage + //Validate.inclusiveBetween(0L, 0L, (long)value); // Paper - Perf: Optimize SimpleBitStorage return 0; } @Override -- public void set(int index, int value) { +- public void set(final int index, final int value) { - Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); - Validate.inclusiveBetween(0L, 0L, (long)value); -+ public final void set(int index, int value) { // Paper - Perf: Optimize SimpleBitStorage ++ public final void set(final int index, final int value) { // Paper - Perf: Optimize SimpleBitStorage + //Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); // Paper - Perf: Optimize SimpleBitStorage + //Validate.inclusiveBetween(0L, 0L, (long)value); // Paper - Perf: Optimize SimpleBitStorage } @Override -- public int get(int index) { +- public int get(final int index) { - Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); -+ public final int get(int index) { // Paper - Perf: Optimize SimpleBitStorage ++ public final int get(final int index) { // Paper - Perf: Optimize SimpleBitStorage + //Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); // Paper - Perf: Optimize SimpleBitStorage return 0; } diff --git a/paper-server/patches/sources/net/minecraft/util/datafix/DataFixTypes.java.patch b/paper-server/patches/sources/net/minecraft/util/datafix/DataFixTypes.java.patch new file mode 100644 index 000000000000..b4b8c5b0387c --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/util/datafix/DataFixTypes.java.patch @@ -0,0 +1,18 @@ +--- a/net/minecraft/util/datafix/DataFixTypes.java ++++ b/net/minecraft/util/datafix/DataFixTypes.java +@@ -15,6 +_,7 @@ + import net.minecraft.util.datafix.fixes.References; + + public enum DataFixTypes { ++ NONE(null), // Paper - add no-op fix for custom types until we actually need fixers for them + LEVEL(References.LEVEL), + LEVEL_SUMMARY(References.LIGHTWEIGHT_LEVEL), + PLAYER(References.PLAYER), +@@ -81,6 +_,7 @@ + } + + public Dynamic update(final DataFixer fixerUpper, final Dynamic input, final int fromVersion, final int toVersion) { ++ if (this.type == null) return input; // Paper - add no-op fix for custom types until we actually need fixers for them + return fixerUpper.update(this.type, input, fromVersion, toVersion); + } + diff --git a/paper-server/patches/sources/net/minecraft/util/datafix/DataFixers.java.patch b/paper-server/patches/sources/net/minecraft/util/datafix/DataFixers.java.patch index d89f25336169..7a93e5373491 100644 --- a/paper-server/patches/sources/net/minecraft/util/datafix/DataFixers.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/datafix/DataFixers.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/util/datafix/DataFixers.java +++ b/net/minecraft/util/datafix/DataFixers.java -@@ -534,6 +_,24 @@ - Schema schema44 = builder.addSchema(1456, SAME_NAMESPACED); - builder.addFixer(new EntityItemFrameDirectionFix(schema44, false)); - Schema schema45 = builder.addSchema(1458, V1458::new); +@@ -549,6 +_,24 @@ + Schema v1456 = fixerUpper.addSchema(1456, SAME_NAMESPACED); + fixerUpper.addFixer(new EntityItemFrameDirectionFix(v1456, false)); + Schema v1458 = fixerUpper.addSchema(1458, V1458::new); + // CraftBukkit start + // API allows setting player custom names, so we need to convert them. + // This does *not* handle upgrades in any other version, but generally those shouldn't need conversion. -+ builder.addFixer(new DataFix(schema45, false) { ++ fixerUpper.addFixer(new DataFix(v1458, false) { + @Override + protected TypeRewriteRule makeRule() { + return this.fixTypeEverywhereTyped("Player CustomName", this.getInputSchema().getType(References.PLAYER), typed -> { @@ -22,6 +22,6 @@ + } + }); + // CraftBukkit end - builder.addFixer(new EntityCustomNameToComponentFix(schema45)); - builder.addFixer(new ItemCustomNameToComponentFix(schema45)); - builder.addFixer(new BlockEntityCustomNameToComponentFix(schema45)); + fixerUpper.addFixer(new EntityCustomNameToComponentFix(v1458)); + fixerUpper.addFixer(new ItemCustomNameToComponentFix(v1458)); + fixerUpper.addFixer(new BlockEntityCustomNameToComponentFix(v1458)); diff --git a/paper-server/patches/sources/net/minecraft/util/datafix/fixes/ItemStackMapIdFix.java.patch b/paper-server/patches/sources/net/minecraft/util/datafix/fixes/ItemStackMapIdFix.java.patch index c40c8e02d28c..ffa5939b56ae 100644 --- a/paper-server/patches/sources/net/minecraft/util/datafix/fixes/ItemStackMapIdFix.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/datafix/fixes/ItemStackMapIdFix.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/util/datafix/fixes/ItemStackMapIdFix.java +++ b/net/minecraft/util/datafix/fixes/ItemStackMapIdFix.java @@ -29,7 +_,7 @@ - Dynamic dynamic = typed.get(DSL.remainderFinder()); - Typed typed1 = typed.getOrCreateTyped(opticFinder1); - Dynamic dynamic1 = typed1.get(DSL.remainderFinder()); -- dynamic1 = dynamic1.set("map", dynamic1.createInt(dynamic.get("Damage").asInt(0))); -+ if (dynamic1.getElement("map").result().isEmpty()) dynamic1 = dynamic1.set("map", dynamic1.createInt(dynamic.get("Damage").asInt(0))); // CraftBukkit - return typed.set(opticFinder1, typed1.set(DSL.remainderFinder(), dynamic1)); + Dynamic rest = input.get(DSL.remainderFinder()); + Typed tag = input.getOrCreateTyped(tagF); + Dynamic tagRest = tag.get(DSL.remainderFinder()); +- tagRest = tagRest.set("map", tagRest.createInt(rest.get("Damage").asInt(0))); ++ if (tagRest.getElement("map").result().isEmpty()) tagRest = tagRest.set("map", tagRest.createInt(rest.get("Damage").asInt(0))); // CraftBukkit + return input.set(tagF, tag.set(DSL.remainderFinder(), tagRest)); } else { - return typed; + return input; diff --git a/paper-server/patches/sources/net/minecraft/util/datafix/fixes/ItemStackTheFlatteningFix.java.patch b/paper-server/patches/sources/net/minecraft/util/datafix/fixes/ItemStackTheFlatteningFix.java.patch index c20365405cf0..6573728534eb 100644 --- a/paper-server/patches/sources/net/minecraft/util/datafix/fixes/ItemStackTheFlatteningFix.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/datafix/fixes/ItemStackTheFlatteningFix.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/util/datafix/fixes/ItemStackTheFlatteningFix.java +++ b/net/minecraft/util/datafix/fixes/ItemStackTheFlatteningFix.java @@ -423,7 +_,7 @@ - if (DAMAGE_IDS.contains(optional.get().getSecond())) { - Typed typed2 = typed.getOrCreateTyped(opticFinder1); - Dynamic dynamic1 = typed2.get(DSL.remainderFinder()); -- dynamic1 = dynamic1.set("Damage", dynamic1.createInt(_int)); -+ if (_int != 0) dynamic1 = dynamic1.set("Damage", dynamic1.createInt(_int)); // CraftBukkit - typed1 = typed1.set(opticFinder1, typed2.set(DSL.remainderFinder(), dynamic1)); + if (DAMAGE_IDS.contains(id.get().getSecond())) { + Typed tag = input.getOrCreateTyped(tagF); + Dynamic tagRest = tag.get(DSL.remainderFinder()); +- tagRest = tagRest.set("Damage", tagRest.createInt(data)); ++ if (data != 0) tagRest = tagRest.set("Damage", tagRest.createInt(data)); // CraftBukkit + output = output.set(tagF, tag.set(DSL.remainderFinder(), tagRest)); } diff --git a/paper-server/patches/sources/net/minecraft/util/filefix/fixes/LegacyStructureFileFix.java.patch b/paper-server/patches/sources/net/minecraft/util/filefix/fixes/LegacyStructureFileFix.java.patch new file mode 100644 index 000000000000..338fe9d3c8ef --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/util/filefix/fixes/LegacyStructureFileFix.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/util/filefix/fixes/LegacyStructureFileFix.java ++++ b/net/minecraft/util/filefix/fixes/LegacyStructureFileFix.java +@@ -145,7 +_,7 @@ + } + + Optional generatorIdentifier = Optional.ofNullable(Identifier.tryParse(chunkGeneratorType)); +- CompoundTag dataFixContext = ChunkMap.getChunkDataFixContextTag(dimensionKey, generatorIdentifier); ++ CompoundTag dataFixContext = ChunkMap.getChunkDataFixContextTag(ResourceKey.create(Registries.LEVEL_STEM, dimensionKey.identifier()), generatorIdentifier); // CraftBukkit + storeLegacyStructureDataToChunks(dimensionFixEntry.structures, chunkNbt, dataFixContext, upgradeProgress); + } + } diff --git a/paper-server/patches/sources/net/minecraft/util/filefix/fixes/LevelDatToSavedDataFileFix.java.patch b/paper-server/patches/sources/net/minecraft/util/filefix/fixes/LevelDatToSavedDataFileFix.java.patch new file mode 100644 index 000000000000..c2ef2fd56574 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/util/filefix/fixes/LevelDatToSavedDataFileFix.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/util/filefix/fixes/LevelDatToSavedDataFileFix.java ++++ b/net/minecraft/util/filefix/fixes/LevelDatToSavedDataFileFix.java +@@ -120,6 +_,12 @@ + return content; + } else { + Dynamic playerTag = playerTagOpt.result().get(); ++ // Paper start - check if player tag is empty, for avoidance create fallback user file with not data ++ final Boolean emptyPlayerTag = playerTag.convert(net.minecraft.nbt.NbtOps.INSTANCE).getValue().asCompound().map(net.minecraft.nbt.CompoundTag::isEmpty).orElse(true); ++ if (emptyPlayerTag) { ++ return content.remove("Player"); ++ } ++ // Paper end + int dataVersion = NbtUtils.getDataVersion(playerTag); + Dynamic playerTagFixed = DataFixTypes.PLAYER.update(DataFixers.getDataFixer(), playerTag, dataVersion, this.getVersion()); + Optional> playerUuid = playerTagFixed.get("UUID").result(); diff --git a/paper-server/patches/sources/net/minecraft/util/random/WeightedList.java.patch b/paper-server/patches/sources/net/minecraft/util/random/WeightedList.java.patch index e67149c78ee4..7cc08b62e7e7 100644 --- a/paper-server/patches/sources/net/minecraft/util/random/WeightedList.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/random/WeightedList.java.patch @@ -11,17 +11,17 @@ private final List> items; private final WeightedList.@Nullable Selector selector; -- WeightedList(List> items) { -+ protected WeightedList(List> items) { // Paper - protected +- private WeightedList(final List> items) { ++ protected WeightedList(final List> items) { // Paper - protected this.items = List.copyOf(items); this.totalWeight = WeightedRandom.getTotalWeight(items, Weighted::weight); if (this.totalWeight == 0) { -@@ -128,7 +_,7 @@ +@@ -140,7 +_,7 @@ } public static class Builder { - private final ImmutableList.Builder> result = ImmutableList.builder(); + protected final ImmutableList.Builder> result = ImmutableList.builder(); - public WeightedList.Builder add(E element) { - return this.add(element, 1); + public WeightedList.Builder add(final E item) { + return this.add(item, 1); diff --git a/paper-server/patches/sources/net/minecraft/util/thread/BlockableEventLoop.java.patch b/paper-server/patches/sources/net/minecraft/util/thread/BlockableEventLoop.java.patch index 0e27c8dac9f8..9166a8d945c0 100644 --- a/paper-server/patches/sources/net/minecraft/util/thread/BlockableEventLoop.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/thread/BlockableEventLoop.java.patch @@ -1,15 +1,17 @@ --- a/net/minecraft/util/thread/BlockableEventLoop.java +++ b/net/minecraft/util/thread/BlockableEventLoop.java -@@ -33,6 +_,23 @@ +@@ -38,6 +_,45 @@ MetricsRegistry.INSTANCE.add(this); } + // Paper start -+ public final void executeAllRecentInternalTasks() { ++ public final boolean executeAllRecentInternalTasks() { + final int pending = this.pendingRunnables.size(); + + // note: due to possible recursive execution, we may execute more tasks than we want to + ++ int ran = 0; ++ + for (int i = 0; i < pending; ++i) { + final R run = this.pendingRunnables.poll(); + if (run == null) { @@ -17,15 +19,35 @@ + break; + } + this.doRunTask(run); ++ ++ran; ++ } ++ ++ return ran > 0; ++ } ++ ++ public boolean runTaskIf(final java.util.function.Predicate predicate) { ++ if (!this.isSameThread()) { ++ throw new IllegalStateException(); + } ++ ++ final R run = this.pendingRunnables.peek(); ++ if (run == null || (predicate != null && !predicate.test(run))) { ++ return false; ++ } ++ ++ this.pendingRunnables.remove(); ++ ++ this.doRunTask(run); ++ ++ return true; + } + // Paper end + - protected abstract boolean shouldRun(R runnable); + protected abstract boolean shouldRun(final R task); public boolean isSameThread() { -@@ -82,6 +_,14 @@ - task.run(); +@@ -87,6 +_,14 @@ + runnable.run(); } } + @@ -38,4 +60,4 @@ + // Paper end @Override - public void schedule(R task) { + public void schedule(final R r) { diff --git a/paper-server/patches/sources/net/minecraft/util/worldupdate/WorldUpgrader.java.patch b/paper-server/patches/sources/net/minecraft/util/worldupdate/WorldUpgrader.java.patch index fe6961f40b5f..a68e0a9f981c 100644 --- a/paper-server/patches/sources/net/minecraft/util/worldupdate/WorldUpgrader.java.patch +++ b/paper-server/patches/sources/net/minecraft/util/worldupdate/WorldUpgrader.java.patch @@ -1,20 +1,11 @@ --- a/net/minecraft/util/worldupdate/WorldUpgrader.java +++ b/net/minecraft/util/worldupdate/WorldUpgrader.java -@@ -89,7 +_,7 @@ - boolean recreateRegionFiles - ) { - this.dimensions = registryAccess.lookupOrThrow(Registries.LEVEL_STEM); -- this.levels = this.dimensions.registryKeySet().stream().map(Registries::levelStemToLevel).collect(Collectors.toUnmodifiableSet()); -+ this.levels = java.util.stream.Stream.of(levelStorage.dimensionType).map(Registries::levelStemToLevel).collect(Collectors.toUnmodifiableSet()); // CraftBukkit - this.eraseCache = eraseCache; - this.dataFixer = dataFixer; - this.levelStorage = levelStorage; -@@ -367,7 +_,7 @@ - int dataVersion = NbtUtils.getDataVersion(compoundTag); - ChunkGenerator chunkGenerator = WorldUpgrader.this.dimensions.getValueOrThrow(Registries.levelToLevelStem(dimension)).generator(); - CompoundTag compoundTag1 = regionStorage.upgradeChunkTag( -- compoundTag, -1, ChunkMap.getChunkDataFixContextTag(dimension, chunkGenerator.getTypeNameForDataFixer()) -+ compoundTag, -1, ChunkMap.getChunkDataFixContextTag(Registries.levelToLevelStem(dimension), chunkGenerator.getTypeNameForDataFixer()), null // CraftBukkit - ); - ChunkPos chunkPos1 = new ChunkPos(compoundTag1.getIntOr("xPos", 0), compoundTag1.getIntOr("zPos", 0)); - if (!chunkPos1.equals(chunkPos)) { +@@ -68,7 +_,7 @@ + + public static CompoundTag getDataFixContextTag(final Registry dimensions, final ResourceKey dimension) { + ChunkGenerator generator = dimensions.getValueOrThrow(Registries.levelToLevelStem(dimension)).generator(); +- return ChunkMap.getChunkDataFixContextTag(dimension, generator.getTypeNameForDataFixer()); ++ return ChunkMap.getChunkDataFixContextTag(Registries.levelToLevelStem(dimension), generator.getTypeNameForDataFixer()); // CraftBukkit + } + + public static boolean verifyChunkPosAndEraseCache(final ChunkPos pos, final CompoundTag upgradedTag) { diff --git a/paper-server/patches/sources/net/minecraft/world/BossEvent.java.patch b/paper-server/patches/sources/net/minecraft/world/BossEvent.java.patch index 6d4cf0bfabe2..953650c178c1 100644 --- a/paper-server/patches/sources/net/minecraft/world/BossEvent.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/BossEvent.java.patch @@ -6,7 +6,7 @@ protected boolean createWorldFog; + public net.kyori.adventure.bossbar.BossBar adventure; // Paper - public BossEvent(UUID id, Component name, BossEvent.BossBarColor color, BossEvent.BossBarOverlay overlay) { + public BossEvent(final UUID id, final Component name, final BossEvent.BossBarColor color, final BossEvent.BossBarOverlay overlay) { this.id = id; @@ -29,61 +_,75 @@ } @@ -16,7 +16,7 @@ return this.name; } - public void setName(Component name) { + public void setName(final Component name) { + if (this.adventure != null) this.adventure.name(io.papermc.paper.adventure.PaperAdventure.asAdventure(name)); // Paper this.name = name; } @@ -26,7 +26,7 @@ return this.progress; } - public void setProgress(float progress) { + public void setProgress(final float progress) { + if (this.adventure != null) this.adventure.progress(progress); // Paper this.progress = progress; } @@ -36,7 +36,7 @@ return this.color; } - public void setColor(BossEvent.BossBarColor color) { + public void setColor(final BossEvent.BossBarColor color) { + if (this.adventure != null) this.adventure.color(io.papermc.paper.adventure.PaperAdventure.asAdventure(color)); // Paper this.color = color; } @@ -46,7 +46,7 @@ return this.overlay; } - public void setOverlay(BossEvent.BossBarOverlay overlay) { + public void setOverlay(final BossEvent.BossBarOverlay overlay) { + if (this.adventure != null) this.adventure.overlay(io.papermc.paper.adventure.PaperAdventure.asAdventure(overlay)); // Paper this.overlay = overlay; } @@ -56,9 +56,9 @@ return this.darkenScreen; } - public BossEvent setDarkenScreen(boolean darkenSky) { -+ if (this.adventure != null) io.papermc.paper.adventure.PaperAdventure.setFlag(this.adventure, net.kyori.adventure.bossbar.BossBar.Flag.DARKEN_SCREEN, darkenSky); // Paper - this.darkenScreen = darkenSky; + public BossEvent setDarkenScreen(final boolean darkenScreen) { ++ if (this.adventure != null) io.papermc.paper.adventure.PaperAdventure.setFlag(this.adventure, net.kyori.adventure.bossbar.BossBar.Flag.DARKEN_SCREEN, darkenScreen); // Paper + this.darkenScreen = darkenScreen; return this; } @@ -67,15 +67,15 @@ return this.playBossMusic; } - public BossEvent setPlayBossMusic(boolean playEndBossMusic) { -+ if (this.adventure != null) io.papermc.paper.adventure.PaperAdventure.setFlag(this.adventure, net.kyori.adventure.bossbar.BossBar.Flag.PLAY_BOSS_MUSIC, playEndBossMusic); // Paper - this.playBossMusic = playEndBossMusic; + public BossEvent setPlayBossMusic(final boolean playBossMusic) { ++ if (this.adventure != null) io.papermc.paper.adventure.PaperAdventure.setFlag(this.adventure, net.kyori.adventure.bossbar.BossBar.Flag.PLAY_BOSS_MUSIC, playBossMusic); // Paper + this.playBossMusic = playBossMusic; return this; } - public BossEvent setCreateWorldFog(boolean createFog) { -+ if (this.adventure != null) io.papermc.paper.adventure.PaperAdventure.setFlag(this.adventure, net.kyori.adventure.bossbar.BossBar.Flag.CREATE_WORLD_FOG, createFog); // Paper - this.createWorldFog = createFog; + public BossEvent setCreateWorldFog(final boolean createWorldFog) { ++ if (this.adventure != null) io.papermc.paper.adventure.PaperAdventure.setFlag(this.adventure, net.kyori.adventure.bossbar.BossBar.Flag.CREATE_WORLD_FOG, createWorldFog); // Paper + this.createWorldFog = createWorldFog; return this; } diff --git a/paper-server/patches/sources/net/minecraft/world/CompoundContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/CompoundContainer.java.patch index 8b3f531611dc..eb23c06dbef1 100644 --- a/paper-server/patches/sources/net/minecraft/world/CompoundContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/CompoundContainer.java.patch @@ -36,7 +36,7 @@ + } + + @Override -+ public @javax.annotation.Nullable org.bukkit.inventory.InventoryHolder getOwner() { ++ public org.bukkit.inventory.@org.jspecify.annotations.Nullable InventoryHolder getOwner() { + return null; // This method won't be called since CraftInventoryDoubleChest doesn't defer to here + } + @@ -51,10 +51,10 @@ + } + // CraftBukkit end + - public CompoundContainer(Container container1, Container container2) { + public CompoundContainer(final Container container1, final Container container2) { this.container1 = container1; this.container2 = container2; -@@ -59,7 +_,7 @@ +@@ -57,7 +_,7 @@ @Override public int getMaxStackSize() { diff --git a/paper-server/patches/sources/net/minecraft/world/Container.java.patch b/paper-server/patches/sources/net/minecraft/world/Container.java.patch index b5e9379ac384..3cb5c688c52b 100644 --- a/paper-server/patches/sources/net/minecraft/world/Container.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/Container.java.patch @@ -1,17 +1,17 @@ --- a/net/minecraft/world/Container.java +++ b/net/minecraft/world/Container.java -@@ -31,9 +_,7 @@ +@@ -32,9 +_,7 @@ - void setItem(int slot, ItemStack stack); + void setItem(int slot, ItemStack itemStack); - default int getMaxStackSize() { - return 99; - } + int getMaxStackSize(); // CraftBukkit - default int getMaxStackSize(ItemStack stack) { - return Math.min(this.getMaxStackSize(), stack.getMaxStackSize()); -@@ -142,4 +_,22 @@ + default int getMaxStackSize(final ItemStack itemStack) { + return Math.min(this.getMaxStackSize(), itemStack.getMaxStackSize()); +@@ -147,4 +_,22 @@ } } } @@ -25,11 +25,11 @@ + + java.util.List getViewers(); + -+ @javax.annotation.Nullable org.bukkit.inventory.InventoryHolder getOwner(); ++ org.bukkit.inventory.@Nullable InventoryHolder getOwner(); + + void setMaxStackSize(int size); + -+ @javax.annotation.Nullable org.bukkit.Location getLocation(); ++ org.bukkit.@Nullable Location getLocation(); + + int MAX_STACK = Item.ABSOLUTE_MAX_STACK_SIZE; + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/InteractionResult.java.patch b/paper-server/patches/sources/net/minecraft/world/InteractionResult.java.patch index 2b132d669a06..f5ed75b9f4a5 100644 --- a/paper-server/patches/sources/net/minecraft/world/InteractionResult.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/InteractionResult.java.patch @@ -6,11 +6,11 @@ - public record Success(InteractionResult.SwingSource swingSource, InteractionResult.ItemContext itemContext) implements InteractionResult { + // Paper start - track more context in interaction result -+ public record PaperSuccessContext(net.minecraft.core.@Nullable BlockPos placedBlockPosition) { ++ public record PaperSuccessContext(net.minecraft.core.@Nullable BlockPos placedPos) { + static PaperSuccessContext DEFAULT = new PaperSuccessContext(null); + -+ public PaperSuccessContext placedBlockAt(final net.minecraft.core.BlockPos blockPos) { -+ return new PaperSuccessContext(blockPos); ++ public PaperSuccessContext placedBlockAt(final net.minecraft.core.BlockPos pos) { ++ return new PaperSuccessContext(pos); + } + } + public record Success(InteractionResult.SwingSource swingSource, InteractionResult.ItemContext itemContext, PaperSuccessContext paperSuccessContext) implements InteractionResult { @@ -18,7 +18,7 @@ + return new InteractionResult.Success(this.swingSource, this.itemContext, edit.apply(this.paperSuccessContext)); + } + -+ public Success(final net.minecraft.world.InteractionResult.SwingSource swingSource, final net.minecraft.world.InteractionResult.ItemContext itemContext) { ++ public Success(final InteractionResult.SwingSource swingSource, final InteractionResult.ItemContext itemContext) { + this(swingSource, itemContext, PaperSuccessContext.DEFAULT); + } + // Paper end - track more context in interaction result @@ -27,9 +27,9 @@ return true; } - public InteractionResult.Success heldItemTransformedTo(ItemStack stack) { -- return new InteractionResult.Success(this.swingSource, new InteractionResult.ItemContext(true, stack)); -+ return new InteractionResult.Success(this.swingSource, new InteractionResult.ItemContext(true, stack), this.paperSuccessContext); // Paper - track more context in interaction result + public InteractionResult.Success heldItemTransformedTo(final ItemStack itemStack) { +- return new InteractionResult.Success(this.swingSource, new InteractionResult.ItemContext(true, itemStack)); ++ return new InteractionResult.Success(this.swingSource, new InteractionResult.ItemContext(true, itemStack), this.paperSuccessContext); // Paper - track more context in interaction result } public InteractionResult.Success withoutItem() { diff --git a/paper-server/patches/sources/net/minecraft/world/LockCode.java.patch b/paper-server/patches/sources/net/minecraft/world/LockCode.java.patch index a031c69581ca..951cc1765580 100644 --- a/paper-server/patches/sources/net/minecraft/world/LockCode.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/LockCode.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/LockCode.java +++ b/net/minecraft/world/LockCode.java -@@ -22,8 +_,14 @@ +@@ -23,8 +_,14 @@ } } + @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper - use new variant for event compatibility - public boolean canUnlock(Player player) { + public boolean canUnlock(final Player player) { - return player.isSpectator() || this.unlocksWith(player.getMainHandItem()); + // Paper start - allow overriding key item + return canUnlock(player, player.getMainHandItem()); @@ -15,4 +15,4 @@ + return player.isSpectator() || this.unlocksWith(item); // Paper - allow overriding key item } - public static LockCode fromTag(ValueInput input) { + public static LockCode fromTag(final ValueInput parent) { diff --git a/paper-server/patches/sources/net/minecraft/world/RandomizableContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/RandomizableContainer.java.patch index cf20d20fe90d..9cacb279dc3b 100644 --- a/paper-server/patches/sources/net/minecraft/world/RandomizableContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/RandomizableContainer.java.patch @@ -2,32 +2,32 @@ +++ b/net/minecraft/world/RandomizableContainer.java @@ -26,7 +_,7 @@ - void setLootTable(@Nullable ResourceKey lootTable); + void setLootTable(final @Nullable ResourceKey lootTable); -- default void setLootTable(ResourceKey lootTable, long seed) { -+ default void setLootTable(@Nullable ResourceKey lootTable, long seed) { // Paper - add nullable +- default void setLootTable(final ResourceKey lootTable, final long seed) { ++ default void setLootTable(final @Nullable ResourceKey lootTable, final long seed) { // Paper - add nullable this.setLootTable(lootTable); this.setLootTableSeed(seed); } -@@ -48,8 +_,9 @@ - default boolean tryLoadLootTable(ValueInput input) { - ResourceKey resourceKey = input.read("LootTable", LootTable.KEY_CODEC).orElse(null); - this.setLootTable(resourceKey); -+ if (this.lootableData() != null && resourceKey != null) this.lootableData().loadNbt(input); // Paper - LootTable API - this.setLootTableSeed(input.getLongOr("LootTableSeed", 0L)); -- return resourceKey != null; -+ return resourceKey != null && this.lootableData() == null; // Paper - only track the loot table if there is chance for replenish +@@ -50,8 +_,9 @@ + default boolean tryLoadLootTable(final ValueInput base) { + ResourceKey lootTable = base.read("LootTable", LootTable.KEY_CODEC).orElse(null); + this.setLootTable(lootTable); ++ if (this.lootableData() != null && lootTable != null) this.lootableData().loadNbt(base); // Paper - LootTable API + this.setLootTableSeed(base.getLongOr("LootTableSeed", 0L)); +- return lootTable != null; ++ return lootTable != null && this.lootableData() == null; // Paper - only track the loot table if there is chance for replenish } - default boolean trySaveLootTable(ValueOutput output) { -@@ -58,26 +_,42 @@ + default boolean trySaveLootTable(final ValueOutput base) { +@@ -60,26 +_,42 @@ return false; } else { - output.store("LootTable", LootTable.KEY_CODEC, lootTable); -+ if (this.lootableData() != null) this.lootableData().saveNbt(output); // Paper - LootTable API + base.store("LootTable", LootTable.KEY_CODEC, lootTable); ++ if (this.lootableData() != null) this.lootableData().saveNbt(base); // Paper - LootTable API long lootTableSeed = this.getLootTableSeed(); if (lootTableSeed != 0L) { - output.putLong("LootTableSeed", lootTableSeed); + base.putLong("LootTableSeed", lootTableSeed); } - return true; @@ -35,18 +35,18 @@ } } - default void unpackLootTable(@Nullable Player player) { + default void unpackLootTable(final @Nullable Player player) { + // Paper start - LootTable API + this.unpackLootTable(player, false); + } + default void unpackLootTable(@Nullable final Player player, final boolean forceClearLootTable) { + // Paper end - LootTable API Level level = this.getLevel(); - BlockPos blockPos = this.getBlockPos(); - ResourceKey lootTable = this.getLootTable(); -- if (lootTable != null && level != null && level.getServer() != null) { + BlockPos worldPosition = this.getBlockPos(); + ResourceKey lootTableKey = this.getLootTable(); +- if (lootTableKey != null && level != null && level.getServer() != null) { + // Paper start - LootTable API -+ lootReplenish: if (lootTable != null && level != null && level.getServer() != null) { ++ lootReplenish: if (lootTableKey != null && level != null && level.getServer() != null) { + if (this.lootableData() != null && !this.lootableData().shouldReplenish(this, com.destroystokyo.paper.loottable.PaperLootableInventoryData.CONTAINER, player)) { + if (forceClearLootTable) { + this.setLootTable(null); @@ -54,19 +54,19 @@ + break lootReplenish; + } + // Paper end - LootTable API - LootTable lootTable1 = level.getServer().reloadableRegistries().getLootTable(lootTable); + LootTable lootTable = level.getServer().reloadableRegistries().getLootTable(lootTableKey); if (player instanceof ServerPlayer) { - CriteriaTriggers.GENERATE_LOOT.trigger((ServerPlayer)player, lootTable); + CriteriaTriggers.GENERATE_LOOT.trigger((ServerPlayer)player, lootTableKey); } + if (forceClearLootTable || this.lootableData() == null || this.lootableData().shouldClearLootTable(this, com.destroystokyo.paper.loottable.PaperLootableInventoryData.CONTAINER, player)) { // Paper - LootTable API this.setLootTable(null); + } // Paper - LootTable API - LootParams.Builder builder = new LootParams.Builder((ServerLevel)level).withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(blockPos)); + LootParams.Builder params = new LootParams.Builder((ServerLevel)level).withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(worldPosition)); if (player != null) { - builder.withLuck(player.getLuck()).withParameter(LootContextParams.THIS_ENTITY, player); -@@ -86,4 +_,16 @@ - lootTable1.fill(this, builder.create(LootContextParamSets.CHEST), this.getLootTableSeed()); + params.withLuck(player.getLuck()).withParameter(LootContextParams.THIS_ENTITY, player); +@@ -88,4 +_,16 @@ + lootTable.fill(this, params.create(LootContextParamSets.CHEST), this.getLootTableSeed()); } } + diff --git a/paper-server/patches/sources/net/minecraft/world/SimpleContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/SimpleContainer.java.patch index 5eeeade6049f..d16d9204ce28 100644 --- a/paper-server/patches/sources/net/minecraft/world/SimpleContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/SimpleContainer.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/world/SimpleContainer.java +++ b/net/minecraft/world/SimpleContainer.java -@@ -18,7 +_,90 @@ +@@ -15,7 +_,90 @@ + private final int size; public final NonNullList items; - private @Nullable List listeners; + // Paper start - add fields and methods + public List transaction = new java.util.ArrayList<>(); + private int maxStack = MAX_STACK; -+ protected org.bukkit.inventory.@Nullable InventoryHolder bukkitOwner; // Paper - annotation ++ protected org.bukkit.inventory.@org.jspecify.annotations.Nullable InventoryHolder bukkitOwner; // Paper - annotation + + @Override + public List getContents() { @@ -40,7 +40,7 @@ + } + + @Override -+ public org.bukkit.inventory.@Nullable InventoryHolder getOwner() { ++ public org.bukkit.inventory.@org.jspecify.annotations.Nullable InventoryHolder getOwner() { + // Paper start - Add missing InventoryHolders + if (this.bukkitOwner == null && this.bukkitOwnerCreator != null) { + this.bukkitOwner = this.bukkitOwnerCreator.get(); @@ -50,7 +50,7 @@ + } + + @Override -+ public org.bukkit.@Nullable Location getLocation() { ++ public org.bukkit.@org.jspecify.annotations.Nullable Location getLocation() { + // Paper start - Fix inventories returning null Locations + // When the block inventory does not have a tile state that implements getLocation, e. g. composters + if (this.bukkitOwner instanceof org.bukkit.inventory.BlockInventoryHolder blockInventoryHolder) { @@ -72,12 +72,12 @@ + } + // Paper end + - public SimpleContainer(int size) { + public SimpleContainer(final int size) { + this(size, null); + } + + // Paper start - Add missing InventoryHolders -+ private java.util.function.@Nullable Supplier bukkitOwnerCreator; ++ private java.util.function.@org.jspecify.annotations.Nullable Supplier bukkitOwnerCreator; + + public SimpleContainer(java.util.function.Supplier bukkitOwnerCreator, int size) { + this(size); @@ -85,7 +85,7 @@ + } + // Paper end - Add missing InventoryHolders + -+ public SimpleContainer(int size, org.bukkit.inventory.@Nullable InventoryHolder owner) { ++ public SimpleContainer(final int size, final org.bukkit.inventory.@org.jspecify.annotations.Nullable InventoryHolder owner) { + this.bukkitOwner = owner; + // Paper end this.size = size; diff --git a/paper-server/patches/sources/net/minecraft/world/clock/ServerClockManager.java.patch b/paper-server/patches/sources/net/minecraft/world/clock/ServerClockManager.java.patch new file mode 100644 index 000000000000..7722ffc60843 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/clock/ServerClockManager.java.patch @@ -0,0 +1,53 @@ +--- a/net/minecraft/world/clock/ServerClockManager.java ++++ b/net/minecraft/world/clock/ServerClockManager.java +@@ -97,7 +_,16 @@ + return set.booleanValue(); + } + +- public void addTicks(final Holder clock, final int ticks) { ++ // Paper start - time skip event ++ public java.util.OptionalLong getTotalTicksToTimeMarker(final Holder clock, final ResourceKey timeMarkerId) { ++ final ServerClockManager.ClockInstance instance = this.getInstance(clock); ++ final ClockTimeMarker timeMarker = instance.timeMarkers.get(timeMarkerId); ++ ++ return timeMarker != null ? java.util.OptionalLong.of(timeMarker.resolveTimeToMoveTo(instance.totalTicks)) : java.util.OptionalLong.empty(); ++ } ++ // Paper end - time skip event ++ ++ public void addTicks(final Holder clock, final long ticks) { // Paper + this.modifyClock(clock, instance -> instance.totalTicks = Math.max(instance.totalTicks + ticks, 0L)); + } + +@@ -113,7 +_,7 @@ + ServerClockManager.ClockInstance instance = this.getInstance(clock); + action.accept(instance); + Map, ClockNetworkState> updates = Map.of(clock, instance.packNetworkState(this.server)); +- this.server.getPlayerList().broadcastAll(new ClientboundSetTimePacket(this.getGameTime(), updates)); ++ this.server.getPlayerList().broadcastAll(new ClientboundSetTimePacket(this.getGameTime(), updates)); // TODO 26.1 per-player time + this.setDirty(); + + for (ServerLevel level : this.server.getAllLevels()) { +@@ -126,7 +_,23 @@ + return this.getInstance(definition).totalTicks; + } + ++ // Paper start ++ // TODO - snapshot: just make the inner class, fields and getter public ++ public boolean isPaused(final Holder definition) { ++ return this.getInstance(definition).paused; ++ } ++ ++ public float partialTick(final Holder definition) { ++ return this.getInstance(definition).partialTick; ++ } ++ ++ public float rate(final Holder definition) { ++ return this.getInstance(definition).rate; ++ } ++ // Paper end ++ + public ClientboundSetTimePacket createFullSyncPacket() { ++ // TODO - snapshot: 26.1 per-player time + return new ClientboundSetTimePacket(this.getGameTime(), Util.mapValues(this.clocks, clock -> clock.packNetworkState(this.server))); + } + diff --git a/paper-server/patches/sources/net/minecraft/world/damagesource/CombatTracker.java.patch b/paper-server/patches/sources/net/minecraft/world/damagesource/CombatTracker.java.patch index 9f7b4e9b5d5c..b4c24f057a19 100644 --- a/paper-server/patches/sources/net/minecraft/world/damagesource/CombatTracker.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/damagesource/CombatTracker.java.patch @@ -6,29 +6,29 @@ public boolean takingDamage; + public final io.papermc.paper.world.damagesource.PaperCombatTrackerWrapper paperCombatTracker; // Paper - Combat tracker API - public CombatTracker(LivingEntity mob) { + public CombatTracker(final LivingEntity mob) { this.mob = mob; + this.paperCombatTracker = new io.papermc.paper.world.damagesource.PaperCombatTrackerWrapper(this); // Paper - Combat tracker API } - public void recordDamage(DamageSource source, float damage) { + public void recordDamage(final DamageSource source, final float damage) { this.recheckStatus(); - FallLocation currentFallLocation = FallLocation.getCurrentFallLocation(this.mob); - CombatEntry combatEntry = new CombatEntry(source, damage, currentFallLocation, (float)this.mob.fallDistance); + FallLocation fallLocation = FallLocation.getCurrentFallLocation(this.mob); + CombatEntry entry = new CombatEntry(source, damage, fallLocation, (float)this.mob.fallDistance); + // Paper start - Combat tracker API -+ recordDamageAndCheckCombatState(combatEntry); ++ recordDamageAndCheckCombatState(entry); + } + -+ public void recordDamageAndCheckCombatState(final CombatEntry combatEntry) { -+ final DamageSource source = combatEntry.source(); ++ public void recordDamageAndCheckCombatState(final CombatEntry entry) { ++ final DamageSource source = entry.source(); + // Paper end - Combat tracker API - this.entries.add(combatEntry); + this.entries.add(entry); this.lastDamageTime = this.mob.tickCount; this.takingDamage = true; -@@ -145,6 +_,13 @@ +@@ -147,6 +_,13 @@ public void recheckStatus() { - int i = this.inCombat ? 300 : 100; - if (this.takingDamage && (!this.mob.isAlive() || this.mob.tickCount - this.lastDamageTime > i)) { + int reset = this.inCombat ? 300 : 100; + if (this.takingDamage && (!this.mob.isAlive() || this.mob.tickCount - this.lastDamageTime > reset)) { + // Paper start - Combat tracker API + resetCombatState(); + } @@ -36,6 +36,6 @@ + + public void resetCombatState() {{ + // Paper end - Combat tracker API - boolean flag = this.inCombat; + boolean wasInCombat = this.inCombat; this.takingDamage = false; this.inCombat = false; diff --git a/paper-server/patches/sources/net/minecraft/world/effect/HealOrHarmMobEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/effect/HealOrHarmMobEffect.java.patch index 274a719765de..0a7679430e14 100644 --- a/paper-server/patches/sources/net/minecraft/world/effect/HealOrHarmMobEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/effect/HealOrHarmMobEffect.java.patch @@ -2,22 +2,22 @@ +++ b/net/minecraft/world/effect/HealOrHarmMobEffect.java @@ -16,7 +_,7 @@ @Override - public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) { - if (this.isHarm == entity.isInvertedHealAndHarm()) { -- entity.heal(Math.max(4 << amplifier, 0)); -+ entity.heal(Math.max(4 << amplifier, 0), org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.MAGIC); // CraftBukkit + public boolean applyEffectTick(final ServerLevel level, final LivingEntity mob, final int amplification) { + if (this.isHarm == mob.isInvertedHealAndHarm()) { +- mob.heal(Math.max(4 << amplification, 0)); ++ mob.heal(Math.max(4 << amplification, 0), org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.MAGIC); // CraftBukkit } else { - entity.hurtServer(level, entity.damageSources().magic(), 6 << amplifier); + mob.hurtServer(level, mob.damageSources().magic(), 6 << amplification); } -@@ -28,9 +_,10 @@ - public void applyInstantenousEffect( - ServerLevel level, @Nullable Entity source, @Nullable Entity indirectSource, LivingEntity entity, int amplifier, double health +@@ -33,9 +_,10 @@ + final int amplification, + final double scale ) { -+ if (!new io.papermc.paper.event.entity.EntityEffectTickEvent(entity.getBukkitLivingEntity(), org.bukkit.craftbukkit.potion.CraftPotionEffectType.minecraftToBukkit(this), amplifier).callEvent()) { return; } // Paper - Add EntityEffectTickEvent - if (this.isHarm == entity.isInvertedHealAndHarm()) { - int i = (int)(health * (4 << amplifier) + 0.5); -- entity.heal(i); -+ entity.heal(i, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.MAGIC); // CraftBukkit ++ if (!new io.papermc.paper.event.entity.EntityEffectTickEvent(mob.getBukkitLivingEntity(), org.bukkit.craftbukkit.potion.CraftPotionEffectType.minecraftToBukkit(this), amplification).callEvent()) { return; } // Paper - Add EntityEffectTickEvent + if (this.isHarm == mob.isInvertedHealAndHarm()) { + int amount = (int)(scale * (4 << amplification) + 0.5); +- mob.heal(amount); ++ mob.heal(amount, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.MAGIC); // CraftBukkit } else { - int i = (int)(health * (6 << amplifier) + 0.5); + int amount = (int)(scale * (6 << amplification) + 0.5); if (source == null) { diff --git a/paper-server/patches/sources/net/minecraft/world/effect/HungerMobEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/effect/HungerMobEffect.java.patch index 0328a5b344a3..c6284d81dd1b 100644 --- a/paper-server/patches/sources/net/minecraft/world/effect/HungerMobEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/effect/HungerMobEffect.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/effect/HungerMobEffect.java @@ -12,7 +_,7 @@ @Override - public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) { - if (entity instanceof Player player) { -- player.causeFoodExhaustion(0.005F * (amplifier + 1)); -+ player.causeFoodExhaustion(0.005F * (amplifier + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent + public boolean applyEffectTick(final ServerLevel serverLevel, final LivingEntity mob, final int amplification) { + if (mob instanceof Player player) { +- player.causeFoodExhaustion(0.005F * (amplification + 1)); ++ player.causeFoodExhaustion(0.005F * (amplification + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent } return true; diff --git a/paper-server/patches/sources/net/minecraft/world/effect/InfestedMobEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/effect/InfestedMobEffect.java.patch index b7ef4b139bfa..da1d18f2e2e3 100644 --- a/paper-server/patches/sources/net/minecraft/world/effect/InfestedMobEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/effect/InfestedMobEffect.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/effect/InfestedMobEffect.java +++ b/net/minecraft/world/effect/InfestedMobEffect.java @@ -44,7 +_,11 @@ - Vector3f vector3f = entity.getLookAngle().toVector3f().mul(0.3F).mul(1.0F, 1.5F, 1.0F).rotateY(f1); + Vector3f viewDirection = mob.getLookAngle().toVector3f().mul(0.3F).mul(1.0F, 1.5F, 1.0F).rotateY(randomAngle); silverfish.snapTo(x, y, z, level.getRandom().nextFloat() * 360.0F, 0.0F); - silverfish.setDeltaMovement(new Vec3(vector3f)); + silverfish.setDeltaMovement(new Vec3(viewDirection)); - level.addFreshEntity(silverfish); + // CraftBukkit start + if (!level.addFreshEntity(silverfish, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.POTION_EFFECT)) { diff --git a/paper-server/patches/sources/net/minecraft/world/effect/MobEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/effect/MobEffect.java.patch index 1d9da101bd4a..4d28b906bda9 100644 --- a/paper-server/patches/sources/net/minecraft/world/effect/MobEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/effect/MobEffect.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/world/effect/MobEffect.java +++ b/net/minecraft/world/effect/MobEffect.java -@@ -85,6 +_,7 @@ - public void applyInstantenousEffect( - ServerLevel level, @Nullable Entity source, @Nullable Entity indirectSource, LivingEntity entity, int amplifier, double health +@@ -90,6 +_,7 @@ + final int amplification, + final double scale ) { -+ if (!new io.papermc.paper.event.entity.EntityEffectTickEvent(entity.getBukkitLivingEntity(), org.bukkit.craftbukkit.potion.CraftPotionEffectType.minecraftToBukkit(this), amplifier).callEvent()) { return; } // Paper - Add EntityEffectTickEvent - this.applyEffectTick(level, entity, amplifier); ++ if (!new io.papermc.paper.event.entity.EntityEffectTickEvent(mob.getBukkitLivingEntity(), org.bukkit.craftbukkit.potion.CraftPotionEffectType.minecraftToBukkit(this), amplification).callEvent()) { return; } // Paper - Add EntityEffectTickEvent + this.applyEffectTick(level, mob, amplification); } diff --git a/paper-server/patches/sources/net/minecraft/world/effect/MobEffectInstance.java.patch b/paper-server/patches/sources/net/minecraft/world/effect/MobEffectInstance.java.patch index e7e66807e993..a037d0743c48 100644 --- a/paper-server/patches/sources/net/minecraft/world/effect/MobEffectInstance.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/effect/MobEffectInstance.java.patch @@ -1,15 +1,14 @@ --- a/net/minecraft/world/effect/MobEffectInstance.java +++ b/net/minecraft/world/effect/MobEffectInstance.java -@@ -218,7 +_,7 @@ - return false; +@@ -227,6 +_,7 @@ } else { - int i = this.isInfiniteDuration() ? entity.tickCount : this.duration; -- if (this.effect.value().shouldApplyEffectTickThisTick(i, this.amplifier) && !this.effect.value().applyEffectTick(level, entity, this.amplifier)) { -+ if (this.effect.value().shouldApplyEffectTickThisTick(i, this.amplifier) && new io.papermc.paper.event.entity.EntityEffectTickEvent(entity.getBukkitLivingEntity(), org.bukkit.craftbukkit.potion.CraftPotionEffectType.minecraftHolderToBukkit(this.effect), this.amplifier).callEvent() && !this.effect.value().applyEffectTick(level, entity, this.amplifier)) { // Paper - Add EntityEffectTickEvent + int tickCount = this.isInfiniteDuration() ? target.tickCount : this.duration; + if (this.effect.value().shouldApplyEffectTickThisTick(tickCount, this.amplifier) ++ && new io.papermc.paper.event.entity.EntityEffectTickEvent(target.getBukkitLivingEntity(), org.bukkit.craftbukkit.potion.CraftPotionEffectType.minecraftHolderToBukkit(this.effect), this.amplifier).callEvent() // Paper - Add EntityEffectTickEvent + && !this.effect.value().applyEffectTick(serverLevel, target, this.amplifier)) { return false; } else { - this.tickDownDuration(); -@@ -253,13 +_,16 @@ +@@ -262,13 +_,16 @@ } private boolean downgradeToHiddenEffect() { @@ -30,22 +29,22 @@ + // Paper end - Fix MC-259832 } - public void onEffectStarted(LivingEntity entity) { -@@ -414,7 +_,7 @@ - .apply(instance, MobEffectInstance.Details::create) + public void onEffectStarted(final LivingEntity mob) { +@@ -425,7 +_,7 @@ + .apply(i, MobEffectInstance.Details::create) ) ); - public static final StreamCodec STREAM_CODEC = StreamCodec.recursive( + public static final StreamCodec STREAM_CODEC = StreamCodec.recursive( // Paper - Track codec depth - codec -> StreamCodec.composite( + subCodec -> StreamCodec.composite( ByteBufCodecs.VAR_INT, MobEffectInstance.Details::amplifier, -@@ -426,7 +_,7 @@ +@@ -437,7 +_,7 @@ MobEffectInstance.Details::showParticles, ByteBufCodecs.BOOL, MobEffectInstance.Details::showIcon, -- codec.apply(ByteBufCodecs::optional), -+ codec.apply(ByteBufCodecs::increaseDepth).apply(ByteBufCodecs::optional), // Paper - Track codec depth +- subCodec.apply(ByteBufCodecs::optional), ++ subCodec.apply(ByteBufCodecs::increaseDepth).apply(ByteBufCodecs::optional), // Paper - Track codec depth MobEffectInstance.Details::hiddenEffect, MobEffectInstance.Details::new ) diff --git a/paper-server/patches/sources/net/minecraft/world/effect/MobEffectUtil.java.patch b/paper-server/patches/sources/net/minecraft/world/effect/MobEffectUtil.java.patch index 54b2f1ba668e..40a12687af8b 100644 --- a/paper-server/patches/sources/net/minecraft/world/effect/MobEffectUtil.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/effect/MobEffectUtil.java.patch @@ -1,48 +1,62 @@ --- a/net/minecraft/world/effect/MobEffectUtil.java +++ b/net/minecraft/world/effect/MobEffectUtil.java -@@ -55,18 +_,38 @@ - public static List addEffectToPlayersAround( - ServerLevel level, @Nullable Entity source, Vec3 pos, double radius, MobEffectInstance effect, int duration +@@ -56,18 +_,56 @@ + final MobEffectInstance effectInstance, + final int displayEffectLimit ) { + // CraftBukkit start -+ return MobEffectUtil.addEffectToPlayersAround(level, source, pos, radius, effect, duration, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); ++ return MobEffectUtil.addEffectToPlayersAround(level, source, position, radius, effectInstance, displayEffectLimit, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); + } + -+ public static List addEffectToPlayersAround(ServerLevel level, @Nullable Entity source, Vec3 pos, double radius, MobEffectInstance effect, int duration, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) { ++ public static List addEffectToPlayersAround( ++ final ServerLevel level, ++ final @Nullable Entity source, ++ final Vec3 position, ++ final double radius, ++ final MobEffectInstance effectInstance, ++ final int displayEffectLimit, ++ final org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause ++ ) { + // Paper start - Add ElderGuardianAppearanceEvent -+ return addEffectToPlayersAround(level, source, pos, radius, effect, duration, cause, null); ++ return addEffectToPlayersAround(level, source, position, radius, effectInstance, displayEffectLimit, cause, null); + } + -+ public static List addEffectToPlayersAround(ServerLevel level, @Nullable Entity source, Vec3 pos, double radius, MobEffectInstance effect, int duration, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause, java.util.function.@Nullable Predicate playerPredicate) { ++ public static List addEffectToPlayersAround( ++ final ServerLevel level, ++ final @Nullable Entity source, ++ final Vec3 position, ++ final double radius, ++ final MobEffectInstance effectInstance, ++ final int displayEffectLimit, ++ final org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause, ++ final java.util.function.@Nullable Predicate playerPredicate ++ ) { + // Paper end - Add ElderGuardianAppearanceEvent + // CraftBukkit end - Holder effect1 = effect.getEffect(); + Holder effect = effectInstance.getEffect(); List players = level.getPlayers( -- serverPlayer -> serverPlayer.gameMode.isSurvival() +- input -> input.gameMode.isSurvival() + // Paper start - Add ElderGuardianAppearanceEvent -+ serverPlayer -> { -+ final boolean condition = serverPlayer.gameMode.isSurvival() - && (source == null || !source.isAlliedTo(serverPlayer)) - && pos.closerThan(serverPlayer.position(), radius) ++ input -> { ++ final boolean condition = input.gameMode.isSurvival() + && (source == null || !source.isAlliedTo(input)) + && position.closerThan(input.position(), radius) && ( -- !serverPlayer.hasEffect(effect1) -- || serverPlayer.getEffect(effect1).getAmplifier() < effect.getAmplifier() -- || serverPlayer.getEffect(effect1).endsWithin(duration - 1) + !input.hasEffect(effect) + || input.getEffect(effect).getAmplifier() < effectInstance.getAmplifier() + || input.getEffect(effect).endsWithin(displayEffectLimit - 1) - ) -- ); -- players.forEach(serverPlayer -> serverPlayer.addEffect(new MobEffectInstance(effect), source)); -+ !serverPlayer.hasEffect(effect1) -+ || serverPlayer.getEffect(effect1).getAmplifier() < effect.getAmplifier() -+ || serverPlayer.getEffect(effect1).endsWithin(duration - 1) -+ ); -+ if (condition) { -+ return playerPredicate == null || playerPredicate.test(serverPlayer); // Only test the player AFTER it is true -+ } else { -+ return false; ++ ); ++ if (condition) { ++ return playerPredicate == null || playerPredicate.test(input); // Only test the player AFTER it is true ++ } else { ++ return false; ++ } + } -+ }); + ); +- players.forEach(player -> player.addEffect(new MobEffectInstance(effectInstance), source)); + // Paper end - Add ElderGuardianAppearanceEvent -+ players.forEach(serverPlayer -> serverPlayer.addEffect(new MobEffectInstance(effect), source, cause)); // CraftBukkit ++ players.forEach(player -> player.addEffect(new MobEffectInstance(effectInstance), source, cause)); // CraftBukkit return players; } } diff --git a/paper-server/patches/sources/net/minecraft/world/effect/PoisonMobEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/effect/PoisonMobEffect.java.patch index 756bcf1d8893..23e24bdf6df0 100644 --- a/paper-server/patches/sources/net/minecraft/world/effect/PoisonMobEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/effect/PoisonMobEffect.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/effect/PoisonMobEffect.java @@ -13,7 +_,7 @@ @Override - public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) { - if (entity.getHealth() > 1.0F) { -- entity.hurtServer(level, entity.damageSources().magic(), 1.0F); -+ entity.hurtServer(level, entity.damageSources().magic().knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.POISON), 1.0F); // CraftBukkit + public boolean applyEffectTick(final ServerLevel level, final LivingEntity mob, final int amplification) { + if (mob.getHealth() > 1.0F) { +- mob.hurtServer(level, mob.damageSources().magic(), 1.0F); ++ mob.hurtServer(level, mob.damageSources().magic().knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.POISON), 1.0F); // CraftBukkit } return true; diff --git a/paper-server/patches/sources/net/minecraft/world/effect/RegenerationMobEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/effect/RegenerationMobEffect.java.patch index c7b4805bbb37..a85c115a311b 100644 --- a/paper-server/patches/sources/net/minecraft/world/effect/RegenerationMobEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/effect/RegenerationMobEffect.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/effect/RegenerationMobEffect.java @@ -11,7 +_,7 @@ @Override - public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) { - if (entity.getHealth() < entity.getMaxHealth()) { -- entity.heal(1.0F); -+ entity.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.MAGIC_REGEN); // CraftBukkit + public boolean applyEffectTick(final ServerLevel level, final LivingEntity mob, final int amplification) { + if (mob.getHealth() < mob.getMaxHealth()) { +- mob.heal(1.0F); ++ mob.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.MAGIC_REGEN); // CraftBukkit } return true; diff --git a/paper-server/patches/sources/net/minecraft/world/effect/SaturationMobEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/effect/SaturationMobEffect.java.patch index d9dff0017600..040d7f476b31 100644 --- a/paper-server/patches/sources/net/minecraft/world/effect/SaturationMobEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/effect/SaturationMobEffect.java.patch @@ -2,12 +2,12 @@ +++ b/net/minecraft/world/effect/SaturationMobEffect.java @@ -12,7 +_,15 @@ @Override - public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) { - if (entity instanceof Player player) { -- player.getFoodData().eat(amplifier + 1, 1.0F); + public boolean applyEffectTick(final ServerLevel level, final LivingEntity mob, final int amplification) { + if (mob instanceof Player player) { +- player.getFoodData().eat(amplification + 1, 1.0F); + // CraftBukkit start + int oldFoodLevel = player.getFoodData().foodLevel; -+ org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(player, amplifier + 1 + oldFoodLevel); ++ org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(player, amplification + 1 + oldFoodLevel); + if (!event.isCancelled()) { + player.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 1.0F); + } diff --git a/paper-server/patches/sources/net/minecraft/world/effect/WeavingMobEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/effect/WeavingMobEffect.java.patch index 3e0ba111f71b..711b4be6fc30 100644 --- a/paper-server/patches/sources/net/minecraft/world/effect/WeavingMobEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/effect/WeavingMobEffect.java.patch @@ -2,22 +2,22 @@ +++ b/net/minecraft/world/effect/WeavingMobEffect.java @@ -27,11 +_,11 @@ @Override - public void onMobRemoved(ServerLevel level, LivingEntity entity, int amplifier, Entity.RemovalReason reason) { - if (reason == Entity.RemovalReason.KILLED && (entity instanceof Player || level.getGameRules().get(GameRules.MOB_GRIEFING))) { -- this.spawnCobwebsRandomlyAround(level, entity.getRandom(), entity.blockPosition()); -+ this.spawnCobwebsRandomlyAround(level, entity.getRandom(), entity.blockPosition(), entity); // Paper - Fire EntityChangeBlockEvent in more places + public void onMobRemoved(final ServerLevel level, final LivingEntity mob, final int amplifier, final Entity.RemovalReason reason) { + if (reason == Entity.RemovalReason.KILLED && (mob instanceof Player || level.getGameRules().get(GameRules.MOB_GRIEFING))) { +- this.spawnCobwebsRandomlyAround(level, mob.getRandom(), mob.blockPosition()); ++ this.spawnCobwebsRandomlyAround(level, mob.getRandom(), mob.blockPosition(), mob); // Paper - Fire EntityChangeBlockEvent in more places } } -- private void spawnCobwebsRandomlyAround(ServerLevel level, RandomSource random, BlockPos pos) { -+ private void spawnCobwebsRandomlyAround(ServerLevel level, RandomSource random, BlockPos pos, LivingEntity entity) { // Paper - Fire EntityChangeBlockEvent in more places - Set set = Sets.newHashSet(); - int i = this.maxCobwebs.applyAsInt(random); +- private void spawnCobwebsRandomlyAround(final ServerLevel level, final RandomSource random, final BlockPos pos) { ++ private void spawnCobwebsRandomlyAround(final ServerLevel level, final RandomSource random, final BlockPos pos, LivingEntity entity) { // Paper - Fire EntityChangeBlockEvent in more places + Set positionsToTransform = Sets.newHashSet(); + int cobwebCount = this.maxCobwebs.applyAsInt(random); @@ -48,6 +_,7 @@ } - for (BlockPos blockPosx : set) { + for (BlockPos blockPosx : positionsToTransform) { + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, blockPosx, Blocks.COBWEB.defaultBlockState())) continue; // Paper - Fire EntityChangeBlockEvent in more places level.setBlock(blockPosx, Blocks.COBWEB.defaultBlockState(), Block.UPDATE_ALL); level.levelEvent(LevelEvent.ANIMATION_SPAWN_COBWEB, blockPosx, 0); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/AgeableMob.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/AgeableMob.java.patch deleted file mode 100644 index 6c83aa84c19b..000000000000 --- a/paper-server/patches/sources/net/minecraft/world/entity/AgeableMob.java.patch +++ /dev/null @@ -1,43 +0,0 @@ ---- a/net/minecraft/world/entity/AgeableMob.java -+++ b/net/minecraft/world/entity/AgeableMob.java -@@ -23,6 +_,7 @@ - protected int age = 0; - protected int forcedAge = 0; - protected int forcedAgeTimer; -+ public boolean ageLocked; // CraftBukkit - - protected AgeableMob(EntityType type, Level level) { - super(type, level); -@@ -68,6 +_,7 @@ - } - - public void ageUp(int amount, boolean forced) { -+ if (this.ageLocked) return; // Paper - Honor ageLock - int age = this.getAge(); - int previousAge = age; - age += amount * 20; -@@ -107,6 +_,7 @@ - super.addAdditionalSaveData(output); - output.putInt("Age", this.getAge()); - output.putInt("ForcedAge", this.forcedAge); -+ output.putBoolean("AgeLocked", this.ageLocked); // CraftBukkit - } - - @Override -@@ -114,6 +_,7 @@ - super.readAdditionalSaveData(input); - this.setAge(input.getIntOr("Age", 0)); - this.forcedAge = input.getIntOr("ForcedAge", 0); -+ this.ageLocked = input.getBooleanOr("AgeLocked", false); // CraftBukkit - } - - @Override -@@ -136,7 +_,7 @@ - - this.forcedAgeTimer--; - } -- } else if (this.isAlive()) { -+ } else if (!this.ageLocked && this.isAlive()) { // CraftBukkit - int age = this.getAge(); - if (age < 0) { - this.setAge(++age); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/AreaEffectCloud.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/AreaEffectCloud.java.patch index 0acae96c2e8e..6ebd8b3cadb0 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/AreaEffectCloud.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/AreaEffectCloud.java.patch @@ -2,13 +2,13 @@ +++ b/net/minecraft/world/entity/AreaEffectCloud.java @@ -189,7 +_,7 @@ - private void serverTick(ServerLevel level) { + private void serverTick(final ServerLevel serverLevel) { if (this.duration != -1 && this.tickCount - this.waitTime >= this.duration) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause } else { boolean isWaiting = this.isWaiting(); - boolean flag = this.tickCount < this.waitTime; + boolean shouldWait = this.tickCount < this.waitTime; @@ -202,7 +_,7 @@ if (this.radiusPerTick != 0.0F) { radius += this.radiusPerTick; @@ -18,38 +18,37 @@ return; } -@@ -218,6 +_,7 @@ - this.potionContents.forEachEffect(list::add, this.potionDurationScale); - List entitiesOfClass = this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox()); - if (!entitiesOfClass.isEmpty()) { -+ List entities = new java.util.ArrayList<>(); // CraftBukkit - for (LivingEntity livingEntity : entitiesOfClass) { - if (!this.victims.containsKey(livingEntity) - && livingEntity.isAffectedByPotions() -@@ -226,6 +_,17 @@ - double d1 = livingEntity.getZ() - this.getZ(); - double d2 = d * d + d1 * d1; - if (d2 <= radius * radius) { +@@ -218,12 +_,24 @@ + this.potionContents.forEachEffect(allEffects::add, this.potionDurationScale); + List entities = this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox()); + if (!entities.isEmpty()) { ++ List bukkitEntities = new java.util.ArrayList<>(); // CraftBukkit + for (LivingEntity entity : entities) { + if (!this.victims.containsKey(entity) && entity.isAffectedByPotions() && !allEffects.stream().noneMatch(entity::canBeAffected)) { + double xd = entity.getX() - this.getX(); + double zd = entity.getZ() - this.getZ(); + double dist = xd * xd + zd * zd; + if (dist <= radius * radius) { + // CraftBukkit start -+ entities.add((org.bukkit.entity.LivingEntity) livingEntity.getBukkitEntity()); ++ bukkitEntities.add((org.bukkit.entity.LivingEntity) entity.getBukkitEntity()); + } + } + } -+ org.bukkit.event.entity.AreaEffectCloudApplyEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callAreaEffectCloudApplyEvent(this, entities); ++ org.bukkit.event.entity.AreaEffectCloudApplyEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callAreaEffectCloudApplyEvent(this, bukkitEntities); + if (!event.isCancelled()) { -+ for (org.bukkit.entity.LivingEntity entity : event.getAffectedEntities()) { -+ if (entity instanceof org.bukkit.craftbukkit.entity.CraftLivingEntity) { -+ net.minecraft.world.entity.LivingEntity livingEntity = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) entity).getHandle(); ++ for (org.bukkit.entity.LivingEntity bukkitLivingEntity : event.getAffectedEntities()) { ++ if (bukkitLivingEntity instanceof org.bukkit.craftbukkit.entity.CraftLivingEntity craftLivingEntity) { ++ LivingEntity entity = craftLivingEntity.getHandle(); + // CraftBukkit end - this.victims.put(livingEntity, this.tickCount + this.reapplicationDelay); + this.victims.put(entity, this.tickCount + this.reapplicationDelay); - for (MobEffectInstance mobEffectInstance : list) { -@@ -234,14 +_,14 @@ + for (MobEffectInstance effect : allEffects) { +@@ -232,14 +_,14 @@ .value() - .applyInstantenousEffect(level, this, this.getOwner(), livingEntity, mobEffectInstance.getAmplifier(), 0.5); + .applyInstantenousEffect(serverLevel, this, this.getOwner(), entity, effect.getAmplifier(), 0.5); } else { -- livingEntity.addEffect(new MobEffectInstance(mobEffectInstance), this); -+ livingEntity.addEffect(new MobEffectInstance(mobEffectInstance), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.AREA_EFFECT_CLOUD); // CraftBukkit +- entity.addEffect(new MobEffectInstance(effect), this); ++ entity.addEffect(new MobEffectInstance(effect), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.AREA_EFFECT_CLOUD); // CraftBukkit } } @@ -61,7 +60,7 @@ return; } -@@ -251,7 +_,7 @@ +@@ -249,7 +_,7 @@ if (this.durationOnUse != 0 && this.duration != -1) { this.duration = this.duration + this.durationOnUse; if (this.duration <= 0) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ConversionType.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ConversionType.java.patch index 479522aad45a..162fc9103da5 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ConversionType.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ConversionType.java.patch @@ -2,18 +2,18 @@ +++ b/net/minecraft/world/entity/ConversionType.java @@ -24,7 +_,7 @@ - for (Entity entity : newMob.getPassengers()) { - entity.stopRiding(); -- entity.remove(Entity.RemovalReason.DISCARDED); -+ entity.remove(Entity.RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.TRANSFORMATION); // CraftBukkit - add Bukkit remove cause + for (Entity passenger : to.getPassengers()) { + passenger.stopRiding(); +- passenger.remove(Entity.RemovalReason.DISCARDED); ++ passenger.remove(Entity.RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.TRANSFORMATION); // CraftBukkit - add Bukkit remove cause } - firstPassenger.startRiding(newMob); + rootPassenger.startRiding(to); @@ -73,6 +_,7 @@ if (leashHolder != null) { - oldMob.dropLeash(); + from.dropLeash(); } -+ newMob.aware = oldMob.aware; // Paper - Fix nerfed slime when splitting ++ to.aware = from.aware; // Paper - Fix nerfed slime when splitting - this.convertCommon(oldMob, newMob, conversionParams); + this.convertCommon(from, to, params); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch index 846f332e554c..e385d875cd33 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -153,6 +_,105 @@ - import org.slf4j.Logger; - - public abstract class Entity implements SyncedDataHolder, DebugValueSource, Nameable, ItemOwner, SlotProvider, EntityAccess, ScoreHolder, DataComponentGetter { +@@ -168,6 +_,105 @@ + SlotProvider, + DebugValueSource, + TypedInstance> { + // CraftBukkit start + private static final int CURRENT_LEVEL = 2; + static boolean isLevelAtLeast(ValueInput input, int level) { @@ -106,7 +106,7 @@ private static final Logger LOGGER = LogUtils.getLogger(); public static final String TAG_ID = "id"; public static final String TAG_UUID = "UUID"; -@@ -228,7 +_,7 @@ +@@ -243,7 +_,7 @@ public double yOld; public double zOld; public boolean noPhysics; @@ -114,8 +114,8 @@ + public final RandomSource random = SHARED_RANDOM; // Paper - Share random for entities to make them more random public int tickCount; private int remainingFireTicks; - public boolean wasTouchingWater; -@@ -264,7 +_,7 @@ + private final EntityFluidInteraction fluidInteraction = new EntityFluidInteraction(Set.of(FluidTags.WATER, FluidTags.LAVA)); +@@ -278,7 +_,7 @@ protected UUID uuid = Mth.createInsecureUUID(this.random); protected String stringUUID = this.uuid.toString(); private boolean hasGlowingTag; @@ -124,7 +124,7 @@ private final double[] pistonDeltas = new double[]{0.0, 0.0, 0.0}; private long pistonDeltasGameTime; private EntityDimensions dimensions; -@@ -278,6 +_,7 @@ +@@ -292,6 +_,7 @@ public boolean hasVisualFire; private Vec3 lastKnownSpeed = Vec3.ZERO; private @Nullable Vec3 lastKnownPosition; @@ -132,7 +132,7 @@ private @Nullable BlockState inBlockState = null; public static final int MAX_MOVEMENTS_HANDELED_PER_TICK = 100; private final ArrayDeque movementThisTick = new ArrayDeque<>(100); -@@ -285,6 +_,40 @@ +@@ -299,6 +_,40 @@ private final LongSet visitedBlocks = new LongOpenHashSet(); private final InsideBlockEffectApplier.StepBasedCollector insideEffectCollector = new InsideBlockEffectApplier.StepBasedCollector(); private CustomData customData = CustomData.EMPTY; @@ -171,17 +171,17 @@ + } + // Paper end - public Entity(EntityType type, Level level) { + public Entity(final EntityType type, final Level level) { this.type = type; -@@ -306,6 +_,7 @@ - this.entityData = builder.build(); +@@ -320,6 +_,7 @@ + this.entityData = entityDataBuilder.build(); this.setPos(0.0, 0.0, 0.0); this.eyeHeight = this.dimensions.eyeHeight(); + this.despawnTime = level == null || type == EntityType.PLAYER ? -1 : level.paperConfig().entities.spawning.despawnTime.getOrDefault(type, io.papermc.paper.configuration.type.number.IntOr.Disabled.DISABLED).or(-1); // Paper - entity despawn time limit } - public boolean isColliding(BlockPos pos, BlockState state) { -@@ -318,6 +_,12 @@ + public boolean isColliding(final BlockPos pos, final BlockState state) { +@@ -332,6 +_,12 @@ return team != null && team.getColor().getColor() != null ? team.getColor().getColor() : 16777215; } @@ -194,19 +194,19 @@ public boolean isSpectator() { return false; } -@@ -370,7 +_,7 @@ +@@ -389,7 +_,7 @@ } - public boolean addTag(String tag) { + public boolean addTag(final String tag) { - return this.tags.size() < 1024 && this.tags.add(tag); + return this.tags.add(tag); // Paper - fully limit tag size - replace set impl } - public boolean removeTag(String tag) { -@@ -378,12 +_,18 @@ + public boolean removeTag(final String tag) { +@@ -397,12 +_,18 @@ } - public void kill(ServerLevel level) { + public void kill(final ServerLevel level) { - this.remove(Entity.RemovalReason.KILLED); + this.remove(Entity.RemovalReason.KILLED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause this.gameEvent(GameEvent.ENTITY_DIE); @@ -223,47 +223,47 @@ + // CraftBukkit end } - protected abstract void defineSynchedData(SynchedEntityData.Builder builder); -@@ -392,6 +_,48 @@ + protected abstract void defineSynchedData(SynchedEntityData.Builder entityData); +@@ -411,6 +_,48 @@ return this.entityData; } + // CraftBukkit start + public void refreshEntityData(ServerPlayer to) { -+ List> list = this.entityData.packAll(); // Paper - Update EVERYTHING not just not default ++ List> values = this.entityData.packAll(); // Paper - Update EVERYTHING not just not default + + if (to.getBukkitEntity().canSee(this.getBukkitEntity())) { // Paper -+ to.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket(this.getId(), list)); ++ to.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket(this.getId(), values)); + } + } + // CraftBukkit end + // Paper start + // This method should only be used if the data of an entity could have become desynced + // due to interactions on the client. -+ public void resendPossiblyDesyncedEntityData(net.minecraft.server.level.ServerPlayer player) { ++ public void resendPossiblyDesyncedEntityData(ServerPlayer player) { + if (player.getBukkitEntity().canSee(this.getBukkitEntity())) { -+ ServerLevel world = (net.minecraft.server.level.ServerLevel)this.level(); -+ net.minecraft.server.level.ChunkMap.TrackedEntity tracker = world == null ? null : world.getChunkSource().chunkMap.entityMap.get(this.getId()); ++ ServerLevel level = (net.minecraft.server.level.ServerLevel) this.level(); ++ net.minecraft.server.level.ChunkMap.TrackedEntity tracker = level == null ? null : level.getChunkSource().chunkMap.entityMap.get(this.getId()); + if (tracker == null) { + return; + } -+ final net.minecraft.server.level.ServerEntity serverEntity = tracker.serverEntity; -+ final List> list = new java.util.ArrayList<>(); ++ final ServerEntity serverEntity = tracker.serverEntity; ++ final List> list = new ArrayList<>(); + serverEntity.sendPairingData(player, list::add); + player.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(list)); + } + } + -+ // This method allows you to specifically resend certain data accessor keys to the client -+ public void resendPossiblyDesyncedDataValues(List> keys, ServerPlayer to) { ++ // This method allows you to specifically resend certain data accessors to the client ++ public void resendPossiblyDesyncedDataValues(List> accessors, ServerPlayer to) { + if (!to.getBukkitEntity().canSee(this.getBukkitEntity())) { + return; + } + -+ final List> values = new java.util.ArrayList<>(keys.size()); -+ for (final EntityDataAccessor key : keys) { -+ final SynchedEntityData.DataItem synchedValue = this.entityData.getItem(key); -+ values.add(synchedValue.value()); ++ final List> values = new ArrayList<>(accessors.size()); ++ for (final EntityDataAccessor accessor : accessors) { ++ final SynchedEntityData.DataItem item = this.entityData.getItem(accessor); ++ values.add(item.value()); + } + + to.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket(this.id, values)); @@ -271,27 +271,27 @@ + // Paper end + @Override - public boolean equals(Object other) { - return other instanceof Entity && ((Entity)other).id == this.id; -@@ -403,7 +_,13 @@ + public boolean equals(final Object obj) { + return obj instanceof Entity && ((Entity)obj).id == this.id; +@@ -422,7 +_,13 @@ } - public void remove(Entity.RemovalReason reason) { + public void remove(final Entity.RemovalReason reason) { - this.setRemoved(reason); + // CraftBukkit start - add Bukkit remove cause + this.remove(reason, null); + } + -+ public void remove(Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause eventCause) { ++ public void remove(final Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause eventCause) { + this.setRemoved(reason, eventCause); + // CraftBukkit end } public void onClientRemoval() { -@@ -413,6 +_,15 @@ +@@ -432,6 +_,15 @@ } - public void setPose(Pose pose) { + public void setPose(final Pose pose) { + if (this.fixedPose) return; // Paper - Expand Pose API + // CraftBukkit start + if (pose == this.getPose()) { @@ -304,11 +304,13 @@ this.entityData.set(DATA_POSE, pose); } -@@ -436,6 +_,32 @@ +@@ -454,7 +_,33 @@ + return Mth.lengthSquared(dx, dz) < Mth.square(distanceXZ) && Mth.square(dy) < Mth.square(distanceY); } - public void setRot(float yRot, float xRot) { -+ // CraftBukkit start - yaw was sometimes set to NaN, so we need to set it back to 0 +- public void setRot(final float yRot, final float xRot) { ++ // CraftBukkit start - yaw was sometimes set to NaN, so we need to set it back to 0 ++ public void setRot(float yRot, float xRot) { + if (Float.isNaN(yRot)) { + yRot = 0; + } @@ -337,10 +339,10 @@ this.setYRot(yRot % 360.0F); this.setXRot(xRot % 360.0F); } -@@ -445,8 +_,8 @@ +@@ -464,8 +_,8 @@ } - public void setPos(double x, double y, double z) { + public void setPos(final double x, final double y, final double z) { - this.setPosRaw(x, y, z); - this.setBoundingBox(this.makeBoundingBox()); + this.setPosRaw(x, y, z, true); // Paper - Block invalid positions and bounding box; force update @@ -348,7 +350,7 @@ } protected final AABB makeBoundingBox() { -@@ -480,12 +_,28 @@ +@@ -499,12 +_,28 @@ } public void tick() { @@ -371,13 +373,13 @@ + // CraftBukkit end + public void baseTick() { - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("entityBaseTick"); -+ if (firstTick && this instanceof net.minecraft.world.entity.NeutralMob neutralMob) neutralMob.tickInitialPersistentAnger(level); // Paper - Prevent entity loading causing async lookups + ProfilerFiller profiler = Profiler.get(); + profiler.push("entityBaseTick"); ++ if (this.firstTick && this instanceof net.minecraft.world.entity.NeutralMob neutralMob) neutralMob.tickInitialPersistentAnger(this.level); // Paper - Prevent entity loading causing async lookups this.computeSpeed(); this.inBlockState = null; if (this.isPassenger() && this.getVehicle().isRemoved()) { -@@ -496,7 +_,7 @@ +@@ -515,7 +_,7 @@ this.boardingCooldown--; } @@ -386,7 +388,7 @@ if (this.canSpawnSprintParticle()) { this.spawnSprintParticle(); } -@@ -524,6 +_,10 @@ +@@ -543,6 +_,10 @@ if (this.isInLava()) { this.fallDistance *= 0.5; @@ -397,12 +399,12 @@ } this.checkBelowWorld(); -@@ -549,11 +_,16 @@ +@@ -568,11 +_,16 @@ } - public void setSharedFlagOnFire(boolean isOnFire) { -- this.setSharedFlag(FLAG_ONFIRE, isOnFire || this.hasVisualFire); -+ this.setSharedFlag(FLAG_ONFIRE, this.visualFire.toBooleanOrElse(isOnFire)); // Paper - improve visual fire API + public void setSharedFlagOnFire(final boolean value) { +- this.setSharedFlag(FLAG_ONFIRE, value || this.hasVisualFire); ++ this.setSharedFlag(FLAG_ONFIRE, this.visualFire.toBooleanOrElse(value)); // Paper - improve visual fire API } public void checkBelowWorld() { @@ -416,7 +418,7 @@ this.onBelowWorld(); } } -@@ -581,15 +_,41 @@ +@@ -600,15 +_,41 @@ } public void lavaIgnite() { @@ -460,28 +462,28 @@ && this.shouldPlayLavaHurtSound() && !this.isSilent()) { serverLevel.playSound( -@@ -604,6 +_,20 @@ +@@ -623,6 +_,20 @@ } - public final void igniteForSeconds(float seconds) { + public final void igniteForSeconds(final float numberOfSeconds) { + // CraftBukkit start -+ this.igniteForSeconds(seconds, true); ++ this.igniteForSeconds(numberOfSeconds, true); + } + -+ public final void igniteForSeconds(float seconds, boolean callEvent) { ++ public final void igniteForSeconds(float numberOfSeconds, final boolean callEvent) { + if (callEvent) { -+ org.bukkit.event.entity.EntityCombustEvent event = new org.bukkit.event.entity.EntityCombustEvent(this.getBukkitEntity(), seconds); ++ org.bukkit.event.entity.EntityCombustEvent event = new org.bukkit.event.entity.EntityCombustEvent(this.getBukkitEntity(), numberOfSeconds); + if (!event.callEvent()) { + return; + } + -+ seconds = event.getDuration(); ++ numberOfSeconds = event.getDuration(); + } + // CraftBukkit end - this.igniteForTicks(Mth.floor(seconds * 20.0F)); + this.igniteForTicks(Mth.floor(numberOfSeconds * 20.0F)); } -@@ -628,7 +_,7 @@ +@@ -647,7 +_,7 @@ } protected void onBelowWorld() { @@ -489,8 +491,8 @@ + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.OUT_OF_WORLD); // CraftBukkit - add Bukkit remove cause } - public boolean isFree(double x, double y, double z) { -@@ -684,7 +_,28 @@ + public boolean isFree(final double xa, final double ya, final double za) { +@@ -703,7 +_,28 @@ return this.onGround; } @@ -504,23 +506,23 @@ + private double moveStartZ; + // Paper end - detailed watchdog information + - public void move(MoverType type, Vec3 movement) { -+ final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity + public void move(final MoverType moverType, Vec3 delta) { ++ final Vec3 originalMovement = delta; // Paper - Expose pre-collision velocity + // Paper start - detailed watchdog information + ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main"); + synchronized (this.posLock) { + this.moveStartX = this.getX(); + this.moveStartY = this.getY(); + this.moveStartZ = this.getZ(); -+ this.moveVector = movement; ++ this.moveVector = delta; + } + try { + // Paper end - detailed watchdog information if (this.noPhysics) { - this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); + this.setPos(this.getX() + delta.x, this.getY() + delta.y, this.getZ() + delta.z); this.horizontalCollision = false; -@@ -767,6 +_,27 @@ - block.updateEntityMovementAfterFallOn(this.level(), this); +@@ -786,6 +_,27 @@ + onBlock.updateEntityMovementAfterFallOn(this.level(), this); } } + // CraftBukkit start @@ -528,13 +530,13 @@ + org.bukkit.entity.Vehicle vehicle = (org.bukkit.entity.Vehicle) this.getBukkitEntity(); + org.bukkit.block.Block block = this.level.getWorld().getBlockAt(Mth.floor(this.getX()), Mth.floor(this.getY()), Mth.floor(this.getZ())); + -+ if (movement.x > vec3.x) { ++ if (delta.x > movement.x) { + block = block.getRelative(org.bukkit.block.BlockFace.EAST); -+ } else if (movement.x < vec3.x) { ++ } else if (delta.x < movement.x) { + block = block.getRelative(org.bukkit.block.BlockFace.WEST); -+ } else if (movement.z > vec3.z) { ++ } else if (delta.z > movement.z) { + block = block.getRelative(org.bukkit.block.BlockFace.SOUTH); -+ } else if (movement.z < vec3.z) { ++ } else if (delta.z < movement.z) { + block = block.getRelative(org.bukkit.block.BlockFace.NORTH); + } + @@ -546,9 +548,9 @@ + // CraftBukkit end if (!this.level().isClientSide() || this.isLocalInstanceAuthoritative()) { - Entity.MovementEmission movementEmission = this.getMovementEmission(); -@@ -780,6 +_,13 @@ - profilerFiller.pop(); + Entity.MovementEmission emission = this.getMovementEmission(); +@@ -799,6 +_,13 @@ + profiler.pop(); } } + // Paper start - detailed watchdog information @@ -560,51 +562,51 @@ + // Paper end - detailed watchdog information } - private void applyMovementEmissionAndPlaySound(Entity.MovementEmission movementEmission, Vec3 movement, BlockPos pos, BlockState state) { -@@ -965,7 +_,7 @@ + private void applyMovementEmissionAndPlaySound( +@@ -994,7 +_,7 @@ } - protected BlockPos getOnPos(float yOffset) { + protected BlockPos getOnPos(final float offset) { - if (this.mainSupportingBlockPos.isPresent()) { + if (this.mainSupportingBlockPos.isPresent() && this.level().getChunkIfLoadedImmediately(this.mainSupportingBlockPos.get()) != null) { // Paper - ensure no loads - BlockPos blockPos = this.mainSupportingBlockPos.get(); - if (!(yOffset > 1.0E-5F)) { - return blockPos; -@@ -1220,7 +_,7 @@ - if (flag2) { + BlockPos getOnPos = this.mainSupportingBlockPos.get(); + if (!(offset > 1.0E-5F)) { + return getOnPos; +@@ -1263,7 +_,7 @@ + if (insideBlock) { try { - boolean flag4 = flag || aabb.intersects(pos); -- stepBasedCollector.advanceStep(index); -+ stepBasedCollector.advanceStep(index, pos); // Paper - track position inside effect was triggered on - blockState.entityInside(this.level(), pos, this, stepBasedCollector, flag4); - this.onInsideBlock(blockState); + boolean isPrecise = movedFar || deflatedBoundingBoxAtTarget.intersects(blockIntersection); +- effectCollector.advanceStep(iteration); ++ effectCollector.advanceStep(iteration, blockIntersection); // Paper - track position inside effect was triggered on + state.entityInside(this.level(), blockIntersection, this, effectCollector, isPrecise); + this.onInsideBlock(state); } catch (Throwable var20) { -@@ -1234,7 +_,7 @@ +@@ -1277,7 +_,7 @@ } - if (flag3) { -- stepBasedCollector.advanceStep(index); -+ stepBasedCollector.advanceStep(index, pos); // Paper - track position inside effect was triggered on - blockState.getFluidState().entityInside(this.level(), pos, this, stepBasedCollector); + if (insideFluid) { +- effectCollector.advanceStep(iteration); ++ effectCollector.advanceStep(iteration, blockIntersection); // Paper - track position inside effect was triggered on + state.getFluidState().entityInside(this.level(), blockIntersection, this, effectCollector); } -@@ -1670,6 +_,7 @@ +@@ -1690,6 +_,7 @@ this.setXRot(Mth.clamp(xRot, -90.0F, 90.0F) % 360.0F); this.yRotO = this.getYRot(); this.xRotO = this.getXRot(); + this.setYHeadRot(yRot); // Paper - Update head rotation } - public void absSnapTo(double x, double y, double z) { -@@ -1679,6 +_,7 @@ + public void absSnapTo(final double x, final double y, final double z) { +@@ -1699,6 +_,7 @@ this.yo = y; - this.zo = d1; - this.setPos(d, y, d1); + this.zo = cz; + this.setPos(cx, y, cz); + if (this.valid) this.level.getChunk((int) Math.floor(this.getX()) >> 4, (int) Math.floor(this.getZ()) >> 4); // CraftBukkit } - public void snapTo(Vec3 pos) { -@@ -1703,6 +_,7 @@ + public void snapTo(final Vec3 pos) { +@@ -1723,6 +_,7 @@ this.setXRot(xRot); this.setOldPosAndRot(); this.reapplyPosition(); @@ -612,28 +614,28 @@ } public final void setOldPosAndRot() { -@@ -1769,6 +_,7 @@ - public void push(Entity entity) { +@@ -1789,6 +_,7 @@ + public void push(final Entity entity) { if (!this.isPassengerOfSameVehicle(entity)) { if (!entity.noPhysics && !this.noPhysics) { + if (this.level.paperConfig().collisions.onlyPlayersCollide && !(entity instanceof ServerPlayer || this instanceof ServerPlayer)) return; // Paper - Collision option for requiring a player participant - double d = entity.getX() - this.getX(); - double d1 = entity.getZ() - this.getZ(); - double max = Mth.absMax(d, d1); -@@ -1804,8 +_,24 @@ + double xa = entity.getX() - this.getX(); + double za = entity.getZ() - this.getZ(); + double dd = Mth.absMax(xa, za); +@@ -1824,8 +_,24 @@ } - public void push(double x, double y, double z) { + public void push(final double xa, final double ya, final double za) { + // Paper start - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent -+ this.push(x, y, z, null); ++ this.push(xa, ya, za, null); + } + -+ public void push(double x, double y, double z, @Nullable Entity pushingEntity) { ++ public void push(final double xa, final double ya, final double za, @Nullable Entity pushingEntity) { + // Paper end - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent - if (Double.isFinite(x) && Double.isFinite(y) && Double.isFinite(z)) { -- this.setDeltaMovement(this.getDeltaMovement().add(x, y, z)); + if (Double.isFinite(xa) && Double.isFinite(ya) && Double.isFinite(za)) { +- this.setDeltaMovement(this.getDeltaMovement().add(xa, ya, za)); + // Paper start - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent -+ org.bukkit.util.Vector delta = new org.bukkit.util.Vector(x, y, z); ++ org.bukkit.util.Vector delta = new org.bukkit.util.Vector(xa, ya, za); + if (pushingEntity != null) { + io.papermc.paper.event.entity.EntityPushedByEntityAttackEvent event = new io.papermc.paper.event.entity.EntityPushedByEntityAttackEvent(this.getBukkitEntity(), io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.PUSH, pushingEntity.getBukkitEntity(), delta); + if (!event.callEvent()) { @@ -646,7 +648,7 @@ this.needsSync = true; } } -@@ -1913,8 +_,20 @@ +@@ -1932,8 +_,20 @@ } public boolean isPushable() { @@ -665,46 +667,46 @@ + } + // CraftBukkit end - public void awardKillScore(Entity entity, DamageSource damageSource) { - if (entity instanceof ServerPlayer) { -@@ -1941,15 +_,23 @@ + public void awardKillScore(final Entity victim, final DamageSource killingBlow) { + if (victim instanceof ServerPlayer) { +@@ -1960,15 +_,23 @@ } - public boolean saveAsPassenger(ValueOutput output) { + public boolean saveAsPassenger(final ValueOutput output) { - if (this.removalReason != null && !this.removalReason.shouldSave()) { + // CraftBukkit start - allow excluding certain data when saving + // Paper start - Raw entity serialization API + return this.saveAsPassenger(output, true, false, false); + } + -+ public boolean saveAsPassenger(ValueOutput output, boolean includeAll, boolean includeNonSaveable, boolean forceSerialization) { ++ public boolean saveAsPassenger(final ValueOutput output, final boolean includeAll, final boolean includeNonSaveable, final boolean forceSerialization) { + // Paper end - Raw entity serialization API + // CraftBukkit end + if (this.removalReason != null && !this.removalReason.shouldSave() && !forceSerialization) { // Paper - Raw entity serialization API return false; } else { -- String encodeId = this.getEncodeId(); -- if (encodeId == null) { -+ String encodeId = this.getEncodeId(includeNonSaveable); // Paper - Raw entity serialization API -+ if ((!this.persist && !forceSerialization) || encodeId == null) { // CraftBukkit - persist flag // Paper - Raw entity serialization API +- String id = this.getEncodeId(); +- if (id == null) { ++ String id = this.getEncodeId(includeNonSaveable); // Paper - Raw entity serialization API ++ if ((!this.persist && !forceSerialization) || id == null) { // CraftBukkit - persist flag // Paper - Raw entity serialization API return false; } else { - output.putString("id", encodeId); + output.putString("id", id); - this.saveWithoutId(output); -+ this.saveWithoutId(output , includeAll, includeNonSaveable, forceSerialization); // CraftBukkit - pass on includeAll // Paper - Raw entity serialization API ++ this.saveWithoutId(output, includeAll, includeNonSaveable, forceSerialization); // CraftBukkit - pass on includeAll // Paper - Raw entity serialization API return true; } } -@@ -1960,14 +_,34 @@ +@@ -1979,14 +_,34 @@ } - public void saveWithoutId(ValueOutput output) { + public void saveWithoutId(final ValueOutput output) { + // CraftBukkit start - allow excluding certain data when saving + // Paper start - Raw entity serialization API + this.saveWithoutId(output, true, false, false); + } + -+ public void saveWithoutId(ValueOutput output, boolean includeAll, boolean includeNonSaveable, boolean forceSerialization) { ++ public void saveWithoutId(final ValueOutput output, final boolean includeAll, final boolean includeNonSaveable, final boolean forceSerialization) { + // Paper end - Raw entity serialization API + // CraftBukkit end try { @@ -730,7 +732,7 @@ output.store("Rotation", Vec2.CODEC, new Vec2(this.getYRot(), this.getXRot())); output.putDouble("fall_distance", this.fallDistance); output.putShort("Fire", (short)this.remainingFireTicks); -@@ -1975,7 +_,29 @@ +@@ -1994,7 +_,29 @@ output.putBoolean("OnGround", this.onGround()); output.putBoolean("Invulnerable", this.invulnerable); output.putInt("PortalCooldown", this.portalCooldown); @@ -760,7 +762,7 @@ output.storeNullable("CustomName", ComponentSerialization.CODEC, this.getCustomName()); if (this.isCustomNameVisible()) { output.putBoolean("CustomNameVisible", this.isCustomNameVisible()); -@@ -1998,9 +_,14 @@ +@@ -2017,9 +_,14 @@ output.putInt("TicksFrozen", this.getTicksFrozen()); } @@ -778,23 +780,23 @@ if (!this.tags.isEmpty()) { output.store("Tags", TAG_LIST_CODEC, List.copyOf(this.tags)); -@@ -2010,13 +_,13 @@ +@@ -2029,13 +_,13 @@ output.store("data", CustomData.CODEC, this.customData); } - this.addAdditionalSaveData(output); + this.addAdditionalSaveData(output, includeAll); // CraftBukkit - pass on includeAll if (this.isVehicle()) { - ValueOutput.ValueOutputList valueOutputList = output.childrenList("Passengers"); + ValueOutput.ValueOutputList passengersList = output.childrenList("Passengers"); - for (Entity entity : this.getPassengers()) { - ValueOutput valueOutput = valueOutputList.addChild(); -- if (!entity.saveAsPassenger(valueOutput)) { -+ if (!entity.saveAsPassenger(valueOutput, includeAll, includeNonSaveable, forceSerialization)) { // CraftBukkit - pass on includeAll // Paper - Raw entity serialization API - valueOutputList.discardLast(); + for (Entity passenger : this.getPassengers()) { + ValueOutput passengerOutput = passengersList.addChild(); +- if (!passenger.saveAsPassenger(passengerOutput)) { ++ if (!passenger.saveAsPassenger(passengerOutput, includeAll, includeNonSaveable, forceSerialization)) { // CraftBukkit - pass on includeAll // Paper - Raw entity serialization API + passengersList.discardLast(); } } -@@ -2025,6 +_,34 @@ +@@ -2044,6 +_,34 @@ output.discard("Passengers"); } } @@ -827,9 +829,9 @@ + } + // Paper end } catch (Throwable var7) { - CrashReport crashReport = CrashReport.forThrowable(var7, "Saving entity NBT"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being saved"); -@@ -2068,7 +_,20 @@ + CrashReport report = CrashReport.forThrowable(var7, "Saving entity NBT"); + CrashReportCategory category = report.addCategory("Entity being saved"); +@@ -2089,7 +_,20 @@ this.setNoGravity(input.getBooleanOr("NoGravity", false)); this.setGlowingTag(input.getBooleanOr("Glowing", false)); this.setTicksFrozen(input.getIntOr("TicksFrozen", 0)); @@ -851,7 +853,7 @@ this.customData = input.read("data", CustomData.CODEC).orElse(CustomData.EMPTY); this.tags.clear(); input.read("Tags", TAG_LIST_CODEC).ifPresent(this.tags::addAll); -@@ -2079,6 +_,59 @@ +@@ -2100,6 +_,59 @@ } else { throw new IllegalStateException("Entity has invalid rotation"); } @@ -909,22 +911,24 @@ + freezeLocked = input.getBooleanOr("Paper.FreezeLock", false); + // Paper end } catch (Throwable var7) { - CrashReport crashReport = CrashReport.forThrowable(var7, "Loading entity NBT"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being loaded"); -@@ -2092,13 +_,24 @@ + CrashReport report = CrashReport.forThrowable(var7, "Loading entity NBT"); + CrashReportCategory category = report.addCategory("Entity being loaded"); +@@ -2113,7 +_,13 @@ } public final @Nullable String getEncodeId() { +- if (!this.getType().canSerialize()) { + // Paper start - Raw entity serialization API -+ return getEncodeId(false); ++ return this.getEncodeId(false); + } -+ public final @Nullable String getEncodeId(boolean includeNonSaveable) { -+ // Paper end - Raw entity serialization API - EntityType type = this.getType(); - Identifier key = EntityType.getKey(type); -- return !type.canSerialize() ? null : key.toString(); -+ return (type.canSerialize() || includeNonSaveable) ? key.toString() : null; // Paper - Raw entity serialization API - } ++ ++ public final @Nullable String getEncodeId(final boolean includeNonSaveable) { ++ if (!includeNonSaveable && !this.getType().canSerialize()) { ++ // Paper end - Raw entity serialization API + return null; + } else { + ResourceKey> typeId = this.typeHolder().unwrapKey().orElseThrow(() -> new IllegalStateException("Unregistered entity")); +@@ -2123,6 +_,12 @@ protected abstract void readAdditionalSaveData(ValueInput input); @@ -936,13 +940,13 @@ + protected abstract void addAdditionalSaveData(ValueOutput output); - public @Nullable ItemEntity spawnAtLocation(ServerLevel level, ItemLike item) { -@@ -2110,11 +_,58 @@ + public @Nullable ItemEntity spawnAtLocation(final ServerLevel level, final ItemLike resource) { +@@ -2134,11 +_,59 @@ } - public @Nullable ItemEntity spawnAtLocation(ServerLevel level, ItemStack stack, Vec3 offset) { + public @Nullable ItemEntity spawnAtLocation(final ServerLevel level, final ItemStack itemStack, final Vec3 offset) { + // Paper start - Restore vanilla drops behavior -+ return this.spawnAtLocation(level, stack, offset, null); ++ return this.spawnAtLocation(level, itemStack, offset, null); + } + + public record DefaultDrop(Item item, org.bukkit.inventory.ItemStack stack, java.util.function.@Nullable Consumer dropConsumer) { @@ -959,80 +963,81 @@ + } + } + -+ public @Nullable ItemEntity spawnAtLocation(ServerLevel level, ItemStack stack, Vec3 offset, java.util.function.@Nullable Consumer delayedAddConsumer) { ++ public @Nullable ItemEntity spawnAtLocation(final ServerLevel level, final ItemStack itemStack, final Vec3 offset, java.util.function.@Nullable Consumer delayedAddConsumer) { + // Paper end - Restore vanilla drops behavior - if (stack.isEmpty()) { + if (itemStack.isEmpty()) { return null; } else { + // CraftBukkit start - Capture drops for death event + if (this instanceof net.minecraft.world.entity.LivingEntity && !this.forceDrops) { + // Paper start - Restore vanilla drops behavior -+ ((net.minecraft.world.entity.LivingEntity) this).drops.add(new net.minecraft.world.entity.Entity.DefaultDrop(stack, itemStack -> { -+ ItemEntity itemEntity = new ItemEntity(this.level, this.getX() + offset.x, this.getY() + offset.y, this.getZ() + offset.z, itemStack); // stack is copied before consumer -+ itemEntity.setDefaultPickUpDelay(); -+ this.level.addFreshEntity(itemEntity); -+ if (delayedAddConsumer != null) delayedAddConsumer.accept(itemEntity); ++ ((net.minecraft.world.entity.LivingEntity)this).drops.add(new net.minecraft.world.entity.Entity.DefaultDrop(itemStack, dropStack -> { ++ ItemEntity dropEntity = new ItemEntity(this.level, this.getX() + offset.x, this.getY() + offset.y, this.getZ() + offset.z, dropStack); // stack is copied before consumer ++ dropEntity.setDefaultPickUpDelay(); ++ this.level.addFreshEntity(dropEntity); ++ if (delayedAddConsumer != null) delayedAddConsumer.accept(dropEntity); + })); + // Paper end - Restore vanilla drops behavior + return null; + } + // CraftBukkit end - ItemEntity itemEntity = new ItemEntity(level, this.getX() + offset.x, this.getY() + offset.y, this.getZ() + offset.z, stack); -- itemEntity.setDefaultPickUpDelay(); -+ itemEntity.setDefaultPickUpDelay(); // Paper - diff on change (in dropConsumer) + ItemEntity entity = new ItemEntity(level, this.getX() + offset.x, this.getY() + offset.y, this.getZ() + offset.z, itemStack); +- entity.setDefaultPickUpDelay(); ++ entity.setDefaultPickUpDelay(); // Paper - diff on change (in dropConsumer) + // Paper start - Call EntityDropItemEvent -+ return this.spawnAtLocation(level, itemEntity); ++ return this.spawnAtLocation(level, entity); + } + } -+ public @Nullable ItemEntity spawnAtLocation(ServerLevel level, ItemEntity itemEntity) { ++ ++ public @Nullable ItemEntity spawnAtLocation(final ServerLevel level, final ItemEntity entity) { ++ // Paper end - Call EntityDropItemEvent ++ // CraftBukkit start ++ org.bukkit.event.entity.EntityDropItemEvent event = new org.bukkit.event.entity.EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entity.getBukkitEntity()); ++ org.bukkit.Bukkit.getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ return null; ++ } ++ // CraftBukkit end + { -+ // Paper end - Call EntityDropItemEvent -+ // CraftBukkit start -+ org.bukkit.event.entity.EntityDropItemEvent event = new org.bukkit.event.entity.EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity()); -+ org.bukkit.Bukkit.getPluginManager().callEvent(event); -+ if (event.isCancelled()) { -+ return null; -+ } -+ // CraftBukkit end - level.addFreshEntity(itemEntity); - return itemEntity; + level.addFreshEntity(entity); + return entity; } -@@ -2159,6 +_,15 @@ +@@ -2183,6 +_,15 @@ - for (Leashable leashable1 : list) { - if (leashable1.canHaveALeashAttachedTo(this)) { + for (Leashable mob : mobsToLeash) { + if (mob.canHaveALeashAttachedTo(this)) { + // Paper start - PlayerLeashEvent + final org.bukkit.event.entity.PlayerLeashEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerLeashEntityEvent( -+ leashable1, ++ mob, + this, + player, + hand + ); + if (event != null && event.isCancelled()) continue; // If the event was called and cancelled, skip this. + // Paper end - PlayerLeashEvent - leashable1.setLeashedTo(this, true); - flag = true; + mob.setLeashedTo(this, true); + anyLeashed = true; } -@@ -2173,7 +_,7 @@ +@@ -2197,7 +_,7 @@ } - ItemStack itemInHand = player.getItemInHand(hand); -- if (itemInHand.is(Items.SHEARS) && this.shearOffAllLeashConnections(player)) { -+ if (itemInHand.is(Items.SHEARS) && this.shearOffAllLeashConnections(player, hand)) { // Paper - PlayerUnleashEntityEvent - pass used hand - itemInHand.hurtAndBreak(1, player, hand); + ItemStack heldItem = player.getItemInHand(hand); +- if (heldItem.is(Items.SHEARS) && this.shearOffAllLeashConnections(player)) { ++ if (heldItem.is(Items.SHEARS) && this.shearOffAllLeashConnections(player, hand)) { // Paper - PlayerUnleashEntityEvent - pass used hand + heldItem.hurtAndBreak(1, player, hand); return InteractionResult.SUCCESS; - } else if (this instanceof Mob mob -@@ -2186,11 +_,13 @@ - if (this.isAlive() && this instanceof Leashable leashable2) { - if (leashable2.getLeashHolder() == player) { + } else if (this instanceof Mob target +@@ -2210,11 +_,13 @@ + if (this.isAlive() && this instanceof Leashable leashablex) { + if (leashablex.getLeashHolder() == player) { if (!this.level().isClientSide()) { - if (player.hasInfiniteMaterials()) { -- leashable2.removeLeash(); +- leashablex.removeLeash(); - } else { -- leashable2.dropLeash(); +- leashablex.dropLeash(); + // Paper start - EntityUnleashEvent + if (!org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent( -+ leashable2, player, hand, !player.hasInfiniteMaterials(), true ++ leashablex, player, hand, !player.hasInfiniteMaterials(), true + )) { + return InteractionResult.PASS; } @@ -1040,118 +1045,120 @@ this.gameEvent(GameEvent.ENTITY_INTERACT, player); this.playSound(SoundEvents.LEAD_UNTIED); -@@ -2207,9 +_,22 @@ +@@ -2231,9 +_,22 @@ - if (leashable2.canHaveALeashAttachedTo(player)) { - if (leashable2.isLeashed()) { -- leashable2.dropLeash(); + if (leashablex.canHaveALeashAttachedTo(player)) { + if (leashablex.isLeashed()) { +- leashablex.dropLeash(); + // Paper start - EntityUnleashEvent + if (!org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent( -+ leashable2, player, hand, true, true ++ leashablex, player, hand, true, true + )) { + return InteractionResult.PASS; + } + // Paper end - EntityUnleashEvent -+ // leashable2.dropLeash(); // Paper - EntityUnleashEvent - moved into handlePlayerUnleashEntityEvent ++ // leashablex.dropLeash(); // Paper - EntityUnleashEvent - moved into handlePlayerUnleashEntityEvent } + // Paper start - EntityLeashEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerLeashEntityEvent(this, player, player, hand).isCancelled()) { -+ ((ServerPlayer) player).connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket(this, leashable2.getLeashHolder())); ++ ((ServerPlayer) player).connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket(this, leashablex.getLeashHolder())); + return InteractionResult.PASS; + } + // Paper end - EntityLeashEvent - leashable2.setLeashedTo(player, true); + leashablex.setLeashedTo(player, true); this.playSound(SoundEvents.LEAD_TIED); - itemInHand1.shrink(1); -@@ -2223,7 +_,12 @@ + itemStack.shrink(1); +@@ -2247,7 +_,13 @@ } - public boolean shearOffAllLeashConnections(@Nullable Player player) { -- boolean flag = this.dropAllLeashConnections(player); -+ // Paper start - EntityUnleashEvent - overload + public boolean shearOffAllLeashConnections(final @Nullable Player player) { +- boolean dropped = this.dropAllLeashConnections(player); ++ // Paper start - EntityUnleashEvent - overload + return this.shearOffAllLeashConnections(player, null); + } -+ public boolean shearOffAllLeashConnections(@Nullable Player player, @Nullable InteractionHand interactionHand) { -+ // Paper end - EntityUnleashEvent - overload -+ boolean flag = this.dropAllLeashConnections(player, interactionHand); // Paper - EntityUnleashEvent - overload - if (flag && this.level() instanceof ServerLevel serverLevel) { ++ ++ public boolean shearOffAllLeashConnections(final @Nullable Player player, final @Nullable InteractionHand hand) { ++ // Paper end - EntityUnleashEvent - overload ++ boolean dropped = this.dropAllLeashConnections(player, hand); // Paper - EntityUnleashEvent - overload + if (dropped && this.level() instanceof ServerLevel serverLevel) { serverLevel.playSound(null, this.blockPosition(), SoundEvents.SHEARS_SNIP, player != null ? player.getSoundSource() : this.getSoundSource()); } -@@ -2232,15 +_,38 @@ +@@ -2256,15 +_,39 @@ } - public boolean dropAllLeashConnections(@Nullable Player player) { -+ // Paper start - EntityUnleashEvent - overload -+ return dropAllLeashConnections(player, null); + public boolean dropAllLeashConnections(final @Nullable Player player) { ++ // Paper start - EntityUnleashEvent - overload ++ return this.dropAllLeashConnections(player, null); + } -+ public boolean dropAllLeashConnections(@Nullable Player player, @Nullable InteractionHand interactionHand) { -+ // Paper start - EntityUnleashEvent - overload - List list = Leashable.leashableLeashedTo(this); -- boolean flag = !list.isEmpty(); -+ boolean flag = false; // Paper - EntityUnleashEvent - compute flag later, events might prevent unleashing all connected leashables. - if (this instanceof Leashable leashable && leashable.isLeashed()) { -- leashable.dropLeash(); -- flag = true; ++ ++ public boolean dropAllLeashConnections(final @Nullable Player player, final @Nullable InteractionHand hand) { ++ // Paper end - EntityUnleashEvent - overload + List leashables = Leashable.leashableLeashedTo(this); +- boolean dropped = !leashables.isEmpty(); ++ boolean dropped = false; // Paper - EntityUnleashEvent - compute flag later, events might prevent unleashing all connected leashables. + if (this instanceof Leashable leashableThis && leashableThis.isLeashed()) { +- leashableThis.dropLeash(); +- dropped = true; + // Paper start - EntityUnleashEvent -+ flag |= org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent( ++ dropped |= org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent( + this, + player, -+ interactionHand, ++ hand, + true, + true + ); + // Paper end - EntityUnleashEvent -+ // leashable.dropLeash(); // Paper - EntityUnleashEvent - moved into handlePlayerUnleashEntityEvent -+ // flag = true; // Paper - EntityUnleashEvent - moved above ++ // leashableThis.dropLeash(); // Paper - EntityUnleashEvent - moved into handlePlayerUnleashEntityEvent ++ // dropped = true; // Paper - EntityUnleashEvent - moved above } - for (Leashable leashable1 : list) { -- leashable1.dropLeash(); + for (Leashable leashable : leashables) { +- leashable.dropLeash(); + // Paper start - EntityUnleashEvent -+ flag |= org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent( // Update flag here, if any entity was unleashed, set to true. -+ leashable1, ++ dropped |= org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent( // Update flag here, if any entity was unleashed, set to true. ++ leashable, + player, -+ interactionHand, ++ hand, + true, + true + ); -+ // leashable1.dropLeash(); // Paper - EntityUnleashEvent - moved into handlePlayerUnleashEntityEvent ++ // leashable.dropLeash(); // Paper - EntityUnleashEvent - moved into handlePlayerUnleashEntityEvent + // Paper end - EntityUnleashEvent } - if (flag) { -@@ -2264,7 +_,9 @@ + if (dropped) { +@@ -2288,7 +_,9 @@ this.gameEvent(GameEvent.SHEAR, player); this.playSound(equippable.shearingSound().value()); if (this.level() instanceof ServerLevel serverLevel) { + this.forceDrops = true; // Paper - this.spawnAtLocation(serverLevel, itemBySlot, average); + this.spawnAtLocation(serverLevel, itemStack, equipmentSpawnOffset); + this.forceDrops = false; // Paper - CriteriaTriggers.PLAYER_SHEARED_EQUIPMENT.trigger((ServerPlayer)player, itemBySlot, mob); + CriteriaTriggers.PLAYER_SHEARED_EQUIPMENT.trigger((ServerPlayer)player, itemStack, target); } -@@ -2337,11 +_,11 @@ +@@ -2356,11 +_,11 @@ } - public boolean startRiding(Entity entity, boolean force, boolean triggerEvents) { -- if (entity == this.vehicle) { -+ if (entity == this.vehicle || entity.level != this.level) { // Paper - Ensure entity passenger world matches ridden entity (bad plugins) + public boolean startRiding(final Entity entityToRide, final boolean force, final boolean sendEventAndTriggers) { +- if (entityToRide == this.vehicle) { ++ if (entityToRide == this.vehicle || entityToRide.level != this.level) { // Paper - Ensure entity passenger world matches ridden entity (bad plugins) return false; - } else if (!entity.couldAcceptPassenger()) { + } else if (!entityToRide.couldAcceptPassenger()) { return false; -- } else if (!this.level().isClientSide() && !entity.type.canSerialize()) { -+ } else if (!force && !this.level().isClientSide() && !entity.type.canSerialize()) { // SPIGOT-7947: Allow force riding all entities +- } else if (!this.level().isClientSide() && !entityToRide.type.canSerialize()) { ++ } else if (!force && !this.level().isClientSide() && !entityToRide.type.canSerialize()) { // SPIGOT-7947: Allow force riding all entities return false; } else { - for (Entity entity1 = entity; entity1.vehicle != null; entity1 = entity1.vehicle) { -@@ -2351,6 +_,27 @@ + for (Entity vehicleEntity = entityToRide; vehicleEntity.vehicle != null; vehicleEntity = vehicleEntity.vehicle) { +@@ -2370,6 +_,27 @@ } - if (force || this.canRide(entity) && entity.canAddPassenger(this)) { + if (force || this.canRide(entityToRide) && entityToRide.canAddPassenger(this)) { + // CraftBukkit start -+ if (entity.getBukkitEntity() instanceof org.bukkit.entity.Vehicle && this.getBukkitEntity() instanceof org.bukkit.entity.LivingEntity) { -+ org.bukkit.event.vehicle.VehicleEnterEvent event = new org.bukkit.event.vehicle.VehicleEnterEvent((org.bukkit.entity.Vehicle) entity.getBukkitEntity(), this.getBukkitEntity()); ++ if (entityToRide.getBukkitEntity() instanceof org.bukkit.entity.Vehicle && this.getBukkitEntity() instanceof org.bukkit.entity.LivingEntity) { ++ org.bukkit.event.vehicle.VehicleEnterEvent event = new org.bukkit.event.vehicle.VehicleEnterEvent((org.bukkit.entity.Vehicle) entityToRide.getBukkitEntity(), this.getBukkitEntity()); + // Suppress during worldgen + if (this.valid) { + org.bukkit.Bukkit.getPluginManager().callEvent(event); @@ -1161,7 +1168,7 @@ + } + } + -+ org.bukkit.event.entity.EntityMountEvent event = new org.bukkit.event.entity.EntityMountEvent(this.getBukkitEntity(), entity.getBukkitEntity()); ++ org.bukkit.event.entity.EntityMountEvent event = new org.bukkit.event.entity.EntityMountEvent(this.getBukkitEntity(), entityToRide.getBukkitEntity()); + // Suppress during worldgen + if (this.valid) { + org.bukkit.Bukkit.getPluginManager().callEvent(event); @@ -1173,7 +1180,7 @@ if (this.isPassenger()) { this.stopRiding(); } -@@ -2383,10 +_,16 @@ +@@ -2402,10 +_,16 @@ } public void removeVehicle() { @@ -1184,14 +1191,14 @@ + public void removeVehicle(boolean suppressCancellation) { + // Paper end - Force entity dismount during teleportation if (this.vehicle != null) { - Entity entity = this.vehicle; + Entity oldVehicle = this.vehicle; this.vehicle = null; -- entity.removePassenger(this); -+ if (!entity.removePassenger(this, suppressCancellation)) this.vehicle = entity; // CraftBukkit // Paper - Force entity dismount during teleportation +- oldVehicle.removePassenger(this); ++ if (!oldVehicle.removePassenger(this, suppressCancellation)) this.vehicle = oldVehicle; // CraftBukkit // Paper - Force entity dismount during teleportation Entity.RemovalReason removalReason = this.getRemovalReason(); if (removalReason == null || removalReason.shouldDestroy()) { - this.level().gameEvent(this, GameEvent.ENTITY_DISMOUNT, entity.position); -@@ -2395,7 +_,13 @@ + this.level().gameEvent(this, GameEvent.ENTITY_DISMOUNT, oldVehicle.position); +@@ -2414,7 +_,13 @@ } public void stopRiding() { @@ -1205,17 +1212,18 @@ + // Paper end - Force entity dismount during teleportation } - protected void addPassenger(Entity passenger) { -@@ -2417,10 +_,43 @@ + protected void addPassenger(final Entity passenger) { +@@ -2436,10 +_,44 @@ } } -- protected void removePassenger(Entity passenger) { +- protected void removePassenger(final Entity passenger) { + // Paper start - Force entity dismount during teleportation -+ protected boolean removePassenger(Entity passenger) { -+ return removePassenger(passenger, false); ++ protected boolean removePassenger(final Entity passenger) { ++ return this.removePassenger(passenger, false); + } -+ protected boolean removePassenger(Entity passenger, boolean suppressCancellation) { // CraftBukkit ++ ++ protected boolean removePassenger(final Entity passenger, final boolean suppressCancellation) { // CraftBukkit + // Paper end - Force entity dismount during teleportation if (passenger.getVehicle() == this) { throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)"); @@ -1251,15 +1259,15 @@ if (this.passengers.size() == 1 && this.passengers.get(0) == passenger) { this.passengers = ImmutableList.of(); } else { -@@ -2429,6 +_,7 @@ +@@ -2448,6 +_,7 @@ passenger.boardingCooldown = 60; } + return true; // CraftBukkit } - protected boolean canAddPassenger(Entity passenger) { -@@ -2611,7 +_,7 @@ + protected boolean canAddPassenger(final Entity passenger) { +@@ -2630,7 +_,7 @@ } public boolean isCrouching() { @@ -1268,7 +1276,7 @@ } public boolean isSprinting() { -@@ -2627,7 +_,7 @@ +@@ -2646,7 +_,7 @@ } public boolean isVisuallySwimming() { @@ -1277,10 +1285,10 @@ } public boolean isVisuallyCrawling() { -@@ -2635,6 +_,13 @@ +@@ -2654,6 +_,13 @@ } - public void setSwimming(boolean swimming) { + public void setSwimming(final boolean swimming) { + // CraftBukkit start + if (this.valid && this.isSwimming() != swimming && this instanceof net.minecraft.world.entity.LivingEntity) { + if (org.bukkit.craftbukkit.event.CraftEventFactory.callToggleSwimEvent((net.minecraft.world.entity.LivingEntity) this, swimming).isCancelled()) { @@ -1291,7 +1299,7 @@ this.setSharedFlag(FLAG_SWIMMING, swimming); } -@@ -2672,6 +_,7 @@ +@@ -2691,6 +_,7 @@ } public @Nullable PlayerTeam getTeam() { @@ -1299,10 +1307,10 @@ return this.level().getScoreboard().getPlayersTeam(this.getScoreboardName()); } -@@ -2688,7 +_,11 @@ +@@ -2707,7 +_,11 @@ } - public void setInvisible(boolean invisible) { + public void setInvisible(final boolean invisible) { - this.setSharedFlag(FLAG_INVISIBLE, invisible); + // CraftBukkit - start + if (!this.persistentInvisibility) { // Prevent Minecraft from removing our invisibility flag @@ -1311,8 +1319,8 @@ + // CraftBukkit - end } - public boolean getSharedFlag(int flag) { -@@ -2705,7 +_,7 @@ + public boolean getSharedFlag(@Entity.Flags final int flag) { +@@ -2724,7 +_,7 @@ } public int getMaxAirSupply() { @@ -1321,19 +1329,21 @@ } public int getAirSupply() { -@@ -2713,10 +_,22 @@ +@@ -2732,10 +_,24 @@ } - public void setAirSupply(int airSupply) { -- this.entityData.set(DATA_AIR_SUPPLY_ID, airSupply); + public void setAirSupply(final int supply) { +- this.entityData.set(DATA_AIR_SUPPLY_ID, supply); + // CraftBukkit start -+ org.bukkit.event.entity.EntityAirChangeEvent event = new org.bukkit.event.entity.EntityAirChangeEvent(this.getBukkitEntity(), airSupply); ++ org.bukkit.event.entity.EntityAirChangeEvent event = new org.bukkit.event.entity.EntityAirChangeEvent(this.getBukkitEntity(), supply); + // Suppress during worldgen + if (this.valid) { + event.getEntity().getServer().getPluginManager().callEvent(event); + } -+ if (event.isCancelled() && this.getAirSupply() != airSupply) { -+ this.entityData.markDirty(DATA_AIR_SUPPLY_ID); ++ if (event.isCancelled() && this.getAirSupply() != supply) { ++ if (this instanceof ServerPlayer player) { ++ this.resendPossiblyDesyncedDataValues(java.util.List.of(DATA_AIR_SUPPLY_ID), player); // todo is that even needed? ++ } + return; + } + this.entityData.set(DATA_AIR_SUPPLY_ID, event.getAmount()); @@ -1345,13 +1355,13 @@ this.setTicksFrozen(0); } -@@ -2743,11 +_,43 @@ +@@ -2762,11 +_,43 @@ - public void thunderHit(ServerLevel level, LightningBolt lightning) { + public void thunderHit(final ServerLevel level, final LightningBolt lightningBolt) { this.setRemainingFireTicks(this.remainingFireTicks + 1); + // CraftBukkit start + final org.bukkit.entity.Entity thisBukkitEntity = this.getBukkitEntity(); -+ final org.bukkit.entity.Entity stormBukkitEntity = lightning.getBukkitEntity(); ++ final org.bukkit.entity.Entity stormBukkitEntity = lightningBolt.getBukkitEntity(); + final org.bukkit.plugin.PluginManager pluginManager = org.bukkit.Bukkit.getPluginManager(); + // CraftBukkit end if (this.remainingFireTicks == 0) { @@ -1386,14 +1396,14 @@ + return; + } + -+ if (!this.hurtServer(level, this.damageSources().lightningBolt().eventEntityDamager(lightning), 5.0F)) { // Paper - fix DamageSource API ++ if (!this.hurtServer(level, this.damageSources().lightningBolt().eventEntityDamager(lightningBolt), 5.0F)) { // Paper - fix DamageSource API + return; + } + // CraftBukkit end } - public void onAboveBubbleColumn(boolean downwards, BlockPos pos) { -@@ -2903,26 +_,30 @@ + public void onAboveBubbleColumn(final boolean dragDown, final BlockPos pos) { +@@ -2916,26 +_,30 @@ return this.removalReason != null ? String.format( Locale.ROOT, @@ -1403,7 +1413,7 @@ this.getPlainTextName(), this.id, + this.uuid, // Paper - add more info - string, + levelId, this.getX(), this.getY(), this.getZ(), @@ -1418,7 +1428,7 @@ this.getPlainTextName(), this.id, + this.uuid, // Paper - add more info - string, + levelId, this.getX(), this.getY(), - this.getZ() @@ -1427,35 +1437,37 @@ ); } -@@ -2946,6 +_,13 @@ +@@ -2959,6 +_,13 @@ } - public void restoreFrom(Entity entity) { + public void restoreFrom(final Entity oldEntity) { + // Paper start - Forward CraftEntity in teleport command -+ org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity = entity.bukkitEntity; ++ org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity = oldEntity.bukkitEntity; + if (bukkitEntity != null) { + bukkitEntity.setHandle(this); + this.bukkitEntity = bukkitEntity; + } + // Paper end - Forward CraftEntity in teleport command - try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(this.problemPath(), LOGGER)) { - TagValueOutput tagValueOutput = TagValueOutput.createWithContext(scopedCollector, entity.registryAccess()); - entity.saveWithoutId(tagValueOutput); -@@ -2957,7 +_,65 @@ + try (ProblemReporter.ScopedCollector reporter = new ProblemReporter.ScopedCollector(this.problemPath(), LOGGER)) { + TagValueOutput entityData = TagValueOutput.createWithContext(reporter, oldEntity.registryAccess()); + oldEntity.saveWithoutId(entityData); +@@ -2969,8 +_,66 @@ + this.portalProcess = oldEntity.portalProcess; } - public @Nullable Entity teleport(TeleportTransition teleportTransition) { +- public @Nullable Entity teleport(final TeleportTransition transition) { ++ public @Nullable Entity teleport(TeleportTransition transition) { // Paper - remove param final + // Paper start - Fix item duplication and teleport issues -+ if ((!this.isAlive() || !this.valid) && (teleportTransition.newLevel() != this.level)) { -+ LOGGER.warn("Illegal Entity Teleport {} to {}:{}", this, teleportTransition.newLevel(), teleportTransition.position(), new Throwable()); ++ if ((!this.isAlive() || !this.valid) && (transition.newLevel() != this.level)) { ++ LOGGER.warn("Illegal Entity Teleport {} to {}:{}", this, transition.newLevel(), transition.position(), new Throwable()); + return null; + } + // Paper end - Fix item duplication and teleport issues if (this.level() instanceof ServerLevel serverLevel && !this.isRemoved()) { + // CraftBukkit start -+ PositionMoveRotation absolutePosition = PositionMoveRotation.calculateAbsolute(PositionMoveRotation.of(this), PositionMoveRotation.of(teleportTransition), teleportTransition.relatives()); ++ PositionMoveRotation absolutePosition = PositionMoveRotation.calculateAbsolute(PositionMoveRotation.of(this), PositionMoveRotation.of(transition), transition.relatives()); + Vec3 velocity = absolutePosition.deltaMovement(); // Paper -+ org.bukkit.Location to = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(absolutePosition.position(), teleportTransition.newLevel(), absolutePosition.yRot(), absolutePosition.xRot()); ++ org.bukkit.Location to = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(absolutePosition.position(), transition.newLevel(), absolutePosition.yRot(), absolutePosition.xRot()); + // Paper start - gateway-specific teleport event + final org.bukkit.event.entity.EntityTeleportEvent teleEvent; + if (this.portalProcess != null && this.portalProcess.isSamePortal(((net.minecraft.world.level.block.EndGatewayBlock) Blocks.END_GATEWAY)) && this.level.getBlockEntity(this.portalProcess.getEntryPosition()) instanceof net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity theEndGatewayBlockEntity) { @@ -1470,7 +1482,7 @@ + } + if (!to.equals(teleEvent.getTo())) { + to = teleEvent.getTo(); -+ teleportTransition = new TeleportTransition(((org.bukkit.craftbukkit.CraftWorld) to.getWorld()).getHandle(), org.bukkit.craftbukkit.util.CraftLocation.toVec3(to), Vec3.ZERO, to.getYaw(), to.getPitch(), teleportTransition.missingRespawnBlock(), teleportTransition.asPassenger(), Set.of(), teleportTransition.postTeleportTransition(), teleportTransition.cause()); ++ transition = new TeleportTransition(((org.bukkit.craftbukkit.CraftWorld) to.getWorld()).getHandle(), org.bukkit.craftbukkit.util.CraftLocation.toVec3(to), Vec3.ZERO, to.getYaw(), to.getPitch(), transition.missingRespawnBlock(), transition.asPassenger(), Set.of(), transition.postTeleportTransition(), transition.cause()); + // Paper start - Call EntityPortalExitEvent + velocity = Vec3.ZERO; + } @@ -1496,7 +1508,7 @@ + to = event.getTo().clone(); + velocity = org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getAfter()); + } -+ teleportTransition = new TeleportTransition(((org.bukkit.craftbukkit.CraftWorld) to.getWorld()).getHandle(), org.bukkit.craftbukkit.util.CraftLocation.toVec3(to), velocity, to.getYaw(), to.getPitch(), teleportTransition.missingRespawnBlock(), teleportTransition.asPassenger(), Set.of(), teleportTransition.postTeleportTransition(), teleportTransition.cause()); ++ transition = new TeleportTransition(((org.bukkit.craftbukkit.CraftWorld) to.getWorld()).getHandle(), org.bukkit.craftbukkit.util.CraftLocation.toVec3(to), velocity, to.getYaw(), to.getPitch(), transition.missingRespawnBlock(), transition.asPassenger(), Set.of(), transition.postTeleportTransition(), transition.cause()); + } + } + if (this.isRemoved()) { @@ -1504,11 +1516,11 @@ + } + // Paper end - Call EntityPortalExitEvent + // CraftBukkit end - ServerLevel level = teleportTransition.newLevel(); - boolean flag = level.dimension() != serverLevel.dimension(); - if (!teleportTransition.asPassenger()) { -@@ -3006,10 +_,15 @@ - profilerFiller.pop(); + ServerLevel newLevel = transition.newLevel(); + boolean otherDimension = newLevel.dimension() != serverLevel.dimension(); + if (!transition.asPassenger()) { +@@ -3019,10 +_,15 @@ + profiler.pop(); return null; } else { + // Paper start - Fix item duplication and teleport issues @@ -1516,15 +1528,15 @@ + leashable.dropLeash(); // Paper drop lead + } + // Paper end - Fix item duplication and teleport issues - entityx.restoreFrom(this); + newEntity.restoreFrom(this); this.removeAfterChangingDimensions(); - entityx.teleportSetPosition(PositionMoveRotation.of(this), PositionMoveRotation.of(teleportTransition), teleportTransition.relatives()); -- newLevel.addDuringTeleport(entityx); -+ if (this.inWorld) newLevel.addDuringTeleport(entityx); // CraftBukkit - Don't spawn the new entity if the current entity isn't spawned + newEntity.teleportSetPosition(PositionMoveRotation.of(this), PositionMoveRotation.of(transition), transition.relatives()); +- newLevel.addDuringTeleport(newEntity); ++ if (this.inWorld) newLevel.addDuringTeleport(newEntity); // CraftBukkit - Don't spawn the new entity if the current entity isn't spawned - for (Entity entity2 : list) { - entity2.startRiding(entityx, true, false); -@@ -3099,9 +_,17 @@ + for (Entity newPassenger : newPassengers) { + newPassenger.startRiding(newEntity, true, false); +@@ -3109,9 +_,17 @@ } protected void removeAfterChangingDimensions() { @@ -1544,42 +1556,61 @@ + // Paper end - Expand EntityUnleashEvent } - if (this instanceof WaypointTransmitter waypointTransmitter && this.level instanceof ServerLevel serverLevel) { -@@ -3118,6 +_,7 @@ + if (this instanceof WaypointTransmitter waypoint && this.level instanceof ServerLevel serverLevel) { +@@ -3128,6 +_,7 @@ } - public boolean canTeleport(Level fromLevel, Level toLevel) { + public boolean canTeleport(final Level from, final Level to) { + if (!this.isAlive() || !this.valid) return false; // Paper - Fix item duplication and teleport issues - if (fromLevel.dimension() == Level.END && toLevel.dimension() == Level.OVERWORLD) { - for (Entity entity : this.getPassengers()) { - if (entity instanceof ServerPlayer serverPlayer && !serverPlayer.seenCredits) { -@@ -3224,13 +_,19 @@ - return this.entityData.get(DATA_CUSTOM_NAME_VISIBLE); + if (from.dimension() == Level.END && to.dimension() == Level.OVERWORLD) { + for (Entity passenger : this.getPassengers()) { + if (passenger instanceof ServerPlayer player && !player.seenCredits) { +@@ -3246,7 +_,7 @@ + } } -- public boolean teleportTo(ServerLevel level, double x, double y, double z, Set relativeMovements, float yaw, float pitch, boolean setCamera) { -- Entity entity = this.teleport(new TeleportTransition(level, new Vec3(x, y, z), Vec3.ZERO, yaw, pitch, relativeMovements, TeleportTransition.DO_NOTHING)); -+ // CraftBukkit start -+ public final boolean teleportTo(ServerLevel level, double x, double y, double z, Set relativeMovements, float yaw, float pitch, boolean setCamera) { -+ return this.teleportTo(level, x, y, z, relativeMovements, yaw, pitch, setCamera, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN); +- public boolean teleportTo( ++ public final boolean teleportTo( // CraftBukkit - final + final ServerLevel level, + final double x, + final double y, +@@ -3256,14 +_,30 @@ + final float newXRot, + final boolean resetCamera + ) { ++ // CraftBukkit start ++ return this.teleportTo(level, x, y, z, relatives, newYRot, newXRot, resetCamera, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN); + } + -+ public boolean teleportTo(ServerLevel level, double x, double y, double z, Set relativeMovements, float yaw, float pitch, boolean setCamera, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { ++ public boolean teleportTo( ++ final ServerLevel level, ++ final double x, ++ final double y, ++ final double z, ++ final Set relatives, ++ final float newYRot, ++ final float newXRot, ++ final boolean resetCamera, ++ final org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause ++ ) { + // CraftBukkit end -+ Entity entity = this.teleport(new TeleportTransition(level, new Vec3(x, y, z), Vec3.ZERO, yaw, pitch, relativeMovements, TeleportTransition.DO_NOTHING, cause)); // CraftBukkit - return entity != null; + Entity newEntity = this.teleport( +- new TeleportTransition(level, new Vec3(x, y, z), Vec3.ZERO, newYRot, newXRot, relatives, TeleportTransition.DO_NOTHING) ++ new TeleportTransition(level, new Vec3(x, y, z), Vec3.ZERO, newYRot, newXRot, relatives, TeleportTransition.DO_NOTHING, cause) // CraftBukkit + ); + return newEntity != null; } - public void dismountTo(double x, double y, double z) { + public void dismountTo(final double x, final double y, final double z) { - this.teleportTo(x, y, z); + this.teleportTo(x, y, z); // Paper - diff on change for override } - public void teleportTo(double x, double y, double z) { -@@ -3339,7 +_,26 @@ + public void teleportTo(final double x, final double y, final double z) { +@@ -3374,7 +_,26 @@ } - public final void setBoundingBox(AABB bb) { + public final void setBoundingBox(final AABB bb) { - this.bb = bb; + // CraftBukkit start - block invalid bounding boxes + double minX = bb.minX, @@ -1603,11 +1634,11 @@ + // CraftBukkit end } - public final float getEyeHeight(Pose pose) { -@@ -3367,6 +_,12 @@ + public final float getEyeHeight(final Pose pose) { +@@ -3398,6 +_,12 @@ } - public void stopSeenByPlayer(ServerPlayer player) { + public void stopSeenByPlayer(final ServerPlayer player) { + // Paper start - entity tracking events + // Since this event cannot be cancelled, we should call it here to catch all "un-tracks" + if (io.papermc.paper.event.player.PlayerUntrackEntityEvent.getHandlerList().getRegisteredListeners().length > 0) { @@ -1616,8 +1647,8 @@ + // Paper end - entity tracking events } - public float rotate(Rotation transformRotation) { -@@ -3425,21 +_,32 @@ + public float rotate(final Rotation rotation) { +@@ -3456,21 +_,32 @@ } private Stream getIndirectPassengersStream() { @@ -1651,7 +1682,7 @@ } public int countPlayerPassengers() { -@@ -3447,6 +_,7 @@ +@@ -3478,6 +_,7 @@ } public boolean hasExactlyOnePlayerPassenger() { @@ -1659,7 +1690,7 @@ return this.countPlayerPassengers() == 1; } -@@ -3527,9 +_,38 @@ +@@ -3558,9 +_,38 @@ return 0; } @@ -1692,28 +1723,16 @@ + }; + // CraftBukkit end + - public CommandSourceStack createCommandSourceStackForNameResolution(ServerLevel level) { + public CommandSourceStack createCommandSourceStackForNameResolution(final ServerLevel level) { return new CommandSourceStack( - CommandSource.NULL, + this.commandSource, // CraftBukkit this.position(), this.getRotationVector(), level, -@@ -3595,6 +_,11 @@ - vec3 = vec3.add(flow); - i++; - } -+ // CraftBukkit start - store last lava contact location -+ if (fluidTag == FluidTags.LAVA) { -+ this.lastLavaContact = mutableBlockPos.immutable(); -+ } -+ // CraftBukkit end - } - } - } -@@ -3694,7 +_,9 @@ +@@ -3658,7 +_,9 @@ - public void setDeltaMovement(Vec3 deltaMovement) { + public void setDeltaMovement(final Vec3 deltaMovement) { if (deltaMovement.isFinite()) { + synchronized (this.posLock) { // Paper - detailed watchdog information this.deltaMovement = deltaMovement; @@ -1721,11 +1740,14 @@ } } -@@ -3760,9 +_,35 @@ - return this.getZ((2.0 * this.random.nextDouble() - 1.0) * scale); +@@ -3729,8 +_,34 @@ } -+ // Paper start - Block invalid positions and bounding box + public final void setPosRaw(final double x, final double y, final double z) { ++ // Paper start - Block invalid positions and bounding box ++ this.setPosRaw(x, y, z, false); ++ } ++ + public static boolean checkPosition(Entity entity, double newX, double newY, double newZ) { + if (Double.isFinite(newX) && Double.isFinite(newY) && Double.isFinite(newZ)) { + return true; @@ -1740,10 +1762,6 @@ + LOGGER.error("New entity position is invalid! Tried to set invalid position ({},{},{}) for entity {} located at {}, entity info: {}", newX, newY, newZ, entity.getClass().getName(), entity.position(), entityInfo, new Throwable()); + return false; + } -+ - public final void setPosRaw(double x, double y, double z) { -+ this.setPosRaw(x, y, z, false); -+ } + + public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) { + if (!checkPosition(this, x, y, z)) { @@ -1754,11 +1772,11 @@ + synchronized (this.posLock) { // Paper - detailed watchdog information this.position = new Vec3(x, y, z); + } // Paper - detailed watchdog information - int floor = Mth.floor(x); - int floor1 = Mth.floor(y); - int floor2 = Mth.floor(z); -@@ -3784,7 +_,18 @@ - serverLevel.getWaypointManager().updatePlayer(serverPlayer); + int fx = Mth.floor(x); + int fy = Mth.floor(y); + int fz = Mth.floor(z); +@@ -3752,7 +_,18 @@ + serverLevel.getWaypointManager().updatePlayer(player); } } - } @@ -1777,7 +1795,7 @@ } public void checkDespawn() { -@@ -3836,6 +_,12 @@ +@@ -3804,6 +_,12 @@ return this.getTicksFrozen() > 0; } @@ -1790,23 +1808,23 @@ public float getYRot() { return this.yRot; } -@@ -3886,7 +_,9 @@ +@@ -3854,7 +_,9 @@ } @Override -- public final void setRemoved(Entity.RemovalReason removalReason) { -+ public final void setRemoved(Entity.RemovalReason removalReason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause +- public final void setRemoved(final Entity.RemovalReason reason) { ++ public final void setRemoved(final Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause + org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit + final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers if (this.removalReason == null) { - this.removalReason = removalReason; + this.removalReason = reason; } -@@ -3898,12 +_,28 @@ +@@ -3866,12 +_,28 @@ this.getPassengers().forEach(Entity::stopRiding); - this.levelCallback.onRemove(removalReason); - this.onRemoval(removalReason); + this.levelCallback.onRemove(reason); + this.onRemoval(reason); + // Paper start - Folia schedulers -+ if (!(this instanceof ServerPlayer) && removalReason != RemovalReason.CHANGED_DIMENSION && !alreadyRemoved) { ++ if (!(this instanceof ServerPlayer) && reason != RemovalReason.CHANGED_DIMENSION && !alreadyRemoved) { + // Players need to be special cased, because they are regularly removed from the world + this.retireScheduler(); + } @@ -1828,9 +1846,9 @@ + // Paper end - Folia schedulers + @Override - public void setLevelCallback(EntityInLevelCallback levelCallback) { + public void setLevelCallback(final EntityInLevelCallback levelCallback) { this.levelCallback = levelCallback; -@@ -4101,4 +_,14 @@ +@@ -4093,4 +_,14 @@ return this.save; } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/EntityEquipment.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/EntityEquipment.java.patch index 391fd01b5702..4893f35cf282 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/EntityEquipment.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/EntityEquipment.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/entity/EntityEquipment.java @@ -70,4 +_,11 @@ public void clear() { - this.items.replaceAll((equipmentSlot, itemStack) -> ItemStack.EMPTY); + this.items.replaceAll((s, v) -> ItemStack.EMPTY); } + + // Paper start - EntityDeathEvent diff --git a/paper-server/patches/sources/net/minecraft/world/entity/EntityFluidInteraction.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/EntityFluidInteraction.java.patch new file mode 100644 index 000000000000..2a6661d49289 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/entity/EntityFluidInteraction.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/EntityFluidInteraction.java ++++ b/net/minecraft/world/entity/EntityFluidInteraction.java +@@ -77,6 +_,11 @@ + } + + tracker.accumulateCurrent(flow); ++ // CraftBukkit start - store last lava contact location ++ if (fluidState.is(net.minecraft.tags.FluidTags.LAVA)) { ++ entity.lastLavaContact = mutablePos.immutable(); ++ } ++ // CraftBukkit end - store last lava contact location + } + } + } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/EntityReference.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/EntityReference.java.patch index 0a2cd6195a45..58febb531c20 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/EntityReference.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/EntityReference.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/EntityReference.java +++ b/net/minecraft/world/entity/EntityReference.java -@@ -140,4 +_,15 @@ +@@ -142,4 +_,15 @@ public int hashCode() { return this.getUUID().hashCode(); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/EntitySelector.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/EntitySelector.java.patch index c4e53b4c8a31..7cd390264779 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/EntitySelector.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/EntitySelector.java.patch @@ -3,7 +3,7 @@ @@ -17,6 +_,22 @@ public static final Predicate NO_SPECTATORS = entity -> !entity.isSpectator(); public static final Predicate CAN_BE_COLLIDED_WITH = NO_SPECTATORS.and(entity -> entity.canBeCollidedWith(null)); - public static final Predicate CAN_BE_PICKED = NO_SPECTATORS.and(Entity::isPickable); + public static final Predicate CAN_BE_PICKED = Entity::isPickable; + // Paper start - Ability to control player's insomnia and phantoms + public static Predicate IS_INSOMNIAC = (player) -> { + int playerInsomniaTicks = player.level().paperConfig().entities.behavior.playerInsomniaStartTicks; @@ -23,20 +23,18 @@ private EntitySelector() { } -@@ -32,13 +_,13 @@ - return (Predicate)(collisionRule == Team.CollisionRule.NEVER +@@ -33,12 +_,12 @@ ? Predicates.alwaysFalse() : NO_SPECTATORS.and( -- entity1 -> { -- if (!entity1.isPushable()) { -+ entity1 -> { final Entity pushedEntity = entity1; // Paper - OBFHELPER -+ if (!pushedEntity.isPushable() || !pushedEntity.canCollideWithBukkit(entity) || !entity.canCollideWithBukkit(pushedEntity)) { // CraftBukkit - collidable API // Paper - Climbing should not bypass cramming gamerule + input -> { +- if (!input.isPushable()) { ++ if (!input.isPushable() || !input.canCollideWithBukkit(entity) || !entity.canCollideWithBukkit(input)) { // CraftBukkit - collidable API // Paper - Climbing should not bypass cramming gamerule return false; - } else if (!entity.level().isClientSide() || entity1 instanceof Player player && player.isLocalPlayer()) { - Team team1 = entity1.getTeam(); - Team.CollisionRule collisionRule1 = team1 == null ? Team.CollisionRule.ALWAYS : team1.getCollisionRule(); -- if (collisionRule1 == Team.CollisionRule.NEVER) { -+ if (collisionRule1 == Team.CollisionRule.NEVER || (pushedEntity instanceof Player && !io.papermc.paper.configuration.GlobalConfiguration.get().collisions.enablePlayerCollisions)) { // Paper - Configurable player collision + } else if (!entity.level().isClientSide() || input instanceof Player player && player.isLocalPlayer()) { + Team theirTeam = input.getTeam(); + Team.CollisionRule theirCollisionRule = theirTeam == null ? Team.CollisionRule.ALWAYS : theirTeam.getCollisionRule(); +- if (theirCollisionRule == Team.CollisionRule.NEVER) { ++ if (theirCollisionRule == Team.CollisionRule.NEVER || (input instanceof Player && !io.papermc.paper.configuration.GlobalConfiguration.get().collisions.enablePlayerCollisions)) { // Paper - Configurable player collision return false; } else { - boolean flag = team != null && team.isAlliedTo(team1); + boolean sameTeam = ownTeam != null && ownTeam.isAlliedTo(theirTeam); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/EntityType.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/EntityType.java.patch index 54535b61d022..849af41b4e84 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/EntityType.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/EntityType.java.patch @@ -1,84 +1,100 @@ --- a/net/minecraft/world/entity/EntityType.java +++ b/net/minecraft/world/entity/EntityType.java -@@ -189,6 +_,7 @@ +@@ -187,6 +_,7 @@ import org.slf4j.Logger; - public class EntityType implements FeatureElement, EntityTypeTest { + public class EntityType implements EntityTypeTest, FeatureElement { + private static final boolean DEBUG_ENTITIES_WITH_INVALID_IDS = Boolean.getBoolean("paper.debugEntitiesWithInvalidIds"); // Paper - Add logging for debugging entity tags with invalid ids private static final Logger LOGGER = LogUtils.getLogger(); private final Holder.Reference> builtInRegistryHolder = BuiltInRegistries.ENTITY_TYPE.createIntrusiveHolder(this); public static final Codec> CODEC = BuiltInRegistries.ENTITY_TYPE.byNameCodec(); -@@ -1290,6 +_,22 @@ - boolean shouldOffsetY, - boolean shouldOffsetYMore +@@ -1288,6 +_,22 @@ + final boolean tryMoveDown, + final boolean movedUp ) { + // CraftBukkit start -+ return this.spawn(level, spawnedFrom, owner, pos, spawnReason, shouldOffsetY, shouldOffsetYMore, spawnReason == EntitySpawnReason.DISPENSER ? org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DISPENSE_EGG : org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); // Paper - use correct spawn reason for dispenser spawn eggs ++ return this.spawn(level, itemStack, user, spawnPos, spawnReason, tryMoveDown, movedUp, spawnReason == EntitySpawnReason.DISPENSER ? org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DISPENSE_EGG : org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); // Paper - use correct spawn reason for dispenser spawn eggs + } + + @Nullable + public T spawn( -+ ServerLevel level, -+ @Nullable ItemStack spawnedFrom, -+ @Nullable LivingEntity owner, -+ BlockPos pos, -+ EntitySpawnReason spawnReason, -+ boolean shouldOffsetY, -+ boolean shouldOffsetYMore, -+ org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason createSpawnReason ++ final ServerLevel level, ++ final @Nullable ItemStack itemStack, ++ final @Nullable LivingEntity user, ++ final BlockPos spawnPos, ++ final EntitySpawnReason spawnReason, ++ final boolean tryMoveDown, ++ final boolean movedUp, ++ final org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason createSpawnReason + ) { + // CraftBukkit end - Consumer consumer; - if (spawnedFrom != null) { - consumer = createDefaultStackConfig(level, spawnedFrom, owner); -@@ -1297,7 +_,7 @@ - consumer = entity -> {}; + Consumer postSpawnConfig; + if (itemStack != null) { + postSpawnConfig = createDefaultStackConfig(level, itemStack, user); +@@ -1295,7 +_,7 @@ + postSpawnConfig = entity -> {}; } -- return this.spawn(level, consumer, pos, spawnReason, shouldOffsetY, shouldOffsetYMore); -+ return this.spawn(level, consumer, pos, spawnReason, shouldOffsetY, shouldOffsetYMore, createSpawnReason); // CraftBukkit +- return this.spawn(level, postSpawnConfig, spawnPos, spawnReason, tryMoveDown, movedUp); ++ return this.spawn(level, postSpawnConfig, spawnPos, spawnReason, tryMoveDown, movedUp, createSpawnReason); // CraftBukkit } - public static Consumer createDefaultStackConfig(Level level, ItemStack stack, @Nullable LivingEntity owner) { -@@ -1314,19 +_,54 @@ - - public static Consumer appendCustomEntityStackConfig(Consumer consumer, Level level, ItemStack stack, @Nullable LivingEntity owner) { - TypedEntityData> typedEntityData = stack.get(DataComponents.ENTITY_DATA); -- return typedEntityData != null ? consumer.andThen(entity -> updateCustomEntityTag(level, owner, entity, typedEntityData)) : consumer; + public static Consumer createDefaultStackConfig(final Level level, final ItemStack itemStack, final @Nullable LivingEntity user) { +@@ -1316,11 +_,30 @@ + final Consumer initialConfig, final Level level, final ItemStack itemStack, final @Nullable LivingEntity user + ) { + TypedEntityData> entityData = itemStack.get(DataComponents.ENTITY_DATA); +- return entityData != null ? initialConfig.andThen(entity -> updateCustomEntityTag(level, user, entity, entityData)) : initialConfig; + // CraftBukkit start - SPIGOT-5665 -+ return typedEntityData != null ? consumer.andThen(entity -> { ++ return entityData != null ? initialConfig.andThen(entity -> { + try { -+ updateCustomEntityTag(level, owner, entity, typedEntityData); ++ updateCustomEntityTag(level, user, entity, entityData); + } catch (Throwable t) { + LOGGER.warn("Error loading spawn egg NBT", t); + } -+ }) : consumer; ++ }) : initialConfig; + // CraftBukkit end } - public @Nullable T spawn(ServerLevel level, BlockPos pos, EntitySpawnReason spawnReason) { -- return this.spawn(level, null, pos, spawnReason, false, false); + public @Nullable T spawn(final ServerLevel level, final BlockPos spawnPos, final EntitySpawnReason spawnReason) { +- return this.spawn(level, null, spawnPos, spawnReason, false, false); + // CraftBukkit start -+ return this.spawn(level, pos, spawnReason, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); ++ return this.spawn(level, spawnPos, spawnReason, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); + } -+ public @Nullable T spawn(ServerLevel level, BlockPos pos, EntitySpawnReason spawnReason, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason creatureSpawnReason) { -+ return this.spawn(level, null, pos, spawnReason, false, false, creatureSpawnReason); ++ ++ public @Nullable T spawn( ++ final ServerLevel level, ++ final BlockPos spawnPos, ++ final EntitySpawnReason spawnReason, ++ final org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason creatureSpawnReason ++ ) { ++ return this.spawn(level, null, spawnPos, spawnReason, false, false, creatureSpawnReason); + // CraftBukkit End } public @Nullable T spawn( - ServerLevel level, @Nullable Consumer consumer, BlockPos pos, EntitySpawnReason spawnReason, boolean shouldOffsetY, boolean shouldOffsetYMore +@@ -1331,9 +_,39 @@ + final boolean tryMoveDown, + final boolean movedUp ) { + // CraftBukkit start -+ return this.spawn(level, consumer, pos, spawnReason, shouldOffsetY, shouldOffsetYMore, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); ++ return this.spawn(level, postSpawnConfig, spawnPos, spawnReason, tryMoveDown, movedUp, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); + } + + @Nullable -+ public T spawn(ServerLevel level, @Nullable Consumer consumer, BlockPos pos, EntitySpawnReason spawnReason, boolean shouldOffsetY, boolean shouldOffsetYMore, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason creatureSpawnReason) { ++ public T spawn( ++ final ServerLevel level, ++ final @Nullable Consumer postSpawnConfig, ++ final BlockPos spawnPos, ++ final EntitySpawnReason spawnReason, ++ final boolean tryMoveDown, ++ final boolean movedUp, ++ final org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason creatureSpawnReason ++ ) { + // CraftBukkit end + // Paper start - PreCreatureSpawnEvent + com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent( -+ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level), ++ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(spawnPos, level), + org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(this), + creatureSpawnReason + ); @@ -86,7 +102,7 @@ + return null; + } + // Paper end - PreCreatureSpawnEvent - T entity = this.create(level, consumer, pos, spawnReason, shouldOffsetY, shouldOffsetYMore); + T entity = this.create(level, postSpawnConfig, spawnPos, spawnReason, tryMoveDown, movedUp); if (entity != null) { - level.addFreshEntityWithPassengers(entity); + // CraftBukkit start @@ -98,12 +114,12 @@ if (entity instanceof Mob mob) { mob.playAmbientSound(); } -@@ -1382,6 +_,13 @@ +@@ -1396,6 +_,13 @@ if (level.isClientSide() || !entity.getType().onlyOpCanSetNbt() - || owner instanceof Player player && server.getPlayerList().isOp(player.nameAndId())) { + || user instanceof Player player && server.getPlayerList().isOp(player.nameAndId())) { + // Paper start - filter out protected tags -+ if (owner == null || !owner.getBukkitEntity().hasPermission("minecraft.nbt.place")) { ++ if (user == null || !user.getBukkitEntity().hasPermission("minecraft.nbt.place")) { + for (net.minecraft.commands.arguments.NbtPathArgument.NbtPath tag : level.paperConfig().entities.spawning.filteredEntityTagNbtPaths) { + tag.remove(entityData.getUnsafe()); + } @@ -112,18 +128,18 @@ entityData.loadInto(entity); } } -@@ -1452,10 +_,28 @@ +@@ -1466,10 +_,28 @@ } - public static Optional create(ValueInput input, Level level, EntitySpawnReason spawnReason) { + public static Optional create(final ValueInput input, final Level level, final EntitySpawnReason reason) { + // Paper start - Don't fire sync event during generation -+ return create(input, level, spawnReason, false); ++ return create(input, level, reason, false); + } + -+ public static Optional create(ValueInput input, Level level, EntitySpawnReason spawnReason, boolean generation) { ++ public static Optional create(final ValueInput input, final Level level, final EntitySpawnReason reason, final boolean generation) { + // Paper end - Don't fire sync event during generation return Util.ifElse( - by(input).map(entityType -> entityType.create(level, spawnReason)), + by(input).map(type -> type.create(level, reason)), - entity -> entity.load(input), - () -> LOGGER.warn("Skipping Entity with id {}", input.getStringOr("id", "[invalid]")) + // Paper start - Don't fire sync event during generation @@ -143,7 +159,7 @@ ); } -@@ -1606,8 +_,23 @@ +@@ -1616,8 +_,23 @@ return this.builtInRegistryHolder; } @@ -167,4 +183,4 @@ + // Paper end } - private static EntityType.EntityFactory boatFactory(Supplier boatItemGetter) { + private static EntityType.EntityFactory boatFactory(final Supplier boatItem) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ExperienceOrb.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ExperienceOrb.java.patch index 77020cd48c6a..c63e98f57f82 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ExperienceOrb.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ExperienceOrb.java.patch @@ -33,22 +33,22 @@ + } + // Paper end + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - overload ctor - public ExperienceOrb(Level level, double x, double y, double z, int value) { + public ExperienceOrb(final Level level, final double x, final double y, final double z, final int value) { - this(level, new Vec3(x, y, z), Vec3.ZERO, value); + // Paper start - add reasons for orbs + this(level, x, y, z, value, null, null, null); + } -+ public ExperienceOrb(Level level, double x, double y, double z, int value, org.bukkit.entity.ExperienceOrb.@Nullable SpawnReason reason, @Nullable Entity triggerId, @Nullable Entity sourceId) { ++ public ExperienceOrb(final Level level, final double x, final double y, final double z, final int value, org.bukkit.entity.ExperienceOrb.@Nullable SpawnReason reason, final @Nullable Entity triggerId, final @Nullable Entity sourceId) { + this(level, new Vec3(x, y, z), Vec3.ZERO, value, reason, triggerId, sourceId); + // Paper end - add reasons for orbs } + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - overload ctor - public ExperienceOrb(Level level, Vec3 pos, Vec3 direction, int value) { + public ExperienceOrb(final Level level, final Vec3 pos, final Vec3 roughly, final int value) { + // Paper start - add reasons for orbs -+ this(level, pos, direction, value, null, null, null); ++ this(level, pos, roughly, value, null, null, null); + } -+ public ExperienceOrb(Level level, Vec3 pos, Vec3 direction, int value, org.bukkit.entity.ExperienceOrb.@Nullable SpawnReason reason, @Nullable Entity triggerId, @Nullable Entity sourceId) { ++ public ExperienceOrb(final Level level, final Vec3 pos, final Vec3 roughly, final int value, org.bukkit.entity.ExperienceOrb.@Nullable SpawnReason reason, final @Nullable Entity triggerId, final @Nullable Entity sourceId) { + // Paper end - add reasons for orbs this(EntityType.EXPERIENCE_ORB, level); + // Paper start - add reasons for orbs @@ -97,45 +97,45 @@ + + if (this.followingPlayer != null && !cancelled) { + // CraftBukkit end - Vec3 vec3 = new Vec3( + Vec3 delta = new Vec3( this.followingPlayer.getX() - this.getX(), this.followingPlayer.getY() + this.followingPlayer.getEyeHeight() / 2.0 - this.getY(), -@@ -192,18 +_,24 @@ +@@ -191,18 +_,24 @@ } - public static void awardWithDirection(ServerLevel level, Vec3 pos, Vec3 direction, int amount) { + public static void awardWithDirection(final ServerLevel level, final Vec3 pos, final Vec3 roughDirection, int amount) { + // Paper start - add reason to orbs -+ awardWithDirection(level, pos, direction, amount, null, null, null); ++ awardWithDirection(level, pos, roughDirection, amount, null, null, null); + } -+ public static void awardWithDirection(ServerLevel level, Vec3 pos, Vec3 direction, int amount, org.bukkit.entity.ExperienceOrb.@Nullable SpawnReason reason, @Nullable Entity triggerId, @Nullable Entity sourceId) { ++ public static void awardWithDirection(final ServerLevel level, final Vec3 pos, final Vec3 roughDirection, int amount, org.bukkit.entity.ExperienceOrb.@Nullable SpawnReason reason, final @Nullable Entity triggerId, final @Nullable Entity sourceId) { + // Paper end - add reason to orbs while (amount > 0) { - int experienceValue = getExperienceValue(amount); - amount -= experienceValue; - if (!tryMergeToExisting(level, pos, experienceValue)) { -- level.addFreshEntity(new ExperienceOrb(level, pos, direction, experienceValue)); -+ level.addFreshEntity(new ExperienceOrb(level, pos, direction, experienceValue, reason, triggerId, sourceId)); // Paper - add reason to orbs + int newCount = getExperienceValue(amount); + amount -= newCount; + if (!tryMergeToExisting(level, pos, newCount)) { +- level.addFreshEntity(new ExperienceOrb(level, pos, roughDirection, newCount)); ++ level.addFreshEntity(new ExperienceOrb(level, pos, roughDirection, newCount, reason, triggerId, sourceId)); // Paper - add reason to orbs } } } - private static boolean tryMergeToExisting(ServerLevel level, Vec3 pos, int amount) { + private static boolean tryMergeToExisting(final ServerLevel level, final Vec3 pos, final int value) { + // Paper - TODO some other event for this kind of merge - AABB aabb = AABB.ofSize(pos, 1.0, 1.0, 1.0); -- int randomInt = level.getRandom().nextInt(40); -+ int randomInt = level.getRandom().nextInt(io.papermc.paper.configuration.GlobalConfiguration.get().misc.xpOrbGroupsPerArea.or(ORB_GROUPS_PER_AREA)); // Paper - Configure how many orb groups per area - List entities = level.getEntities( - EntityTypeTest.forClass(ExperienceOrb.class), aabb, experienceOrb1 -> canMerge(experienceOrb1, randomInt, amount) - ); -@@ -222,13 +_,18 @@ + AABB box = AABB.ofSize(pos, 1.0, 1.0, 1.0); +- int id = level.getRandom().nextInt(40); ++ int id = level.getRandom().nextInt(io.papermc.paper.configuration.GlobalConfiguration.get().misc.xpOrbGroupsPerArea.or(ORB_GROUPS_PER_AREA)); // Paper - Configure how many orb groups per area + List orbs = level.getEntities(EntityTypeTest.forClass(ExperienceOrb.class), box, orbx -> canMerge(orbx, id, value)); + if (!orbs.isEmpty()) { + ExperienceOrb orb = orbs.get(0); +@@ -219,13 +_,18 @@ } - private static boolean canMerge(ExperienceOrb orb, int amount, int other) { -- return !orb.isRemoved() && (orb.getId() - amount) % 40 == 0 && orb.getValue() == other; -+ return !orb.isRemoved() && (orb.getId() - amount) % io.papermc.paper.configuration.GlobalConfiguration.get().misc.xpOrbGroupsPerArea.or(ORB_GROUPS_PER_AREA) == 0 && orb.getValue() == other; // Paper - Configure how many orbs will merge together + private static boolean canMerge(final ExperienceOrb orb, final int id, final int value) { +- return !orb.isRemoved() && (orb.getId() - id) % 40 == 0 && orb.getValue() == value; ++ return !orb.isRemoved() && (orb.getId() - id) % io.papermc.paper.configuration.GlobalConfiguration.get().misc.xpOrbGroupsPerArea.or(ORB_GROUPS_PER_AREA) == 0 && orb.getValue() == value; // Paper - Configure how many orbs will merge together } - private void merge(ExperienceOrb orb) { + private void merge(final ExperienceOrb orb) { + // Paper start - call orb merge event + if (!new com.destroystokyo.paper.event.entity.ExperienceOrbMergeEvent((org.bukkit.entity.ExperienceOrb) this.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) orb.getBukkitEntity()).callEvent()) { + return; @@ -148,17 +148,17 @@ } private void setUnderwaterMovement() { -@@ -253,7 +_,7 @@ +@@ -250,7 +_,7 @@ this.markHurt(); - this.health = (int)(this.health - amount); + this.health = (int)(this.health - damage); if (this.health <= 0) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause } return true; -@@ -264,32 +_,34 @@ - protected void addAdditionalSaveData(ValueOutput output) { +@@ -261,32 +_,34 @@ + protected void addAdditionalSaveData(final ValueOutput output) { output.putShort("Health", (short)this.health); output.putShort("Age", (short)this.age); - output.putShort("Value", (short)this.getValue()); @@ -168,7 +168,7 @@ } @Override - protected void readAdditionalSaveData(ValueInput input) { + protected void readAdditionalSaveData(final ValueInput input) { this.health = input.getShortOr("Health", (short)5); this.age = input.getShortOr("Age", (short)0); - this.setValue(input.getShortOr("Value", (short)0)); @@ -178,17 +178,17 @@ } @Override - public void playerTouch(Player entity) { - if (entity instanceof ServerPlayer serverPlayer) { -- if (entity.takeXpDelay == 0) { -- entity.takeXpDelay = 2; -+ if (entity.takeXpDelay == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(serverPlayer.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper - PlayerPickupExperienceEvent -+ entity.takeXpDelay = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerXpCooldownEvent(entity, 2, org.bukkit.event.player.PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entity.takeXpDelay = 2; - entity.take(this, 1); - int i = this.repairPlayerItems(serverPlayer, this.getValue()); - if (i > 0) { -- entity.giveExperiencePoints(i); -+ entity.giveExperiencePoints(org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerExpChangeEvent(entity, this, i).getAmount()); // CraftBukkit - i -> event.getAmount() // Paper - supply experience orb + public void playerTouch(final Player player) { + if (player instanceof ServerPlayer serverPlayer) { +- if (player.takeXpDelay == 0) { +- player.takeXpDelay = 2; ++ if (player.takeXpDelay == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(serverPlayer.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper - PlayerPickupExperienceEvent ++ player.takeXpDelay = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerXpCooldownEvent(player, 2, org.bukkit.event.player.PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entity.takeXpDelay = 2; + player.take(this, 1); + int remaining = this.repairPlayerItems(serverPlayer, this.getValue()); + if (remaining > 0) { +- player.giveExperiencePoints(remaining); ++ player.giveExperiencePoints(org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerExpChangeEvent(player, this, remaining).getAmount()); // CraftBukkit - remaining -> event.getAmount() // Paper - supply experience orb } this.count--; @@ -198,49 +198,49 @@ } } } -@@ -303,9 +_,19 @@ - ItemStack itemStack = randomItemWith.get().itemStack(); - int i = EnchantmentHelper.modifyDurabilityToRepairFromXp(player.level(), itemStack, value); - int min = Math.min(i, itemStack.getDamageValue()); +@@ -298,9 +_,19 @@ + ItemStack itemStack = selected.get().itemStack(); + int toRepairFromXpAmount = EnchantmentHelper.modifyDurabilityToRepairFromXp(player.level(), itemStack, amount); + int repair = Math.min(toRepairFromXpAmount, itemStack.getDamageValue()); + // CraftBukkit start + // Paper start - mending event -+ final int consumedExperience = min > 0 ? min * value / i : 0; -+ org.bukkit.event.player.PlayerItemMendEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemMendEvent(player, this, itemStack, randomItemWith.get().inSlot(), min, consumedExperience); ++ final int consumedExperience = repair > 0 ? repair * amount / toRepairFromXpAmount : 0; ++ org.bukkit.event.player.PlayerItemMendEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemMendEvent(player, this, itemStack, selected.get().inSlot(), repair, consumedExperience); + // Paper end - mending event -+ min = event.getRepairAmount(); ++ repair = event.getRepairAmount(); + if (event.isCancelled()) { -+ return value; ++ return amount; + } + // CraftBukkit end - itemStack.setDamageValue(itemStack.getDamageValue() - min); - if (min > 0) { -- int i1 = value - min * value / i; -+ int i1 = value - min * value / i; // Paper - diff on change - expand PlayerMendEvents - if (i1 > 0) { - return this.repairPlayerItems(player, i1); + itemStack.setDamageValue(itemStack.getDamageValue() - repair); + if (repair > 0) { +- int remaining = amount - repair * amount / toRepairFromXpAmount; ++ int remaining = amount - repair * amount / toRepairFromXpAmount; // Paper - diff on change - expand PlayerMendEvents + if (remaining > 0) { + return this.repairPlayerItems(player, remaining); } -@@ -351,6 +_,24 @@ +@@ -346,6 +_,24 @@ } - public static int getExperienceValue(int expValue) { + public static int getExperienceValue(final int maxValue) { + // CraftBukkit start -+ if (expValue > 162670129) return expValue - 100000; -+ if (expValue > 81335063) return 81335063; -+ if (expValue > 40667527) return 40667527; -+ if (expValue > 20333759) return 20333759; -+ if (expValue > 10166857) return 10166857; -+ if (expValue > 5083423) return 5083423; -+ if (expValue > 2541701) return 2541701; -+ if (expValue > 1270849) return 1270849; -+ if (expValue > 635413) return 635413; -+ if (expValue > 317701) return 317701; -+ if (expValue > 158849) return 158849; -+ if (expValue > 79423) return 79423; -+ if (expValue > 39709) return 39709; -+ if (expValue > 19853) return 19853; -+ if (expValue > 9923) return 9923; -+ if (expValue > 4957) return 4957; ++ if (maxValue > 162670129) return maxValue - 100000; ++ if (maxValue > 81335063) return 81335063; ++ if (maxValue > 40667527) return 40667527; ++ if (maxValue > 20333759) return 20333759; ++ if (maxValue > 10166857) return 10166857; ++ if (maxValue > 5083423) return 5083423; ++ if (maxValue > 2541701) return 2541701; ++ if (maxValue > 1270849) return 1270849; ++ if (maxValue > 635413) return 635413; ++ if (maxValue > 317701) return 317701; ++ if (maxValue > 158849) return 158849; ++ if (maxValue > 79423) return 79423; ++ if (maxValue > 39709) return 39709; ++ if (maxValue > 19853) return 19853; ++ if (maxValue > 9923) return 9923; ++ if (maxValue > 4957) return 4957; + // CraftBukkit end - if (expValue >= 2477) { + if (maxValue >= 2477) { return 2477; - } else if (expValue >= 1237) { + } else if (maxValue >= 1237) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/InsideBlockEffectApplier.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/InsideBlockEffectApplier.java.patch index 27a6b68777b2..d459738f55c7 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/InsideBlockEffectApplier.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/InsideBlockEffectApplier.java.patch @@ -7,40 +7,40 @@ - private final Set effectsInStep = EnumSet.noneOf(InsideBlockEffectType.class); + private final Map> effectsInStep = new java.util.EnumMap<>(InsideBlockEffectType.class); // Paper - track position inside effect was triggered on private final Map>> beforeEffectsInStep = Util.makeEnumMap( - InsideBlockEffectType.class, insideBlockEffectType -> new ArrayList<>() + InsideBlockEffectType.class, type -> new ArrayList<>() ); @@ -42,7 +_,8 @@ private final List> finalEffects = new ArrayList<>(); private int lastStep = -1; -- public void advanceStep(int step) { -+ public void advanceStep(int step, net.minecraft.core.BlockPos pos) { // Paper - track position inside effect was triggered on +- public void advanceStep(final int step) { ++ public void advanceStep(final int step, net.minecraft.core.BlockPos pos) { // Paper - track position inside effect was triggered on + this.currentBlockPos = pos; // Paper - track position inside effect was triggered on if (this.lastStep != step) { this.lastStep = step; this.flushStep(); @@ -69,8 +_,8 @@ - List> list = this.beforeEffectsInStep.get(insideBlockEffectType); - this.finalEffects.addAll(list); - list.clear(); -- if (this.effectsInStep.remove(insideBlockEffectType)) { -- this.finalEffects.add(insideBlockEffectType.effect()); -+ if (this.effectsInStep.remove(insideBlockEffectType) instanceof final Consumer recordedEffect) { // Paper - track position inside effect was triggered on - better than null check to avoid diff. + List> beforeEffects = this.beforeEffectsInStep.get(type); + this.finalEffects.addAll(beforeEffects); + beforeEffects.clear(); +- if (this.effectsInStep.remove(type)) { +- this.finalEffects.add(type.effect()); ++ if (this.effectsInStep.remove(type) instanceof final Consumer recordedEffect) { // Paper - track position inside effect was triggered on - better than null check to avoid diff. + this.finalEffects.add(recordedEffect); // Paper - track position inside effect was triggered on } - List> list1 = this.afterEffectsInStep.get(insideBlockEffectType); + List> afterEffects = this.afterEffectsInStep.get(type); @@ -81,7 +_,7 @@ @Override - public void apply(InsideBlockEffectType type) { + public void apply(final InsideBlockEffectType type) { - this.effectsInStep.add(type); -+ this.effectsInStep.put(type, recorded(type)); // Paper - track position inside effect was triggered on ++ this.effectsInStep.put(type, recorded(type)); // Paper - track position inside effect was triggered on } @Override @@ -93,5 +_,24 @@ - public void runAfter(InsideBlockEffectType type, Consumer effect) { + public void runAfter(final InsideBlockEffectType type, final Consumer effect) { this.afterEffectsInStep.get(type).add(effect); } + diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Interaction.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Interaction.java.patch index 64ff18b39efb..836e158702a1 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/Interaction.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/Interaction.java.patch @@ -2,11 +2,11 @@ +++ b/net/minecraft/world/entity/Interaction.java @@ -99,9 +_,16 @@ @Override - public boolean skipAttackInteraction(Entity entity) { - if (entity instanceof Player player) { + public boolean skipAttackInteraction(final Entity source) { + if (source instanceof Player player) { + // CraftBukkit start -+ DamageSource source = player.damageSources().generic().eventEntityDamager(entity); -+ org.bukkit.event.entity.EntityDamageEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNonLivingEntityDamageEvent(this, source, 1.0F, false); ++ DamageSource damageSource = player.damageSources().generic().eventEntityDamager(source); ++ org.bukkit.event.entity.EntityDamageEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNonLivingEntityDamageEvent(this, damageSource, 1.0F, false); + if (event.isCancelled()) { + return true; + } @@ -14,7 +14,7 @@ this.attack = new Interaction.PlayerAction(player.getUUID(), this.level().getGameTime()); if (player instanceof ServerPlayer serverPlayer) { - CriteriaTriggers.PLAYER_HURT_ENTITY.trigger(serverPlayer, this, player.damageSources().generic(), 1.0F, 1.0F, false); -+ CriteriaTriggers.PLAYER_HURT_ENTITY.trigger(serverPlayer, this, source, 1.0F, (float) event.getFinalDamage(), false); // CraftBukkit ++ CriteriaTriggers.PLAYER_HURT_ENTITY.trigger(serverPlayer, this, damageSource, 1.0F, (float) event.getFinalDamage(), false); // CraftBukkit } return !this.getResponse(); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Leashable.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Leashable.java.patch index 568f7284c189..b9b645f10f4b 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/Leashable.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/Leashable.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/entity/Leashable.java +++ b/net/minecraft/world/entity/Leashable.java -@@ -79,6 +_,11 @@ +@@ -80,6 +_,11 @@ } - default void writeLeashData(ValueOutput output, Leashable.@Nullable LeashData leashData) { + default void writeLeashData(final ValueOutput output, final Leashable.@Nullable LeashData leashData) { + // CraftBukkit start - SPIGOT-7487: Don't save (and possible drop) leash, when the holder was removed by a plugin + if (leashData != null && leashData.leashHolder != null && leashData.leashHolder.pluginRemoved) { + return; @@ -12,7 +12,7 @@ output.storeNullable("leash", Leashable.LeashData.CODEC, leashData); } -@@ -98,7 +_,9 @@ +@@ -99,7 +_,9 @@ } if (entity.tickCount > 100) { @@ -22,17 +22,17 @@ entity.setLeashData(null); } } -@@ -122,7 +_,9 @@ +@@ -123,7 +_,9 @@ entity.onLeashRemoved(); - if (entity.level() instanceof ServerLevel serverLevel) { - if (dropItem) { + if (entity.level() instanceof ServerLevel level) { + if (dropLead) { + entity.forceDrops = true; // CraftBukkit - entity.spawnAtLocation(serverLevel, Items.LEAD); + entity.spawnAtLocation(level, Items.LEAD); + entity.forceDrops = false; // CraftBukkit } - if (broadcastPacket) { -@@ -142,7 +_,15 @@ + if (sendPacket) { +@@ -143,7 +_,15 @@ if (leashData != null && leashData.leashHolder != null) { if (!entity.canInteractWithLevel() || !leashData.leashHolder.canInteractWithLevel()) { @@ -49,17 +49,17 @@ entity.dropLeash(); } else { entity.removeLeash(); -@@ -153,8 +_,7 @@ +@@ -154,8 +_,7 @@ if (leashHolder != null && leashHolder.level() == entity.level()) { - double d = entity.leashDistanceTo(leashHolder); + double distanceTo = entity.leashDistanceTo(leashHolder); entity.whenLeashedTo(leashHolder); -- if (d > entity.leashSnapDistance()) { +- if (distanceTo > entity.leashSnapDistance()) { - level.playSound(null, leashHolder.getX(), leashHolder.getY(), leashHolder.getZ(), SoundEvents.LEAD_BREAK, SoundSource.NEUTRAL, 1.0F, 1.0F); -+ if (d > entity.leashSnapDistanceOrConfig()) { // Paper - Configurable max leash distance ++ if (distanceTo > entity.leashSnapDistanceOrConfig()) { // Paper - Configurable max leash distance entity.leashTooFarBehaviour(); - } else if (d > entity.leashElasticDistance() - leashHolder.getBbWidth() - entity.getBbWidth() + } else if (distanceTo > entity.leashElasticDistance() - leashHolder.getBbWidth() - entity.getBbWidth() && entity.checkElasticInteractions(leashHolder, leashData)) { -@@ -174,6 +_,12 @@ +@@ -175,6 +_,12 @@ entity.checkFallDistanceAccumulation(); } @@ -72,7 +72,7 @@ default double leashSnapDistance() { return 12.0; } -@@ -195,7 +_,25 @@ +@@ -196,7 +_,25 @@ } default void leashTooFarBehaviour() { @@ -98,4 +98,4 @@ + // Paper end - Expand EntityUnleashEvent } - default void closeRangeLeashBehaviour(Entity entity) { + default void closeRangeLeashBehaviour(final Entity leashHolder) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/LightningBolt.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/LightningBolt.java.patch index ebe1f9fbce02..5af35a432823 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/LightningBolt.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/LightningBolt.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/LightningBolt.java +++ b/net/minecraft/world/entity/LightningBolt.java -@@ -39,6 +_,7 @@ +@@ -40,6 +_,7 @@ private @Nullable ServerPlayer cause; private final Set hitEntities = Sets.newHashSet(); private int blocksSetOnFire; + public boolean isEffect; // Paper - Properly handle lightning effects api - public LightningBolt(EntityType type, Level level) { + public LightningBolt(final EntityType type, final Level level) { super(type, level); -@@ -75,7 +_,7 @@ +@@ -76,7 +_,7 @@ @Override public void tick() { super.tick(); @@ -17,7 +17,7 @@ if (this.level().isClientSide()) { this.level() .playLocalSound( -@@ -106,7 +_,7 @@ +@@ -107,7 +_,7 @@ } this.powerLightningRod(); @@ -26,7 +26,7 @@ this.gameEvent(GameEvent.LIGHTNING_STRIKE); } } -@@ -129,7 +_,7 @@ +@@ -130,7 +_,7 @@ } } @@ -35,7 +35,7 @@ } else if (this.life < -this.random.nextInt(10)) { this.flashes--; this.life = 1; -@@ -138,10 +_,10 @@ +@@ -139,10 +_,10 @@ } } @@ -48,28 +48,28 @@ List entities = this.level() .getEntities( this, -@@ -167,34 +_,43 @@ +@@ -168,34 +_,43 @@ } - private void spawnFire(int extraIgnitions) { -- if (!this.visualOnly && this.level() instanceof ServerLevel serverLevel) { -+ if (!this.visualOnly && !this.isEffect && this.level() instanceof ServerLevel serverLevel) { // Paper - Properly handle lightning effects api + private void spawnFire(final int additionalSources) { +- if (!this.visualOnly && this.level() instanceof ServerLevel level) { ++ if (!this.visualOnly && !this.isEffect && this.level() instanceof ServerLevel level) { // Paper - Properly handle lightning effects api BlockPos var7 = this.blockPosition(); - if (serverLevel.canSpreadFireAround(var7)) { - BlockState state = BaseFireBlock.getState(serverLevel, var7); - if (serverLevel.getBlockState(var7).isAir() && state.canSurvive(serverLevel, var7)) { + if (level.canSpreadFireAround(var7)) { + BlockState fire = BaseFireBlock.getState(level, var7); + if (level.getBlockState(var7).isAir() && fire.canSurvive(level, var7)) { + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level(), var7, this).isCancelled()) { // CraftBukkit - serverLevel.setBlockAndUpdate(var7, state); + level.setBlockAndUpdate(var7, fire); this.blocksSetOnFire++; + } // CraftBukkit } - for (int i = 0; i < extraIgnitions; i++) { - BlockPos blockPos1 = var7.offset(this.random.nextInt(3) - 1, this.random.nextInt(3) - 1, this.random.nextInt(3) - 1); - state = BaseFireBlock.getState(serverLevel, blockPos1); - if (serverLevel.getBlockState(blockPos1).isAir() && state.canSurvive(serverLevel, blockPos1)) { -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level(), blockPos1, this).isCancelled()) { // CraftBukkit - serverLevel.setBlockAndUpdate(blockPos1, state); + for (int i = 0; i < additionalSources; i++) { + BlockPos nearbyPos = var7.offset(this.random.nextInt(3) - 1, this.random.nextInt(3) - 1, this.random.nextInt(3) - 1); + fire = BaseFireBlock.getState(level, nearbyPos); + if (level.getBlockState(nearbyPos).isAir() && fire.canSurvive(level, nearbyPos)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level(), nearbyPos, this).isCancelled()) { // CraftBukkit + level.setBlockAndUpdate(nearbyPos, fire); this.blocksSetOnFire++; + } // CraftBukkit } @@ -78,60 +78,62 @@ } } -- private static void clearCopperOnLightningStrike(Level level, BlockPos pos) { -+ private static void clearCopperOnLightningStrike(Level level, BlockPos pos, Entity lightning) { // Paper - Call EntityChangeBlockEvent - BlockState blockState = level.getBlockState(pos); - boolean flag = HoneycombItem.WAX_OFF_BY_BLOCK.get().get(blockState.getBlock()) != null; - boolean flag1 = blockState.getBlock() instanceof WeatheringCopper; - if (flag1 || flag) { - if (flag1) { -- level.setBlockAndUpdate(pos, WeatheringCopper.getFirst(level.getBlockState(pos))); +- private static void clearCopperOnLightningStrike(final Level level, final BlockPos struckPos) { ++ private static void clearCopperOnLightningStrike(final Level level, final BlockPos struckPos, final Entity lightning) { // Paper - Call EntityChangeBlockEvent + BlockState struckState = level.getBlockState(struckPos); + boolean isWaxed = HoneycombItem.WAX_OFF_BY_BLOCK.get().get(struckState.getBlock()) != null; + boolean isWeatheringCopper = struckState.getBlock() instanceof WeatheringCopper; + if (isWeatheringCopper || isWaxed) { + if (isWeatheringCopper) { +- level.setBlockAndUpdate(struckPos, WeatheringCopper.getFirst(level.getBlockState(struckPos))); + // Paper start - Call EntityChangeBlockEvent -+ BlockState newBlockState = WeatheringCopper.getFirst(level.getBlockState(pos)); -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(lightning, pos, newBlockState)) { -+ level.setBlockAndUpdate(pos, newBlockState); ++ BlockState newBlockState = WeatheringCopper.getFirst(level.getBlockState(struckPos)); ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(lightning, struckPos, newBlockState)) { ++ level.setBlockAndUpdate(struckPos, newBlockState); + } + // Paper end - Call EntityChangeBlockEvent } - BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); -@@ -202,16 +_,16 @@ + BlockPos.MutableBlockPos workPos = struckPos.mutable(); +@@ -204,18 +_,18 @@ - for (int i1 = 0; i1 < i; i1++) { - int i2 = level.random.nextInt(8) + 1; -- randomWalkCleaningCopper(level, pos, mutableBlockPos, i2); -+ randomWalkCleaningCopper(level, pos, mutableBlockPos, i2, lightning); // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent + for (int strike = 0; strike < strikesCount; strike++) { + int stepCount = random.nextInt(8) + 1; +- randomWalkCleaningCopper(level, struckPos, workPos, stepCount); ++ randomWalkCleaningCopper(level, struckPos, workPos, stepCount, lightning); // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent } } } -- private static void randomWalkCleaningCopper(Level level, BlockPos pos, BlockPos.MutableBlockPos mutable, int steps) { -+ private static void randomWalkCleaningCopper(Level level, BlockPos pos, BlockPos.MutableBlockPos mutable, int steps, Entity lightning) { // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent - mutable.set(pos); + private static void randomWalkCleaningCopper( +- final Level level, final BlockPos originalStrikePos, final BlockPos.MutableBlockPos workPos, final int stepCount ++ final Level level, final BlockPos originalStrikePos, final BlockPos.MutableBlockPos workPos, final int stepCount, final Entity lightning // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent + ) { + workPos.set(originalStrikePos); - for (int i = 0; i < steps; i++) { -- Optional optional = randomStepCleaningCopper(level, mutable); -+ Optional optional = randomStepCleaningCopper(level, mutable, lightning); // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent - if (optional.isEmpty()) { + for (int step = 0; step < stepCount; step++) { +- Optional stepPos = randomStepCleaningCopper(level, workPos); ++ Optional stepPos = randomStepCleaningCopper(level, workPos, lightning); // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent + if (stepPos.isEmpty()) { break; } -@@ -220,11 +_,17 @@ +@@ -224,11 +_,17 @@ } } -- private static Optional randomStepCleaningCopper(Level level, BlockPos pos) { -+ private static Optional randomStepCleaningCopper(Level level, BlockPos pos, Entity lightning) { // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent - for (BlockPos blockPos : BlockPos.randomInCube(level.random, 10, pos, 1)) { - BlockState blockState = level.getBlockState(blockPos); - if (blockState.getBlock() instanceof WeatheringCopper) { -- WeatheringCopper.getPrevious(blockState).ifPresent(blockState1 -> level.setBlockAndUpdate(blockPos, blockState1)); +- private static Optional randomStepCleaningCopper(final Level level, final BlockPos pos) { ++ private static Optional randomStepCleaningCopper(final Level level, final BlockPos pos, final Entity lightning) { // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent + for (BlockPos candidate : BlockPos.randomInCube(level.getRandom(), 10, pos, 1)) { + BlockState state = level.getBlockState(candidate); + if (state.getBlock() instanceof WeatheringCopper) { +- WeatheringCopper.getPrevious(state).ifPresent(s -> level.setBlockAndUpdate(candidate, s)); + // Paper start - call EntityChangeBlockEvent -+ WeatheringCopper.getPrevious(blockState).ifPresent(blockState1 -> { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(lightning, blockPos, blockState1)) { -+ level.setBlockAndUpdate(blockPos, blockState1); ++ WeatheringCopper.getPrevious(state).ifPresent(s -> { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(lightning, candidate, s)) { ++ level.setBlockAndUpdate(candidate, s); + } + }); + // Paper end - call EntityChangeBlockEvent - level.levelEvent(LevelEvent.PARTICLES_ELECTRIC_SPARK, blockPos, -1); - return Optional.of(blockPos); + level.levelEvent(LevelEvent.PARTICLES_ELECTRIC_SPARK, candidate, -1); + return Optional.of(candidate); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch index e763a6ef5cad..e80001ef8d20 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java -@@ -143,6 +_,17 @@ +@@ -140,6 +_,17 @@ import org.jspecify.annotations.Nullable; import org.slf4j.Logger; @@ -18,10 +18,10 @@ public abstract class LivingEntity extends Entity implements Attackable, WaypointTransmitter { private static final Logger LOGGER = LogUtils.getLogger(); private static final String TAG_ACTIVE_EFFECTS = "active_effects"; -@@ -268,11 +_,25 @@ - ); - protected final EntityEquipment equipment; +@@ -269,11 +_,25 @@ private Waypoint.Icon locatorBarIcon = new Waypoint.Icon(); + public @Nullable Vec3 currentImpulseImpactPos; + public @Nullable Entity currentExplosionCause; + // CraftBukkit start + public int expToDrop; + public List drops = new java.util.ArrayList<>(); // Paper - Restore vanilla drops behavior @@ -35,7 +35,7 @@ + public int invulnerableDuration = LivingEntity.INVULNERABLE_DURATION; // Paper - configurable invulnerable duration + // CraftBukkit end - protected LivingEntity(EntityType type, Level level) { + protected LivingEntity(final EntityType type, final Level level) { super(type, level); this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type)); - this.setHealth(this.getMaxHealth()); @@ -45,22 +45,21 @@ this.equipment = this.createEquipment(); this.blocksBuilding = true; this.reapplyPosition(); -@@ -371,7 +_,13 @@ +@@ -365,7 +_,13 @@ - double d1 = Math.min(0.2F + d / 15.0, 2.5); - int i = (int)(150.0 * d1); -- serverLevel.sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, state), x, y1, z, i, 0.0, 0.0, 0.0, 0.15F); + double scale = Math.min(0.2F + power / 15.0, 2.5); + int particles = (int)(150.0 * scale); + // CraftBukkit start - visibility api + if (this instanceof ServerPlayer) { -+ serverLevel.sendParticlesSource((ServerPlayer) this, new BlockParticleOption(ParticleTypes.BLOCK, state), false, false, x, y1, z, i, 0.0, 0.0, 0.0, 0.15F); ++ level.sendParticlesSource((ServerPlayer)this, new BlockParticleOption(ParticleTypes.BLOCK, onState), false, false, x, y, z, particles, 0.0, 0.0, 0.0, 0.15F); + } else { -+ serverLevel.sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, state), x, y1, z, i, 0.0, 0.0, 0.0, 0.15F); + level.sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, onState), x, y, z, particles, 0.0, 0.0, 0.0, 0.15F); + } + // CraftBukkit end } } -@@ -556,7 +_,7 @@ +@@ -550,7 +_,7 @@ this.deathTime++; if (this.deathTime >= 20 && !this.level().isClientSide() && !this.isRemoved()) { this.level().broadcastEntityEvent(this, EntityEvent.POOF); @@ -69,7 +68,7 @@ } } -@@ -658,7 +_,7 @@ +@@ -652,7 +_,7 @@ } public boolean shouldDiscardFriction() { @@ -77,32 +76,32 @@ + return !this.frictionState.toBooleanOrElse(!this.discardFriction); // Paper - Friction API } - public void setDiscardFriction(boolean discardFriction) { -@@ -670,10 +_,15 @@ + public void setDiscardFriction(final boolean discardFriction) { +@@ -664,10 +_,15 @@ } - public void onEquipItem(EquipmentSlot slot, ItemStack oldItem, ItemStack newItem) { + public void onEquipItem(final EquipmentSlot slot, final ItemStack oldStack, final ItemStack stack) { + // CraftBukkit start -+ this.onEquipItem(slot, oldItem, newItem, false); ++ this.onEquipItem(slot, oldStack, stack, false); + } -+ public void onEquipItem(EquipmentSlot slot, ItemStack oldItem, ItemStack newItem, boolean silent) { ++ public void onEquipItem(final EquipmentSlot slot, final ItemStack oldStack, final ItemStack stack, final boolean silent) { + // CraftBukkit end if (!this.level().isClientSide() && !this.isSpectator()) { - if (!ItemStack.isSameItemSameComponents(oldItem, newItem) && !this.firstTick) { - Equippable equippable = newItem.get(DataComponents.EQUIPPABLE); + if (!ItemStack.isSameItemSameComponents(oldStack, stack) && !this.firstTick) { + Equippable equippable = stack.get(DataComponents.EQUIPPABLE); - if (!this.isSilent() && equippable != null && slot == equippable.slot()) { + if (!this.isSilent() && equippable != null && slot == equippable.slot() && !silent) { // CraftBukkit this.level() .playSeededSound( null, -@@ -700,12 +_,12 @@ +@@ -694,12 +_,12 @@ } @Override -- public void remove(Entity.RemovalReason reason) { -+ public void remove(Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause eventCause) { // CraftBukkit - add Bukkit remove cause - if ((reason == Entity.RemovalReason.KILLED || reason == Entity.RemovalReason.DISCARDED) && this.level() instanceof ServerLevel serverLevel) { - this.triggerOnDeathMobEffects(serverLevel, reason); +- public void remove(final Entity.RemovalReason reason) { ++ public void remove(final Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause eventCause) { // CraftBukkit - add Bukkit remove cause + if ((reason == Entity.RemovalReason.KILLED || reason == Entity.RemovalReason.DISCARDED) && this.level() instanceof ServerLevel level) { + this.triggerOnDeathMobEffects(level, reason); } - super.remove(reason); @@ -110,8 +109,8 @@ this.brain.clearMemories(); } -@@ -722,11 +_,17 @@ - mobEffectInstance.onMobRemoved(level, this, removalReason); +@@ -716,11 +_,17 @@ + effect.onMobRemoved(level, this, reason); } + this.removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.DEATH); // CraftBukkit @@ -119,7 +118,7 @@ } @Override - protected void addAdditionalSaveData(ValueOutput output) { + protected void addAdditionalSaveData(final ValueOutput output) { + // Paper start - Friction API + if (this.frictionState != net.kyori.adventure.util.TriState.NOT_SET) { + output.putString("Paper.FrictionState", this.frictionState.toString()); @@ -128,38 +127,38 @@ output.putFloat("Health", this.getHealth()); output.putShort("HurtTime", (short)this.hurtTime); output.putInt("HurtByTimestamp", this.lastHurtByMobTimestamp); -@@ -760,7 +_,12 @@ +@@ -755,7 +_,12 @@ } } -- public @Nullable ItemEntity drop(ItemStack stack, boolean randomizeMotion, boolean includeThrower) { +- public @Nullable ItemEntity drop(final ItemStack itemStack, final boolean randomly, final boolean thrownFromHand) { + // Paper start - Extend dropItem API -+ public final @Nullable ItemEntity drop(ItemStack stack, boolean randomizeMotion, boolean includeThrower) { -+ return this.drop(stack, randomizeMotion, includeThrower, true, null); ++ public final @Nullable ItemEntity drop(final ItemStack itemStack, final boolean randomly, final boolean thrownFromHand) { ++ return this.drop(itemStack, randomly, thrownFromHand, true, null); + } -+ public @Nullable ItemEntity drop(ItemStack stack, boolean randomizeMotion, boolean includeThrower, boolean callEvent, java.util.function.@Nullable Consumer entityOperation) { ++ public @Nullable ItemEntity drop(final ItemStack itemStack, final boolean randomly, final boolean thrownFromHand, final boolean callEvent, final java.util.function.@Nullable Consumer entityOperation) { + // Paper end - Extend dropItem API - if (stack.isEmpty()) { + if (itemStack.isEmpty()) { return null; } else if (this.level().isClientSide()) { -@@ -769,6 +_,31 @@ +@@ -764,6 +_,31 @@ } else { - ItemEntity itemEntity = this.createItemStackToDrop(stack, randomizeMotion, includeThrower); - if (itemEntity != null) { + ItemEntity entity = this.createItemStackToDrop(itemStack, randomly, thrownFromHand); + if (entity != null) { + // CraftBukkit start - fire PlayerDropItemEvent -+ if (entityOperation != null) entityOperation.accept((org.bukkit.entity.Item) itemEntity.getBukkitEntity()); ++ if (entityOperation != null) entityOperation.accept((org.bukkit.entity.Item) entity.getBukkitEntity()); + if (callEvent && this.getBukkitEntity() instanceof org.bukkit.entity.Player player) { -+ org.bukkit.entity.Item drop = (org.bukkit.entity.Item) itemEntity.getBukkitEntity(); ++ org.bukkit.entity.Item drop = (org.bukkit.entity.Item) entity.getBukkitEntity(); + + org.bukkit.event.player.PlayerDropItemEvent event = new org.bukkit.event.player.PlayerDropItemEvent(player, drop); + this.level().getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + org.bukkit.inventory.ItemStack inHandItem = player.getInventory().getItemInMainHand(); -+ if (includeThrower && inHandItem.getAmount() == 0) { ++ if (thrownFromHand && inHandItem.getAmount() == 0) { + // The complete stack was dropped + player.getInventory().setItemInMainHand(drop.getItemStack()); -+ } else if (includeThrower && inHandItem.isSimilar(drop.getItemStack()) && inHandItem.getAmount() < inHandItem.getMaxStackSize() && drop.getItemStack().getAmount() == 1) { ++ } else if (thrownFromHand && inHandItem.isSimilar(drop.getItemStack()) && inHandItem.getAmount() < inHandItem.getMaxStackSize() && drop.getItemStack().getAmount() == 1) { + // Only one item is dropped + inHandItem.setAmount(inHandItem.getAmount() + 1); + player.getInventory().setItemInMainHand(inHandItem); @@ -171,13 +170,13 @@ + } + } + // CraftBukkit end - this.level().addFreshEntity(itemEntity); + this.level().addFreshEntity(entity); } -@@ -778,7 +_,22 @@ +@@ -773,7 +_,22 @@ @Override - protected void readAdditionalSaveData(ValueInput input) { + protected void readAdditionalSaveData(final ValueInput input) { - this.internalSetAbsorptionAmount(input.getFloatOr("AbsorptionAmount", 0.0F)); + // Paper start - Check for NaN + float absorptionAmount = input.getFloatOr("AbsorptionAmount", 0.0F); @@ -198,7 +197,7 @@ if (this.level() != null && !this.level().isClientSide()) { input.read("attributes", AttributeInstance.Packed.LIST_CODEC).ifPresent(this.getAttributes()::apply); } -@@ -791,6 +_,11 @@ +@@ -786,6 +_,11 @@ this.effectsDirty = true; } @@ -210,29 +209,29 @@ this.setHealth(input.getFloatOr("Health", this.getMaxHealth())); this.hurtTime = input.getShortOr("HurtTime", (short)0); this.deathTime = input.getShortOr("DeathTime", (short)0); -@@ -798,6 +_,7 @@ - input.getString("Team").ifPresent(string -> { +@@ -793,6 +_,7 @@ + input.getString("Team").ifPresent(teamName -> { Scoreboard scoreboard = this.level().getScoreboard(); - PlayerTeam playerTeam = scoreboard.getPlayerTeam(string); -+ if (!this.level().paperConfig().scoreboards.allowNonPlayerEntitiesOnScoreboards && !(this instanceof net.minecraft.world.entity.player.Player)) { playerTeam = null; } // Paper - Perf: Disable Scoreboards for non players by default - boolean flag = playerTeam != null && scoreboard.addPlayerToTeam(this.getStringUUID(), playerTeam); - if (!flag) { - LOGGER.warn("Unable to add mob to team \"{}\" (that team probably doesn't exist)", string); -@@ -805,11 +_,13 @@ + PlayerTeam team = scoreboard.getPlayerTeam(teamName); ++ if (!this.level().paperConfig().scoreboards.allowNonPlayerEntitiesOnScoreboards && !(this instanceof net.minecraft.world.entity.player.Player)) { team = null; } // Paper - Perf: Disable Scoreboards for non players by default + boolean success = team != null && scoreboard.addPlayerToTeam(this.getStringUUID(), team); + if (!success) { + LOGGER.warn("Unable to add mob to team \"{}\" (that team probably doesn't exist)", teamName); +@@ -800,11 +_,13 @@ }); this.setSharedFlag(Entity.FLAG_FALL_FLYING, input.getBooleanOr("FallFlying", false)); - input.read("sleeping_pos", BlockPos.CODEC).ifPresentOrElse(blockPos -> { -+ if (this.position().distanceToSqr(blockPos.getX(), blockPos.getY(), blockPos.getZ()) < Mth.square(16)) { // Paper - The sleeping pos will always also set the actual pos, so a desync suggests something is wrong - this.setSleepingPos(blockPos); + input.read("sleeping_pos", BlockPos.CODEC).ifPresentOrElse(sleepingPos -> { ++ if (this.position().distanceToSqr(sleepingPos.getX(), sleepingPos.getY(), sleepingPos.getZ()) < Mth.square(16)) { // Paper - The sleeping pos will always also set the actual pos, so a desync suggests something is wrong + this.setSleepingPos(sleepingPos); this.entityData.set(DATA_POSE, Pose.SLEEPING); if (!this.firstTick) { - this.setPosToBed(blockPos); + this.setPosToBed(sleepingPos); } + } // Paper - The sleeping pos will always also set the actual pos, so a desync suggests something is wrong }, this::clearSleepingPos); - input.read("Brain", Codec.PASSTHROUGH).ifPresent(dynamic -> this.brain = this.makeBrain((Dynamic)dynamic)); + input.read("Brain", Brain.Packed.CODEC).ifPresent(packedBrain -> this.brain = this.makeBrain(packedBrain)); this.lastHurtByPlayer = EntityReference.read(input, "last_hurt_by_player"); -@@ -826,15 +_,44 @@ +@@ -823,15 +_,44 @@ this.updateDirtyEffects(); } @@ -265,19 +264,19 @@ + this.isTickingEffects = true; // CraftBukkit try { while (iterator.hasNext()) { - Holder holder = iterator.next(); - MobEffectInstance mobEffectInstance = this.activeEffects.get(holder); - if (!mobEffectInstance.tickServer(serverLevel, this, () -> this.onEffectUpdated(mobEffectInstance, true, null))) { + Holder mobEffect = iterator.next(); + MobEffectInstance effect = this.activeEffects.get(mobEffect); + if (!effect.tickServer(serverLevel, this, () -> this.onEffectUpdated(effect, true, null))) { + // CraftBukkit start -+ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, mobEffectInstance, null, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.EXPIRATION); ++ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.EXPIRATION); + if (event.isCancelled()) { + continue; + } + // CraftBukkit end iterator.remove(); - this.onEffectsRemoved(List.of(mobEffectInstance)); - } else if (mobEffectInstance.getDuration() % 600 == 0) { -@@ -843,6 +_,18 @@ + this.onEffectsRemoved(List.of(effect)); + } else if (effect.getDuration() % 600 == 0) { +@@ -840,6 +_,18 @@ } } catch (ConcurrentModificationException var6) { } @@ -294,9 +293,9 @@ + this.effectsToProcess.clear(); + // CraftBukkit end } else { - for (MobEffectInstance mobEffectInstance1 : this.activeEffects.values()) { - mobEffectInstance1.tickClient(); -@@ -953,15 +_,33 @@ + for (MobEffectInstance effect : this.activeEffects.values()) { + effect.tickClient(); +@@ -950,15 +_,33 @@ } public boolean removeAllEffects() { @@ -310,9 +309,9 @@ } else if (this.activeEffects.isEmpty()) { return false; } else { -- Map, MobEffectInstance> map = Maps.newHashMap(this.activeEffects); +- Map, MobEffectInstance> copy = Maps.newHashMap(this.activeEffects); - this.activeEffects.clear(); -- this.onEffectsRemoved(map.values()); +- this.onEffectsRemoved(copy.values()); - return true; + // CraftBukkit start + List toRemove = new java.util.LinkedList<>(); @@ -334,88 +333,88 @@ } } -@@ -987,23 +_,71 @@ +@@ -984,23 +_,71 @@ } - public final boolean addEffect(MobEffectInstance effectInstance) { -- return this.addEffect(effectInstance, null); -+ return this.addEffect(effectInstance, (Entity) null); // CraftBukkit + public final boolean addEffect(final MobEffectInstance newEffect) { +- return this.addEffect(newEffect, null); ++ return this.addEffect(newEffect, (Entity)null); // CraftBukkit + } + + // CraftBukkit start -+ public boolean addEffect(MobEffectInstance effectInstance, EntityPotionEffectEvent.Cause cause) { -+ return this.addEffect(effectInstance, (Entity) null, cause); ++ public boolean addEffect(final MobEffectInstance newEffect, final EntityPotionEffectEvent.Cause cause) { ++ return this.addEffect(newEffect, (Entity)null, cause); } - public boolean addEffect(MobEffectInstance effectInstance, @Nullable Entity entity) { -+ return this.addEffect(effectInstance, entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); + public boolean addEffect(final MobEffectInstance newEffect, final @Nullable Entity source) { ++ return this.addEffect(newEffect, source, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); + } + -+ public boolean addEffect(MobEffectInstance effectInstance, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause) { ++ public boolean addEffect(final MobEffectInstance newEffect, final @Nullable Entity source, final EntityPotionEffectEvent.Cause cause) { + // Paper start - Don't fire sync event during generation -+ return this.addEffect(effectInstance, entity, cause, true); ++ return this.addEffect(newEffect, source, cause, true); + } -+ public boolean addEffect(MobEffectInstance effectInstance, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause, boolean fireEvent) { ++ public boolean addEffect(final MobEffectInstance newEffect, final @Nullable Entity source, final EntityPotionEffectEvent.Cause cause, final boolean fireEvent) { + // Paper end - Don't fire sync event during generation + // org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot // Paper - move to API + if (this.isTickingEffects) { -+ this.effectsToProcess.add(new ProcessableEffect(effectInstance, cause)); ++ this.effectsToProcess.add(new ProcessableEffect(newEffect, cause)); + return true; + } + // CraftBukkit end - if (!this.canBeAffected(effectInstance)) { + if (!this.canBeAffected(newEffect)) { return false; } else { - MobEffectInstance mobEffectInstance = this.activeEffects.get(effectInstance.getEffect()); - boolean flag = false; + MobEffectInstance effect = this.activeEffects.get(newEffect.getEffect()); + boolean changed = false; + // CraftBukkit start + boolean override = false; + // Paper start - Properly update hidden effects + boolean addAsHiddenEffect = false; -+ if (mobEffectInstance != null) { -+ override = new MobEffectInstance(mobEffectInstance).update(effectInstance); -+ addAsHiddenEffect = mobEffectInstance.getAmplifier() > effectInstance.getAmplifier() && mobEffectInstance.isShorterDurationThan(effectInstance); ++ if (effect != null) { ++ override = new MobEffectInstance(effect).update(newEffect); ++ addAsHiddenEffect = effect.getAmplifier() > newEffect.getAmplifier() && effect.isShorterDurationThan(newEffect); + // Paper end - Properly update hidden effects + } + + if (fireEvent) { // Paper - Don't fire sync event during generation -+ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, mobEffectInstance, effectInstance, cause, override); ++ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, newEffect, cause, override); + override = event.isOverride(); // Paper - Don't fire sync event during generation + if (event.isCancelled()) { + return false; + } + } // Paper - Don't fire sync event during generation + // CraftBukkit end - if (mobEffectInstance == null) { - this.activeEffects.put(effectInstance.getEffect(), effectInstance); - this.onEffectAdded(effectInstance, entity); - flag = true; - effectInstance.onEffectAdded(this); -- } else if (mobEffectInstance.update(effectInstance)) { + if (effect == null) { + this.activeEffects.put(newEffect.getEffect(), newEffect); + this.onEffectAdded(newEffect, source); + changed = true; + newEffect.onEffectAdded(this); +- } else if (effect.update(newEffect)) { + // CraftBukkit start + } else if (override) { // Paper - Don't fire sync event during generation -+ mobEffectInstance.update(effectInstance); - this.onEffectUpdated(mobEffectInstance, true, entity); - flag = true; ++ effect.update(newEffect); + this.onEffectUpdated(effect, true, source); + changed = true; + // Paper start - Properly update hidden effects + } else if (addAsHiddenEffect) { -+ if (mobEffectInstance.hiddenEffect == null) { -+ mobEffectInstance.hiddenEffect = new MobEffectInstance(effectInstance); ++ if (effect.hiddenEffect == null) { ++ effect.hiddenEffect = new MobEffectInstance(newEffect); + } else { -+ mobEffectInstance.hiddenEffect.update(effectInstance); ++ effect.hiddenEffect.update(newEffect); + } + // Paper end - Properly update hidden effects } - effectInstance.onEffectStarted(this); -@@ -1039,11 +_,35 @@ + newEffect.onEffectStarted(this); +@@ -1035,11 +_,35 @@ } - public final @Nullable MobEffectInstance removeEffectNoUpdate(Holder effect) { + public final @Nullable MobEffectInstance removeEffectNoUpdate(final Holder effect) { + // CraftBukkit start + return this.removeEffectNoUpdate(effect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); + } -+ public @Nullable MobEffectInstance removeEffectNoUpdate(Holder effect, EntityPotionEffectEvent.Cause cause) { ++ public @Nullable MobEffectInstance removeEffectNoUpdate(final Holder effect, final EntityPotionEffectEvent.Cause cause) { + if (this.isTickingEffects) { + this.effectsToProcess.add(new ProcessableEffect(effect, cause)); + return null; @@ -434,36 +433,36 @@ return this.activeEffects.remove(effect); } - public boolean removeEffect(Holder effect) { -- MobEffectInstance mobEffectInstance = this.removeEffectNoUpdate(effect); + public boolean removeEffect(final Holder effect) { +- MobEffectInstance effectInstance = this.removeEffectNoUpdate(effect); + return this.removeEffect(effect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); + } + -+ public boolean removeEffect(Holder effect, EntityPotionEffectEvent.Cause cause) { -+ MobEffectInstance mobEffectInstance = this.removeEffectNoUpdate(effect, cause); ++ public boolean removeEffect(final Holder effect, final EntityPotionEffectEvent.Cause cause) { ++ MobEffectInstance effectInstance = this.removeEffectNoUpdate(effect, cause); + // CraftBukkit end - if (mobEffectInstance != null) { - this.onEffectsRemoved(List.of(mobEffectInstance)); + if (effectInstance != null) { + this.onEffectsRemoved(List.of(effectInstance)); return true; -@@ -1134,17 +_,62 @@ +@@ -1130,17 +_,62 @@ } - public void heal(float amount) { + public void heal(final float heal) { + // CraftBukkit start - Delegate so we can handle providing a reason for health being regained -+ this.heal(amount, EntityRegainHealthEvent.RegainReason.CUSTOM); ++ this.heal(heal, EntityRegainHealthEvent.RegainReason.CUSTOM); + } + -+ public void heal(float amount, EntityRegainHealthEvent.RegainReason regainReason) { ++ public void heal(final float heal, final EntityRegainHealthEvent.RegainReason regainReason) { + // Paper start - Forward -+ this.heal(amount, regainReason, false); ++ this.heal(heal, regainReason, false); + } + -+ public void heal(float amount, EntityRegainHealthEvent.RegainReason regainReason, boolean isFastRegen) { ++ public void heal(final float heal, final EntityRegainHealthEvent.RegainReason regainReason, final boolean isFastRegen) { + // Paper end - Forward float health = this.getHealth(); if (health > 0.0F) { -- this.setHealth(health + amount); -+ EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), amount, regainReason, isFastRegen); // Paper +- this.setHealth(health + heal); ++ EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), heal, regainReason, isFastRegen); // Paper + // Suppress during worldgen + if (this.valid) { + this.level().getCraftServer().getPluginManager().callEvent(event); @@ -485,8 +484,9 @@ return this.entityData.get(DATA_HEALTH_ID); } - public void setHealth(float health) { -+ // Paper start - Check for NaN +- public void setHealth(final float health) { ++ // Paper start - Check for NaN ++ public void setHealth(float health) { + if (Float.isNaN(health)) { health = getMaxHealth(); if (this.valid) { + System.err.println("[NAN-HEALTH] " + getScoreboardName() + " had NaN health set"); + } } // Paper end - Check for NaN @@ -495,7 +495,7 @@ + org.bukkit.craftbukkit.entity.CraftPlayer player = ((ServerPlayer) this).getBukkitEntity(); + // Squeeze + if (health < 0.0F) { -+ player.setRealHealth(0.0D); ++ player.setRealHealth(0.0); + } else if (health > player.getMaxHealth()) { + player.setRealHealth(player.getMaxHealth()); + } else { @@ -509,177 +509,177 @@ this.entityData.set(DATA_HEALTH_ID, Mth.clamp(health, 0.0F, this.getMaxHealth())); } -@@ -1156,7 +_,7 @@ - public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { - if (this.isInvulnerableTo(level, damageSource)) { +@@ -1152,7 +_,7 @@ + public boolean hurtServer(final ServerLevel level, final DamageSource source, float damage) { + if (this.isInvulnerableTo(level, source)) { return false; - } else if (this.isDeadOrDying()) { + } else if (this.isRemoved() || this.dead || this.getHealth() <= 0.0F) { // CraftBukkit - Don't allow entities that got set to dead/killed elsewhere to get damaged and die return false; - } else if (damageSource.is(DamageTypeTags.IS_FIRE) && this.hasEffect(MobEffects.FIRE_RESISTANCE)) { + } else if (source.is(DamageTypeTags.IS_FIRE) && this.hasEffect(MobEffects.FIRE_RESISTANCE)) { return false; -@@ -1172,35 +_,58 @@ - - ItemStack useItem = this.getUseItem(); - float originAmount = amount; -- float f1 = this.applyItemBlocking(level, damageSource, amount); -- amount -= f1; -+ float f1 = this.applyItemBlocking(level, damageSource, amount, true); // Paper -+ // amount -= f1; // CraftBukkit - Moved into handleEntityDamage(DamageSource, float) to allow modification - boolean flag = f1 > 0.0F; -- if (damageSource.is(DamageTypeTags.IS_FREEZING) && this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES)) { +@@ -1168,35 +_,58 @@ + + ItemStack itemInUse = this.getUseItem(); + float originalDamage = damage; +- float damageBlocked = this.applyItemBlocking(level, source, damage); +- damage -= damageBlocked; ++ float damageBlocked = this.applyItemBlocking(level, source, damage, true); // Paper ++ // damage -= damageBlocked; // CraftBukkit - Moved into handleEntityDamage(DamageSource, float) to allow modification + boolean blocked = damageBlocked > 0.0F; +- if (source.is(DamageTypeTags.IS_FREEZING) && this.is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES)) { + // CraftBukkit - Moved into handleEntityDamage(DamageSource, float) to get amount -+ if (false && damageSource.is(DamageTypeTags.IS_FREEZING) && this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES)) { - amount *= 5.0F; ++ if (false && source.is(DamageTypeTags.IS_FREEZING) && this.is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES)) { + damage *= 5.0F; } -- if (damageSource.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { +- if (source.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { + // CraftBukkit - Moved into handleEntityDamage(DamageSource, float) to get amount and actuallyHurt(DamageSource, float, EntityDamageEvent) for handle damage -+ if (false && damageSource.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { - this.hurtHelmet(damageSource, amount); - amount *= 0.75F; ++ if (false && source.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { + this.hurtHelmet(source, damage); + damage *= 0.75F; } + EntityDamageEvent event; // CraftBukkit // Paper - move this into the actual invuln check.... - if (Float.isNaN(amount) || Float.isInfinite(amount)) { - amount = Float.MAX_VALUE; + if (Float.isNaN(damage) || Float.isInfinite(damage)) { + damage = Float.MAX_VALUE; } - boolean flag1 = true; -- if (this.invulnerableTime > 10.0F && !damageSource.is(DamageTypeTags.BYPASSES_COOLDOWN)) { -+ if (this.invulnerableTime > (float) this.invulnerableDuration / 2.0F && !damageSource.is(DamageTypeTags.BYPASSES_COOLDOWN)) { // CraftBukkit - restore use of maxNoDamageTicks - if (amount <= this.lastHurt) { + boolean tookFullDamage = true; +- if (this.invulnerableTime > 10.0F && !source.is(DamageTypeTags.BYPASSES_COOLDOWN)) { ++ if (this.invulnerableTime > (float)this.invulnerableDuration / 2.0F && !source.is(DamageTypeTags.BYPASSES_COOLDOWN)) { // CraftBukkit - restore use of maxNoDamageTicks + if (damage <= this.lastHurt) { return false; } -- this.actuallyHurt(level, damageSource, amount - this.lastHurt); +- this.actuallyHurt(level, source, damage - this.lastHurt); + // Paper start - only call damage event when actuallyHurt will be called - move call logic down -+ event = this.handleEntityDamage(damageSource, amount, this.lastHurt); // Paper - fix invulnerability reduction in EntityDamageEvent - pass lastDamage reduction -+ amount = computeAmountFromEntityDamageEvent(event); ++ event = this.handleEntityDamage(source, damage, this.lastHurt); // Paper - fix invulnerability reduction in EntityDamageEvent - pass lastDamage reduction ++ damage = this.computeAmountFromEntityDamageEvent(event); + // Paper end - only call damage event when actuallyHurt will be called - move call logic down + + // CraftBukkit start -+ if (!this.actuallyHurt(level, damageSource, (float) event.getFinalDamage(), event)) { // Paper - fix invulnerability reduction in EntityDamageEvent - no longer subtract lastHurt, that is part of the damage event calc now ++ if (!this.actuallyHurt(level, source, (float)event.getFinalDamage(), event)) { // Paper - fix invulnerability reduction in EntityDamageEvent - no longer subtract lastHurt, that is part of the damage event calc now + return false; + } -+ if (this instanceof ServerPlayer && event.getDamage() == 0 && originAmount == 0) return false; // Paper - revert to vanilla damage - players are not affected by damage that is 0 - skip damage if the vanilla damage is 0 and was not modified by plugins in the event. ++ if (this instanceof ServerPlayer && event.getDamage() == 0 && originalDamage == 0) return false; // Paper - revert to vanilla damage - players are not affected by damage that is 0 - skip damage if the vanilla damage is 0 and was not modified by plugins in the event. + // CraftBukkit end - this.lastHurt = amount; - flag1 = false; + this.lastHurt = damage; + tookFullDamage = false; } else { + // Paper start - only call damage event when actuallyHurt will be called - move call logic down -+ event = this.handleEntityDamage(damageSource, amount, 0); // Paper - fix invulnerability reduction in EntityDamageEvent - pass lastDamage reduction (none in this branch) -+ amount = computeAmountFromEntityDamageEvent(event); ++ event = this.handleEntityDamage(source, damage, 0); // Paper - fix invulnerability reduction in EntityDamageEvent - pass lastDamage reduction (none in this branch) ++ damage = this.computeAmountFromEntityDamageEvent(event); + // Paper end - only call damage event when actuallyHurt will be called - move call logic down + // CraftBukkit start -+ if (!this.actuallyHurt(level, damageSource, (float) event.getFinalDamage(), event)) { ++ if (!this.actuallyHurt(level, source, (float)event.getFinalDamage(), event)) { + return false; + } -+ if (this instanceof ServerPlayer && event.getDamage() == 0 && originAmount == 0) return false; // Paper - revert to vanilla damage - players are not affected by damage that is 0 - skip damage if the vanilla damage is 0 and was not modified by plugins in the event. - this.lastHurt = amount; ++ if (this instanceof ServerPlayer && event.getDamage() == 0 && originalDamage == 0) return false; // Paper - revert to vanilla damage - players are not affected by damage that is 0 - skip damage if the vanilla damage is 0 and was not modified by plugins in the event. + this.lastHurt = damage; - this.invulnerableTime = 20; -- this.actuallyHurt(level, damageSource, amount); +- this.actuallyHurt(level, source, damage); + this.invulnerableTime = this.invulnerableDuration; // CraftBukkit - restore use of maxNoDamageTicks -+ // this.actuallyHurt(level, damageSource, amount); ++ // this.actuallyHurt(level, source, damage); + // CraftBukkit end this.hurtDuration = 10; this.hurtTime = this.hurtDuration; } -@@ -1215,7 +_,7 @@ - level.broadcastDamageEvent(this, damageSource); +@@ -1211,7 +_,7 @@ + level.broadcastDamageEvent(this, source); } -- if (!damageSource.is(DamageTypeTags.NO_IMPACT) && (!flag || amount > 0.0F)) { -+ if (!damageSource.is(DamageTypeTags.NO_IMPACT) && !flag) { // CraftBukkit - Prevent marking hurt if the damage is blocked +- if (!source.is(DamageTypeTags.NO_IMPACT) && (!blocked || damage > 0.0F)) { ++ if (!source.is(DamageTypeTags.NO_IMPACT) && !blocked) { // CraftBukkit - Prevent marking hurt if the damage is blocked this.markHurt(); } -@@ -1230,8 +_,16 @@ - d = damageSource.getSourcePosition().x() - this.getX(); - d1 = damageSource.getSourcePosition().z() - this.getZ(); +@@ -1226,8 +_,16 @@ + xd = source.getSourcePosition().x() - this.getX(); + zd = source.getSourcePosition().z() - this.getZ(); } + // Paper start - Check distance in entity interactions; see for loop in knockback method -+ if (Math.abs(d) > 200) { -+ d = Math.random() - Math.random(); ++ if (Math.abs(xd) > 200) { ++ xd = Math.random() - Math.random(); + } -+ if (Math.abs(d1) > 200) { -+ d1 = Math.random() - Math.random(); ++ if (Math.abs(zd) > 200) { ++ zd = Math.random() - Math.random(); + } + // Paper end - Check distance in entity interactions -- this.knockback(0.4F, d, d1); -+ this.knockback(0.4F, d, d1, damageSource.getDirectEntity(), damageSource.getDirectEntity() == null ? io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE : io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events - if (!flag) { - this.indicateDamage(d, d1); +- this.knockback(0.4F, xd, zd); ++ this.knockback(0.4F, xd, zd, source.getDirectEntity(), source.getDirectEntity() == null ? io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE : io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events + if (!blocked) { + this.indicateDamage(xd, zd); } -@@ -1240,19 +_,19 @@ +@@ -1236,19 +_,19 @@ if (this.isDeadOrDying()) { - if (!this.checkTotemDeathProtection(damageSource)) { -- if (flag1) { + if (!this.checkTotemDeathProtection(source)) { +- if (tookFullDamage) { - this.makeSound(this.getDeathSound()); -- this.playSecondaryHurtSound(damageSource); +- this.playSecondaryHurtSound(source); - } + // Paper start - moved into CraftEventFactory event caller for cancellable death event -+ this.silentDeath = !flag1; // mark entity as dying silently ++ this.silentDeath = !tookFullDamage; // mark entity as dying silently + // Paper end - this.die(damageSource); + this.die(source); + this.silentDeath = false; // Paper - cancellable death event - reset to default } - } else if (flag1) { - this.playHurtSound(damageSource); - this.playSecondaryHurtSound(damageSource); + } else if (tookFullDamage) { + this.playHurtSound(source); + this.playSecondaryHurtSound(source); } -- boolean flag2 = !flag || amount > 0.0F; -+ boolean flag2 = !flag; // CraftBukkit - Ensure to return false if damage is blocked - if (flag2) { - this.lastDamageSource = damageSource; +- boolean success = !blocked || damage > 0.0F; ++ boolean success = !blocked; // CraftBukkit - Ensure to return false if damage is blocked + if (success) { + this.lastDamageSource = source; this.lastDamageStamp = this.level().getGameTime(); -@@ -1278,6 +_,12 @@ +@@ -1274,6 +_,12 @@ } - public float applyItemBlocking(ServerLevel level, DamageSource damageSource, float damageAmount) { + public float applyItemBlocking(final ServerLevel level, final DamageSource source, final float damage) { + // Paper start -+ return applyItemBlocking(level, damageSource, damageAmount, false); ++ return this.applyItemBlocking(level, source, damage, false); + } + -+ public float applyItemBlocking(ServerLevel level, DamageSource damageSource, float damageAmount, boolean dryRun) { ++ public float applyItemBlocking(final ServerLevel level, final DamageSource source, final float damage, final boolean dryRun) { + // Paper end - if (damageAmount <= 0.0F) { + if (damage <= 0.0F) { return 0.0F; } else { -@@ -1302,10 +_,12 @@ +@@ -1298,10 +_,12 @@ } - float f = blocksAttacks.resolveBlockedDamage(damageSource, damageAmount, acos); + float damageBlocked = blocksAttacks.resolveBlockedDamage(source, damage, angle); + if (!dryRun) { // Paper - blocksAttacks.hurtBlockingItem(this.level(), itemBlockingWith, this, this.getUsedItemHand(), f); -- if (f > 0.0F && !damageSource.is(DamageTypeTags.IS_PROJECTILE) && damageSource.getDirectEntity() instanceof LivingEntity livingEntity) { -+ if (f > 0.0F && !damageSource.is(DamageTypeTags.IS_PROJECTILE) && damageSource.getDirectEntity() instanceof LivingEntity livingEntity && livingEntity.distanceToSqr(this) <= Mth.square(200.0D)) { // Paper - Fix shield disable inconsistency & Check distance in entity interactions + blocksAttacks.hurtBlockingItem(this.level(), blockingWith, this, this.getUsedItemHand(), damageBlocked); +- if (damageBlocked > 0.0F && !source.is(DamageTypeTags.IS_PROJECTILE) && source.getDirectEntity() instanceof LivingEntity livingEntity) { ++ if (damageBlocked > 0.0F && !source.is(DamageTypeTags.IS_PROJECTILE) && source.getDirectEntity() instanceof LivingEntity livingEntity && livingEntity.distanceToSqr(this) <= Mth.square(200.0)) { // Paper - Fix shield disable inconsistency & Check distance in entity interactions this.blockUsingItem(level, livingEntity); } + } // Paper - return f; + return damageBlocked; } -@@ -1316,6 +_,59 @@ +@@ -1312,6 +_,59 @@ } } + // Paper start - copied from above split by relevant part -+ public boolean canBlockAttack(DamageSource damageSource, float damageAmount) { -+ if (damageAmount <= 0.0F) { ++ public boolean canBlockAttack(DamageSource source, float damage) { ++ if (damage <= 0.0F) { + return false; + } else { -+ ItemStack itemBlockingWith = this.getItemBlockingWith(); -+ if (itemBlockingWith == null) { ++ ItemStack blockingWith = this.getItemBlockingWith(); ++ if (blockingWith == null) { + return false; + } else { -+ BlocksAttacks blocksAttacks = itemBlockingWith.get(DataComponents.BLOCKS_ATTACKS); -+ if (blocksAttacks != null && !blocksAttacks.bypassedBy().map(damageSource::is).orElse(false)) { -+ if (damageSource.getDirectEntity() instanceof AbstractArrow abstractArrow && abstractArrow.getPierceLevel() > 0) { ++ BlocksAttacks blocksAttacks = blockingWith.get(DataComponents.BLOCKS_ATTACKS); ++ if (blocksAttacks != null && !blocksAttacks.bypassedBy().map(t -> t.contains(source.typeHolder())).orElse(false)) { ++ if (source.getDirectEntity() instanceof AbstractArrow abstractArrow && abstractArrow.getPierceLevel() > 0) { + return false; + } else { + return true; @@ -715,16 +715,16 @@ + if (blocksAttacks == null) return; + + blocksAttacks.hurtBlockingItem(this.level(), itemBlockingWith, this, this.getUsedItemHand(), f); -+ if (f > 0.0F && !damageSource.is(DamageTypeTags.IS_PROJECTILE) && damageSource.getDirectEntity() instanceof LivingEntity livingEntity && livingEntity.distanceToSqr(this) <= Mth.square(200.0D)) { // Paper - Fix shield disable inconsistency & Check distance in entity interactions ++ if (f > 0.0F && !damageSource.is(DamageTypeTags.IS_PROJECTILE) && damageSource.getDirectEntity() instanceof LivingEntity livingEntity && livingEntity.distanceToSqr(this) <= Mth.square(200.0)) { // Paper - Fix shield disable inconsistency & Check distance in entity interactions + this.blockUsingItem(level, livingEntity); + } + } + // Paper end - copied from above split by relevant part + - public void playSecondaryHurtSound(DamageSource damageSource) { - if (damageSource.is(DamageTypes.THORNS)) { + public void playSecondaryHurtSound(final DamageSource source) { + if (source.is(DamageTypes.THORNS)) { SoundSource soundSource = this instanceof Player ? SoundSource.PLAYERS : SoundSource.HOSTILE; -@@ -1347,12 +_,24 @@ +@@ -1343,12 +_,24 @@ return EntityReference.getPlayer(this.lastHurtByPlayer, this.level()); } @@ -740,77 +740,77 @@ + } + // Paper end - only call damage event when actuallyHurt will be called - move out amount computation logic + - protected void blockUsingItem(ServerLevel level, LivingEntity entity) { - entity.blockedByItem(this); + protected void blockUsingItem(final ServerLevel level, final LivingEntity attacker) { + attacker.blockedByItem(this); } - protected void blockedByItem(LivingEntity entity) { -- entity.knockback(0.5, entity.getX() - this.getX(), entity.getZ() - this.getZ()); -+ entity.knockback(0.5, entity.getX() - this.getX(), entity.getZ() - this.getZ(), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SHIELD_BLOCK); // CraftBukkit // Paper - fix attacker & knockback events + protected void blockedByItem(final LivingEntity defender) { +- defender.knockback(0.5, defender.getX() - this.getX(), defender.getZ() - this.getZ()); ++ defender.knockback(0.5, defender.getX() - this.getX(), defender.getZ() - this.getZ(), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SHIELD_BLOCK); // CraftBukkit // Paper - fix attacker & knockback events } - private boolean checkTotemDeathProtection(DamageSource damageSource) { -@@ -1362,18 +_,39 @@ - ItemStack itemStack = null; - DeathProtection deathProtection = null; + private boolean checkTotemDeathProtection(final DamageSource killingDamage) { +@@ -1358,18 +_,39 @@ + ItemStack protectionItem = null; + DeathProtection protection = null; + // CraftBukkit start -+ InteractionHand hand = null; -+ ItemStack itemInHand = ItemStack.EMPTY; - for (InteractionHand interactionHand : InteractionHand.values()) { -- ItemStack itemInHand = this.getItemInHand(interactionHand); -+ itemInHand = this.getItemInHand(interactionHand); - deathProtection = itemInHand.get(DataComponents.DEATH_PROTECTION); - if (deathProtection != null) { -+ hand = interactionHand; // CraftBukkit - itemStack = itemInHand.copy(); -+ // itemInHand.shrink(1); // CraftBukkit ++ InteractionHand usedHand = null; ++ ItemStack itemStack = ItemStack.EMPTY; + for (InteractionHand hand : InteractionHand.values()) { +- ItemStack itemStack = this.getItemInHand(hand); ++ itemStack = this.getItemInHand(hand); + protection = itemStack.get(DataComponents.DEATH_PROTECTION); + if (protection != null) { ++ usedHand = hand; + protectionItem = itemStack.copy(); ++ // itemStack.shrink(1); + break; + } + } + -+ final org.bukkit.inventory.EquipmentSlot handSlot = (hand != null) ? org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand) : null; ++ final org.bukkit.inventory.EquipmentSlot handSlot = (usedHand != null) ? org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(usedHand) : null; + final EntityResurrectEvent event = new EntityResurrectEvent((org.bukkit.entity.LivingEntity) this.getBukkitEntity(), handSlot); -+ event.setCancelled(itemStack == null); ++ event.setCancelled(protectionItem == null); + this.level().getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + // Set death protection to null as the event was cancelled. Prevent any attempt at resurrection. -+ deathProtection = null; ++ protection = null; + } else { -+ if (!itemInHand.isEmpty() && itemStack != null) { // Paper - only reduce item if actual totem was found - itemInHand.shrink(1); ++ if (!itemStack.isEmpty() && protectionItem != null) { // Paper - only reduce item if actual totem was found + itemStack.shrink(1); - break; - } - } - -- if (itemStack != null) { -- if (this instanceof ServerPlayer serverPlayer) { +- if (protectionItem != null) { +- if (this instanceof ServerPlayer player) { + } + // Paper start - fix NPE when pre-cancelled EntityResurrectEvent is uncancelled + // restore the previous behavior in that case by defaulting to vanilla's totem of undying effect -+ if (deathProtection == null) { -+ deathProtection = DeathProtection.TOTEM_OF_UNDYING; ++ if (protection == null) { ++ protection = DeathProtection.TOTEM_OF_UNDYING; + } + // Paper end - fix NPE when pre-cancelled EntityResurrectEvent is uncancelled -+ if (itemStack != null && this instanceof final ServerPlayer serverPlayer) { -+ // CraftBukkit end - serverPlayer.awardStat(Stats.ITEM_USED.get(itemStack.getItem())); - CriteriaTriggers.USED_TOTEM.trigger(serverPlayer, itemStack); - itemStack.causeUseVibration(this, GameEvent.ITEM_INTERACT_FINISH); -@@ -1431,6 +_,7 @@ ++ if (protectionItem != null && this instanceof final ServerPlayer player) { ++ // CraftBukkit end + player.awardStat(Stats.ITEM_USED.get(protectionItem.getItem())); + CriteriaTriggers.USED_TOTEM.trigger(player, protectionItem); + protectionItem.causeUseVibration(this, GameEvent.ITEM_INTERACT_FINISH); +@@ -1420,6 +_,7 @@ if (!this.isRemoved() && !this.dead) { - Entity entity = damageSource.getEntity(); - LivingEntity killCredit = this.getKillCredit(); + Entity sourceEntity = source.getEntity(); + LivingEntity killer = this.getKillCredit(); + /* // Paper - move down to make death event cancellable - this is the awardKillScore below - if (killCredit != null) { - killCredit.awardKillScore(this, damageSource); + if (killer != null) { + killer.awardKillScore(this, source); } -@@ -1441,68 +_,147 @@ +@@ -1430,68 +_,147 @@ this.stopUsingItem(); if (!this.level().isClientSide() && this.hasCustomName()) { - LOGGER.info("Named entity {} died: {}", this, this.getCombatTracker().getDeathMessage().getString()); -+ if (org.spigotmc.SpigotConfig.logNamedDeaths) LivingEntity.LOGGER.info("Named entity {} died: {}", this, this.getCombatTracker().getDeathMessage().getString()); // Spigot ++ if (org.spigotmc.SpigotConfig.logNamedDeaths) LOGGER.info("Named entity {} died: {}", this, this.getCombatTracker().getDeathMessage().getString()); // Spigot } + */ // Paper - move down to make death event cancellable - this is the awardKillScore below @@ -818,16 +818,16 @@ - this.getCombatTracker().recheckStatus(); + // Paper - moved into if below if (this.level() instanceof ServerLevel serverLevel) { -- if (entity == null || entity.killedEntity(serverLevel, this, damageSource)) { +- if (sourceEntity == null || sourceEntity.killedEntity(serverLevel, this, source)) { + // Paper - move below into if for onKill + // Paper start -+ if (entity instanceof net.minecraft.world.entity.monster.Creeper creeper) { // Creeper has logic for drops we need capture -+ creeper.killedEntity((ServerLevel) this.level(), this, damageSource); ++ if (sourceEntity instanceof net.minecraft.world.entity.monster.Creeper creeper) { // Creeper has logic for drops we need capture ++ creeper.killedEntity((ServerLevel)this.level(), this, source); + } -+ org.bukkit.event.entity.EntityDeathEvent deathEvent = this.dropAllDeathLoot(serverLevel, damageSource); ++ org.bukkit.event.entity.EntityDeathEvent deathEvent = this.dropAllDeathLoot(serverLevel, source); + if (deathEvent == null || !deathEvent.isCancelled()) { -+ //if (entityliving != null) { // Paper - Fix item duplication and teleport issues; moved to be run earlier in #dropAllDeathLoot before destroying the drop items in CraftEventFactory#callEntityDeathEvent -+ // entityliving.awardKillScore(this, damageSource); ++ //if (killer != null) { // Paper - Fix item duplication and teleport issues; moved to be run earlier in #dropAllDeathLoot before destroying the drop items in CraftEventFactory#callEntityDeathEvent ++ // killer.awardKillScore(this, source); + //} + // Paper start - clear equipment if event is not cancelled + if (this instanceof Mob) { @@ -843,24 +843,25 @@ + } + + if (!this.level().isClientSide() && this.hasCustomName()) { -+ if (org.spigotmc.SpigotConfig.logNamedDeaths) LivingEntity.LOGGER.info("Named entity {} died: {}", this, this.getCombatTracker().getDeathMessage().getString()); // Spigot ++ if (org.spigotmc.SpigotConfig.logNamedDeaths) LOGGER.info("Named entity {} died: {}", this, this.getCombatTracker().getDeathMessage().getString()); // Spigot + } + + this.getCombatTracker().recheckStatus(); -+ if (entity != null) { -+ entity.killedEntity((ServerLevel) this.level(), this, damageSource); ++ if (sourceEntity != null) { ++ sourceEntity.killedEntity((ServerLevel)this.level(), this, source); + } this.gameEvent(GameEvent.ENTITY_DIE); -- this.dropAllDeathLoot(serverLevel, damageSource); +- this.dropAllDeathLoot(serverLevel, source); +- this.createWitherRose(killer); + } else { + this.dead = false; + this.setHealth((float) deathEvent.getReviveHealth()); -+ if (entity instanceof net.minecraft.world.entity.monster.Creeper creeper && creeper.droppedSkulls) { // Creeper has logic for drops skull and need revert that flag ++ if (sourceEntity instanceof net.minecraft.world.entity.monster.Creeper creeper && creeper.droppedSkulls) { // Creeper has logic for drops skull and need revert that flag + creeper.droppedSkulls = false; + } + } + // Paper end - this.createWitherRose(killCredit); ++ this.createWitherRose(killer); } + // Paper start @@ -874,18 +875,18 @@ } } - protected void createWitherRose(@Nullable LivingEntity entitySource) { + protected void createWitherRose(final @Nullable LivingEntity killer) { if (this.level() instanceof ServerLevel serverLevel) { boolean var6 = false; -- if (entitySource instanceof WitherBoss) { -+ if (this.dead && entitySource instanceof WitherBoss) { // Paper +- if (killer instanceof WitherBoss) { ++ if (this.dead && killer instanceof WitherBoss) { // Paper if (serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) { - BlockPos blockPos = this.blockPosition(); - BlockState blockState = Blocks.WITHER_ROSE.defaultBlockState(); - if (this.level().getBlockState(blockPos).isAir() && blockState.canSurvive(this.level(), blockPos)) { -- this.level().setBlock(blockPos, blockState, Block.UPDATE_ALL); + BlockPos pos = this.blockPosition(); + BlockState state = Blocks.WITHER_ROSE.defaultBlockState(); + if (this.level().getBlockState(pos).isAir() && state.canSurvive(this.level(), pos)) { +- this.level().setBlock(pos, state, Block.UPDATE_ALL); - var6 = true; -+ var6 = CraftEventFactory.handleBlockFormEvent(this.level(), blockPos, blockState, Block.UPDATE_ALL, this); // CraftBukkit - call EntityBlockFormEvent for Wither Rose ++ var6 = CraftEventFactory.handleBlockFormEvent(this.level(), pos, state, Block.UPDATE_ALL, this); // CraftBukkit - call EntityBlockFormEvent for Wither Rose } } @@ -903,55 +904,55 @@ } } -- protected void dropAllDeathLoot(ServerLevel level, DamageSource damageSource) { +- protected void dropAllDeathLoot(final ServerLevel level, final DamageSource source) { + // Paper start + protected boolean clearEquipmentSlots = true; + protected Set clearedEquipmentSlots = new java.util.HashSet<>(); -+ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(ServerLevel level, DamageSource damageSource) { ++ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(final ServerLevel level, final DamageSource source) { + // Paper end - boolean flag = this.lastHurtByPlayerMemoryTime > 0; + boolean playerKilled = this.lastHurtByPlayerMemoryTime > 0; + this.dropEquipment(level); // CraftBukkit - from below if (this.shouldDropLoot(level)) { - this.dropFromLootTable(level, damageSource, flag); + this.dropFromLootTable(level, source, playerKilled); + // Paper start + final boolean prev = this.clearEquipmentSlots; + this.clearEquipmentSlots = false; + this.clearedEquipmentSlots.clear(); + // Paper end - this.dropCustomDeathLoot(level, damageSource, flag); + this.dropCustomDeathLoot(level, source, playerKilled); + this.clearEquipmentSlots = prev; // Paper } - this.dropEquipment(level); + // CraftBukkit start - Call death event // Paper start - call advancement triggers with correct entity equipment -+ org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, damageSource, this.drops, () -> { ++ org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, source, this.drops, () -> { + final LivingEntity killer = this.getKillCredit(); + if (killer != null) { -+ killer.awardKillScore(this, damageSource); ++ killer.awardKillScore(this, source); + } + }); // Paper end + this.postDeathDropItems(deathEvent); // Paper + this.drops = new java.util.ArrayList<>(); + // this.dropEquipment(level); // CraftBukkit - moved up + // CraftBukkit end - this.dropExperience(level, damageSource.getEntity()); + this.dropExperience(level, source.getEntity()); + return deathEvent; // Paper } - protected void dropEquipment(ServerLevel level) { + protected void dropEquipment(final ServerLevel level) { } + protected void postDeathDropItems(org.bukkit.event.entity.EntityDeathEvent event) {} // Paper - method for post death logic that cannot be ran before the event is potentially cancelled -- protected void dropExperience(ServerLevel level, @Nullable Entity entity) { -+ public int getExpReward(ServerLevel level, @Nullable Entity entity) { // CraftBukkit +- protected void dropExperience(final ServerLevel level, final @Nullable Entity killer) { ++ public int getExpReward(final ServerLevel level, final @Nullable Entity killer) { // CraftBukkit if (!this.wasExperienceConsumed() && ( this.isAlwaysExperienceDropper() || this.lastHurtByPlayerMemoryTime > 0 && this.shouldDropExperience() && level.getGameRules().get(GameRules.MOB_DROPS) )) { -- ExperienceOrb.award(level, this.position(), this.getExperienceReward(level, entity)); +- ExperienceOrb.award(level, this.position(), this.getExperienceReward(level, killer)); - } -+ return this.getExperienceReward(level, entity); // CraftBukkit ++ return this.getExperienceReward(level, killer); // CraftBukkit + } + return 0; // CraftBukkit + } @@ -965,37 +966,37 @@ + // CraftBukkit end } - protected void dropCustomDeathLoot(ServerLevel level, DamageSource damageSource, boolean recentlyHit) { -@@ -1606,9 +_,14 @@ + protected void dropCustomDeathLoot(final ServerLevel level, final DamageSource source, final boolean killedByPlayer) { +@@ -1603,9 +_,14 @@ } - public void knockback(double strength, double x, double z) { + public void knockback(double power, double xd, double zd) { + // CraftBukkit start - EntityKnockbackEvent -+ this.knockback(strength, x, z, null, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.UNKNOWN); // Paper - knockback events ++ this.knockback(power, xd, zd, null, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.UNKNOWN); // Paper - knockback events + } + -+ public void knockback(double strength, double x, double z, @Nullable Entity attacker, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause eventCause) { // Paper - knockback events - strength *= 1.0 - this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE); -- if (!(strength <= 0.0)) { ++ public void knockback(double power, double xd, double zd, @Nullable Entity attacker, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause eventCause) { // Paper - knockback events + power *= 1.0 - this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE); +- if (!(power <= 0.0)) { - this.needsSync = true; -+ if (true || !(strength <= 0.0)) { // CraftBukkit - Call event even when force is 0 ++ if (true || !(power <= 0.0)) { // CraftBukkit - Call event even when force is 0 + // this.needsSync = true; // CraftBukkit - Move down Vec3 deltaMovement = this.getDeltaMovement(); - while (x * x + z * z < 1.0E-5F) { -@@ -1617,11 +_,22 @@ + while (xd * xd + zd * zd < 1.0E-5F) { +@@ -1614,11 +_,22 @@ } - Vec3 vec3 = new Vec3(x, 0.0, z).normalize().scale(strength); + Vec3 deltaVector = new Vec3(xd, 0.0, zd).normalize().scale(power); - this.setDeltaMovement( + // Paper start - knockback events + Vec3 finalVelocity = new Vec3( - deltaMovement.x / 2.0 - vec3.x, - this.onGround() ? Math.min(0.4, deltaMovement.y / 2.0 + strength) : deltaMovement.y, - deltaMovement.z / 2.0 - vec3.z + deltaMovement.x / 2.0 - deltaVector.x, + this.onGround() ? Math.min(0.4, deltaMovement.y / 2.0 + power) : deltaMovement.y, + deltaMovement.z / 2.0 - deltaVector.z ); + Vec3 diff = finalVelocity.subtract(deltaMovement); -+ io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity) this.getBukkitEntity(), attacker, attacker, eventCause, strength, diff); ++ io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity)this.getBukkitEntity(), attacker, attacker, eventCause, power, diff); + // Paper end - knockback events + if (event.isCancelled()) { + return; @@ -1007,7 +1008,7 @@ } } -@@ -1712,7 +_,7 @@ +@@ -1709,7 +_,7 @@ @Override public boolean isAlive() { @@ -1015,83 +1016,84 @@ + return !this.isRemoved() && this.getHealth() > 0.0F && !this.dead; // Paper - Check this.dead } - public boolean isLookingAtMe(LivingEntity entity, double tolerance, boolean scaleByDistance, boolean visual, double... yValues) { -@@ -1746,9 +_,14 @@ - boolean flag = super.causeFallDamage(fallDistance, damageMultiplier, damageSource); - int i = this.calculateFallDamage(fallDistance, damageMultiplier); - if (i > 0) { + public boolean isLookingAtMe( +@@ -1764,10 +_,15 @@ + boolean damaged = super.causeFallDamage(effectiveFallDistance, damageModifier, damageSource); + int dmg = this.calculateFallDamage(effectiveFallDistance, damageModifier); + if (dmg > 0) { + // CraftBukkit start -+ if (!this.hurtServer((ServerLevel) this.level(), damageSource, (float) i)) { ++ if (!this.hurtServer((ServerLevel)this.level(), damageSource, (float)dmg)) { + return true; + } + // CraftBukkit end - this.playSound(this.getFallDamageSound(i), 1.0F, 1.0F); + this.resetCurrentImpulseContext(); + this.playSound(this.getFallDamageSound(dmg), 1.0F, 1.0F); this.playBlockFallSound(); -- this.hurt(damageSource, i); -+ // this.hurt(damageSource, i); // CraftBukkit - moved up +- this.hurt(damageSource, dmg); ++ // this.hurt(damageSource, dmg); // CraftBukkit - moved up return true; } else { - return flag; -@@ -1813,7 +_,7 @@ + return damaged; +@@ -1865,7 +_,7 @@ - protected float getDamageAfterArmorAbsorb(DamageSource damageSource, float damageAmount) { + protected float getDamageAfterArmorAbsorb(final DamageSource damageSource, float damage) { if (!damageSource.is(DamageTypeTags.BYPASSES_ARMOR)) { -- this.hurtArmor(damageSource, damageAmount); -+ // this.hurtArmor(damageSource, damageAmount); // CraftBukkit - actuallyHurt(DamageSource, float, EntityDamageEvent) for damage handling - damageAmount = CombatRules.getDamageAfterAbsorb( - this, damageAmount, damageSource, this.getArmorValue(), (float)this.getAttributeValue(Attributes.ARMOR_TOUGHNESS) +- this.hurtArmor(damageSource, damage); ++ // this.hurtArmor(damageSource, damage); // CraftBukkit - actuallyHurt(DamageSource, float, EntityDamageEvent) for damage handling + damage = CombatRules.getDamageAfterAbsorb( + this, damage, damageSource, this.getArmorValue(), (float)this.getAttributeValue(Attributes.ARMOR_TOUGHNESS) ); -@@ -1826,7 +_,8 @@ +@@ -1878,7 +_,8 @@ if (damageSource.is(DamageTypeTags.BYPASSES_EFFECTS)) { - return damageAmount; + return damage; } else { - if (this.hasEffect(MobEffects.RESISTANCE) && !damageSource.is(DamageTypeTags.BYPASSES_RESISTANCE)) { + // CraftBukkit - Moved to handleEntityDamage(DamageSource, float) + if (false && this.hasEffect(MobEffects.RESISTANCE) && !damageSource.is(DamageTypeTags.BYPASSES_RESISTANCE)) { - int i = (this.getEffect(MobEffects.RESISTANCE).getAmplifier() + 1) * 5; - int i1 = 25 - i; - float f = damageAmount * i1; -@@ -1863,24 +_,201 @@ + int absorbValue = (this.getEffect(MobEffects.RESISTANCE).getAmplifier() + 1) * 5; + int absorb = 25 - absorbValue; + float v = damage * absorb; +@@ -1915,24 +_,194 @@ } } -- protected void actuallyHurt(ServerLevel level, DamageSource damageSource, float amount) { +- protected void actuallyHurt(final ServerLevel level, final DamageSource source, float dmg) { + // CraftBukkit start -+ private EntityDamageEvent handleEntityDamage(final DamageSource damagesource, float amount, final float invulnerabilityRelatedLastDamage) { // Paper - fix invulnerability reduction in EntityDamageEvent -+ float originalDamage = amount; ++ private EntityDamageEvent handleEntityDamage(final DamageSource damagesource, float damage, final float invulnerabilityRelatedLastDamage) { // Paper - fix invulnerability reduction in EntityDamageEvent ++ float originalDamage = damage; + // Paper start - fix invulnerability reduction in EntityDamageEvent + final com.google.common.base.Function invulnerabilityReductionEquation = mod -> { -+ if (invulnerabilityRelatedLastDamage == 0) return 0D; // no last damage, no reduction ++ if (invulnerabilityRelatedLastDamage == 0) return 0.0; // no last damage, no reduction + // last damage existed, this means the reduction *technically* is (new damage - last damage). + // If the event damage was changed to something less than invul damage, hard lock it at 0. + // + // Cast the passed in double down to a float as double -> float -> double is lossy. -+ // If last damage is a (float) 3.2D (since the events use doubles), we cannot compare -+ // the new damage value of this damage instance by upcasting it again to a double as 3.2D != (double) (float) 3.2D. -+ if (mod.floatValue() < invulnerabilityRelatedLastDamage) return 0D; ++ // If last damage is a (float) 3.2 (since the events use doubles), we cannot compare ++ // the new damage value of this damage instance by upcasting it again to a double as 3.2 != (double) (float) 3.2. ++ if (mod.floatValue() < invulnerabilityRelatedLastDamage) return 0.0; + return (double) -invulnerabilityRelatedLastDamage; + }; -+ final float originalInvulnerabilityReduction = invulnerabilityReductionEquation.apply((double) amount).floatValue(); -+ amount += originalInvulnerabilityReduction; ++ final float originalInvulnerabilityReduction = invulnerabilityReductionEquation.apply((double) damage).floatValue(); ++ damage += originalInvulnerabilityReduction; + // Paper end - fix invulnerability reduction in EntityDamageEvent + + com.google.common.base.Function freezing = mod -> { -+ if (damagesource.is(net.minecraft.tags.DamageTypeTags.IS_FREEZING) && LivingEntity.this.getType().is(net.minecraft.tags.EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES)) { -+ return -(mod - (mod * 5.0F)); ++ if (damagesource.is(net.minecraft.tags.DamageTypeTags.IS_FREEZING) && LivingEntity.this.is(net.minecraft.tags.EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES)) { ++ return -(mod - mod * 5.0F); + } + return -0.0; + }; -+ float freezingModifier = freezing.apply((double) amount).floatValue(); -+ amount += freezingModifier; ++ float freezingModifier = freezing.apply((double) damage).floatValue(); ++ damage += freezingModifier; + + com.google.common.base.Function hardHat = mod -> { + if (damagesource.is(net.minecraft.tags.DamageTypeTags.DAMAGES_HELMET) && !LivingEntity.this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { -+ return -(mod - (mod * 0.75F)); ++ return -(mod - mod * 0.75F); + } + return -0.0; + }; -+ float hardHatModifier = hardHat.apply((double) amount).floatValue(); -+ amount += hardHatModifier; ++ float hardHatModifier = hardHat.apply((double) damage).floatValue(); ++ damage += hardHatModifier; + + com.google.common.base.Function blocking = mod -> { + if (!LivingEntity.this.canBlockAttack(damagesource, mod.floatValue())) { @@ -1099,38 +1101,32 @@ + } + return (double) -LivingEntity.this.resolveBlockedDamage(damagesource, mod.floatValue()); + }; -+ float blockingModifier = blocking.apply((double) amount).floatValue(); -+ amount += blockingModifier; ++ float blockingModifier = blocking.apply((double) damage).floatValue(); ++ damage += blockingModifier; + -+ com.google.common.base.Function armor = mod -> { -+ return -(mod - LivingEntity.this.getDamageAfterArmorAbsorb(damagesource, mod.floatValue())); -+ }; -+ float armorModifier = armor.apply((double) amount).floatValue(); -+ amount += armorModifier; ++ com.google.common.base.Function armor = mod -> -(mod - LivingEntity.this.getDamageAfterArmorAbsorb(damagesource, mod.floatValue())); ++ float armorModifier = armor.apply((double) damage).floatValue(); ++ damage += armorModifier; + + com.google.common.base.Function resistance = mod -> { + if (!damagesource.is(net.minecraft.tags.DamageTypeTags.BYPASSES_EFFECTS) && LivingEntity.this.hasEffect(net.minecraft.world.effect.MobEffects.RESISTANCE) && !damagesource.is(net.minecraft.tags.DamageTypeTags.BYPASSES_RESISTANCE)) { -+ int i = (LivingEntity.this.getEffect(net.minecraft.world.effect.MobEffects.RESISTANCE).getAmplifier() + 1) * 5; -+ int j = 25 - i; -+ float f1 = mod.floatValue() * (float) j; ++ int absorbValue = (LivingEntity.this.getEffect(net.minecraft.world.effect.MobEffects.RESISTANCE).getAmplifier() + 1) * 5; ++ int absorb = 25 - absorbValue; ++ float v = mod.floatValue() * (float) absorb; + -+ return -(mod - Math.max(f1 / 25.0F, 0.0F)); ++ return -(mod - Math.max(v / 25.0F, 0.0F)); + } + return -0.0; + }; -+ float resistanceModifier = resistance.apply((double) amount).floatValue(); -+ amount += resistanceModifier; ++ float resistanceModifier = resistance.apply((double) damage).floatValue(); ++ damage += resistanceModifier; + -+ com.google.common.base.Function magic = mod -> { -+ return -(mod - net.minecraft.world.entity.LivingEntity.this.getDamageAfterMagicAbsorb(damagesource, mod.floatValue())); -+ }; -+ float magicModifier = magic.apply((double) amount).floatValue(); -+ amount += magicModifier; ++ com.google.common.base.Function magic = mod -> -(mod - net.minecraft.world.entity.LivingEntity.this.getDamageAfterMagicAbsorb(damagesource, mod.floatValue())); ++ float magicModifier = magic.apply((double) damage).floatValue(); ++ damage += magicModifier; + -+ com.google.common.base.Function absorption = mod -> { -+ return -(Math.max(mod - Math.max(mod - net.minecraft.world.entity.LivingEntity.this.getAbsorptionAmount(), 0.0F), 0.0F)); -+ }; -+ float absorptionModifier = absorption.apply((double) amount).floatValue(); ++ com.google.common.base.Function absorption = mod -> -(Math.max(mod - Math.max(mod - net.minecraft.world.entity.LivingEntity.this.getAbsorptionAmount(), 0.0F), 0.0F)); ++ float absorptionModifier = absorption.apply((double) damage).floatValue(); + + // Paper start - fix invulnerability reduction in EntityDamageEvent + return CraftEventFactory.handleLivingEntityDamageEvent(this, damagesource, originalDamage, freezingModifier, hardHatModifier, blockingModifier, armorModifier, resistanceModifier, magicModifier, absorptionModifier, freezing, hardHat, blocking, armor, resistance, magic, absorption, (damageModifierDoubleMap, damageModifierFunctionMap) -> { @@ -1140,116 +1136,115 @@ + // Paper end - fix invulnerability reduction in EntityDamageEvent + } + -+ protected boolean actuallyHurt(ServerLevel level, final DamageSource damageSource, float amount, final EntityDamageEvent event) { // void -> boolean, add final - if (!this.isInvulnerableTo(level, damageSource)) { -- amount = this.getDamageAfterArmorAbsorb(damageSource, amount); -- amount = this.getDamageAfterMagicAbsorb(damageSource, amount); -- float var10 = Math.max(amount - this.getAbsorptionAmount(), 0.0F); -- this.setAbsorptionAmount(this.getAbsorptionAmount() - (amount - var10)); -- float f1 = amount - var10; ++ protected boolean actuallyHurt(ServerLevel level, final DamageSource source, float dmg, final EntityDamageEvent event) { // void -> boolean, add final + if (!this.isInvulnerableTo(level, source)) { +- dmg = this.getDamageAfterArmorAbsorb(source, dmg); +- dmg = this.getDamageAfterMagicAbsorb(source, dmg); +- float var10 = Math.max(dmg - this.getAbsorptionAmount(), 0.0F); +- this.setAbsorptionAmount(this.getAbsorptionAmount() - (dmg - var10)); +- float absorbedDamage = dmg - var10; + if (event.isCancelled()) { + return false; + } + -+ if (damageSource.getEntity() instanceof net.minecraft.world.entity.player.Player) { ++ if (source.getEntity() instanceof net.minecraft.world.entity.player.Player) { + // Paper start - PlayerAttackEntityCooldownResetEvent + //((net.minecraft.world.entity.player.Player) damagesource.getEntity()).resetOnlyAttackStrengthTicker(); // Moved from Player in order to make the cooldown reset get called after the damage event is fired -+ if (damageSource.getEntity() instanceof ServerPlayer) { -+ ServerPlayer player = (ServerPlayer) damageSource.getEntity(); ++ if (source.getEntity() instanceof ServerPlayer player) { + if (new com.destroystokyo.paper.event.player.PlayerAttackEntityCooldownResetEvent(player.getBukkitEntity(), this.getBukkitEntity(), player.getAttackStrengthScale(0F)).callEvent()) { + player.resetOnlyAttackStrengthTicker(); + } + } else { -+ ((net.minecraft.world.entity.player.Player) damageSource.getEntity()).resetOnlyAttackStrengthTicker(); ++ ((net.minecraft.world.entity.player.Player)source.getEntity()).resetOnlyAttackStrengthTicker(); + } + // Paper end - PlayerAttackEntityCooldownResetEvent + } + + // Resistance + if (event.getDamage(DamageModifier.RESISTANCE) < 0) { -+ float f3 = (float) -event.getDamage(DamageModifier.RESISTANCE); -+ if (f3 > 0.0F && f3 < 3.4028235E37F) { ++ float damageResisted = (float)-event.getDamage(DamageModifier.RESISTANCE); ++ if (damageResisted > 0.0F && damageResisted < 3.4028235E37F) { + if (this instanceof ServerPlayer) { -+ ((ServerPlayer) this).awardStat(Stats.DAMAGE_RESISTED, Math.round(f3 * 10.0F)); -+ } else if (damageSource.getEntity() instanceof ServerPlayer) { -+ ((ServerPlayer) damageSource.getEntity()).awardStat(Stats.DAMAGE_DEALT_RESISTED, Math.round(f3 * 10.0F)); ++ ((ServerPlayer)this).awardStat(Stats.DAMAGE_RESISTED, Math.round(damageResisted * 10.0F)); ++ } else if (source.getEntity() instanceof ServerPlayer) { ++ ((ServerPlayer)source.getEntity()).awardStat(Stats.DAMAGE_DEALT_RESISTED, Math.round(damageResisted * 10.0F)); + } + } + } + + // Apply damage to helmet -+ if (damageSource.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { -+ float helmetDamage = (float) event.getDamage(); -+ helmetDamage += (float) event.getDamage(DamageModifier.INVULNERABILITY_REDUCTION); -+ helmetDamage += (float) event.getDamage(DamageModifier.BLOCKING); -+ helmetDamage += (float) event.getDamage(DamageModifier.FREEZING); -+ this.hurtHelmet(damageSource, helmetDamage); ++ if (source.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { ++ float helmetDamage = (float)event.getDamage(); ++ helmetDamage += (float)event.getDamage(DamageModifier.INVULNERABILITY_REDUCTION); ++ helmetDamage += (float)event.getDamage(DamageModifier.BLOCKING); ++ helmetDamage += (float)event.getDamage(DamageModifier.FREEZING); ++ this.hurtHelmet(source, helmetDamage); + } + + // Apply damage to armor -+ if (!damageSource.is(DamageTypeTags.BYPASSES_ARMOR)) { -+ float armorDamage = (float) event.getDamage(); -+ armorDamage += (float) event.getDamage(DamageModifier.INVULNERABILITY_REDUCTION); -+ armorDamage += (float) event.getDamage(DamageModifier.BLOCKING); -+ armorDamage += (float) event.getDamage(DamageModifier.FREEZING); -+ armorDamage += (float) event.getDamage(DamageModifier.HARD_HAT); -+ this.hurtArmor(damageSource, armorDamage); ++ if (!source.is(DamageTypeTags.BYPASSES_ARMOR)) { ++ float armorDamage = (float)event.getDamage(); ++ armorDamage += (float)event.getDamage(DamageModifier.INVULNERABILITY_REDUCTION); ++ armorDamage += (float)event.getDamage(DamageModifier.BLOCKING); ++ armorDamage += (float)event.getDamage(DamageModifier.FREEZING); ++ armorDamage += (float)event.getDamage(DamageModifier.HARD_HAT); ++ this.hurtArmor(source, armorDamage); + } + + // Apply blocking code + if (event.getDamage(DamageModifier.BLOCKING) < 0) { -+ this.blockingItemEffects(level, damageSource, (float) -event.getDamage(DamageModifier.BLOCKING)); ++ this.blockingItemEffects(level, source, (float)-event.getDamage(DamageModifier.BLOCKING)); + } + + boolean human = this instanceof net.minecraft.world.entity.player.Player; -+ float originalDamage = (float) event.getDamage(); -+ float absorptionModifier = (float) -event.getDamage(DamageModifier.ABSORPTION); ++ float originalDamage = (float)event.getDamage(); ++ float absorptionModifier = (float)-event.getDamage(DamageModifier.ABSORPTION); + this.setAbsorptionAmount(Math.max(this.getAbsorptionAmount() - absorptionModifier, 0.0F)); -+ float f1 = absorptionModifier; ++ float absorbedDamage = absorptionModifier; + -+ if (f1 > 0.0F && f1 < 3.4028235E37F && this instanceof Player player) { -+ player.awardStat(Stats.DAMAGE_ABSORBED, Math.round(f1 * 10.0F)); ++ if (absorbedDamage > 0.0F && absorbedDamage < 3.4028235E37F && this instanceof Player player) { ++ player.awardStat(Stats.DAMAGE_ABSORBED, Math.round(absorbedDamage * 10.0F)); + } + // CraftBukkit end - if (f1 > 0.0F && f1 < 3.4028235E37F && damageSource.getEntity() instanceof ServerPlayer serverPlayer) { - serverPlayer.awardStat(Stats.DAMAGE_DEALT_ABSORBED, Math.round(f1 * 10.0F)); + if (absorbedDamage > 0.0F && absorbedDamage < 3.4028235E37F && source.getEntity() instanceof ServerPlayer serverPlayer) { + serverPlayer.awardStat(Stats.DAMAGE_DEALT_ABSORBED, Math.round(absorbedDamage * 10.0F)); } - if (var10 != 0.0F) { -- this.getCombatTracker().recordDamage(damageSource, var10); +- this.getCombatTracker().recordDamage(source, var10); - this.setHealth(this.getHealth() - var10); - this.setAbsorptionAmount(this.getAbsorptionAmount() - var10); + // CraftBukkit start -+ if (amount > 0 || !human) { ++ if (dmg > 0 || !human) { + if (human) { + // PAIL: Be sure to drag all this code from the EntityHuman subclass each update. -+ ((net.minecraft.world.entity.player.Player) this).causeFoodExhaustion(damageSource.getFoodExhaustion(), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.DAMAGED); // CraftBukkit - EntityExhaustionEvent -+ if (amount < 3.4028235E37F) { -+ ((net.minecraft.world.entity.player.Player) this).awardStat(Stats.DAMAGE_TAKEN, Math.round(amount * 10.0F)); ++ ((net.minecraft.world.entity.player.Player)this).causeFoodExhaustion(source.getFoodExhaustion(), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.DAMAGED); // CraftBukkit - EntityExhaustionEvent ++ if (dmg < 3.4028235E37F) { ++ ((net.minecraft.world.entity.player.Player)this).awardStat(Stats.DAMAGE_TAKEN, Math.round(dmg * 10.0F)); + } + } + // CraftBukkit end -+ this.getCombatTracker().recordDamage(damageSource, amount); -+ this.setHealth(this.getHealth() - amount); ++ this.getCombatTracker().recordDamage(source, dmg); ++ this.setHealth(this.getHealth() - dmg); + // CraftBukkit start + if (!human) { -+ this.setAbsorptionAmount(this.getAbsorptionAmount() - amount); ++ this.setAbsorptionAmount(this.getAbsorptionAmount() - dmg); + } this.gameEvent(GameEvent.ENTITY_DAMAGE); + return true; + } else { + // Duplicate triggers if blocking + if (event.getDamage(DamageModifier.BLOCKING) < 0) { -+ if (this instanceof ServerPlayer) { -+ CriteriaTriggers.ENTITY_HURT_PLAYER.trigger((ServerPlayer) this, damageSource, originalDamage, amount, true); // Paper - fix taken/dealt param order -+ f1 = (float) -event.getDamage(DamageModifier.BLOCKING); -+ if (f1 > 0.0F && f1 < 3.4028235E37F) { -+ ((ServerPlayer) this).awardStat(Stats.DAMAGE_BLOCKED_BY_SHIELD, Math.round(originalDamage * 10.0F)); ++ if (this instanceof ServerPlayer serverPlayer) { ++ CriteriaTriggers.ENTITY_HURT_PLAYER.trigger(serverPlayer, source, originalDamage, dmg, true); // Paper - fix taken/dealt param order ++ float damageBlocked = (float)-event.getDamage(DamageModifier.BLOCKING); ++ if (damageBlocked > 0.0F && damageBlocked < 3.4028235E37F) { ++ serverPlayer.awardStat(Stats.DAMAGE_BLOCKED_BY_SHIELD, Math.round(damageBlocked * 10.0F)); + } + } + -+ if (damageSource.getEntity() instanceof ServerPlayer) { -+ CriteriaTriggers.PLAYER_HURT_ENTITY.trigger((ServerPlayer) damageSource.getEntity(), this, damageSource, originalDamage, amount, true); // Paper - fix taken/dealt param order ++ if (source.getEntity() instanceof ServerPlayer) { ++ CriteriaTriggers.PLAYER_HURT_ENTITY.trigger((ServerPlayer)source.getEntity(), this, source, originalDamage, dmg, true); // Paper - fix taken/dealt param order + } + + return !io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.skipVanillaDamageTickWhenShieldBlocked; // Paper - this should always return true, however expose an unsupported setting to flip this to false to enable "shield stunning". @@ -1263,16 +1258,16 @@ } public CombatTracker getCombatTracker() { -@@ -1908,7 +_,17 @@ +@@ -1960,7 +_,17 @@ } - public final void setArrowCount(int count) { + public final void setArrowCount(final int count) { - this.entityData.set(DATA_ARROW_COUNT_ID, count); + // CraftBukkit start + this.setArrowCount(count, false); + } + -+ public final void setArrowCount(int count, boolean reset) { ++ public final void setArrowCount(final int count, final boolean reset) { + org.bukkit.event.entity.ArrowBodyCountChangeEvent event = CraftEventFactory.callArrowBodyCountChangeEvent(this, this.getArrowCount(), count, reset); + if (event.isCancelled()) { + return; @@ -1282,16 +1277,16 @@ } public final int getStingerCount() { -@@ -1955,7 +_,7 @@ +@@ -2010,7 +_,7 @@ @Override - public void handleDamageEvent(DamageSource damageSource) { + public void handleDamageEvent(final DamageSource source) { this.walkAnimation.setSpeed(1.5F); - this.invulnerableTime = 20; + this.invulnerableTime = this.invulnerableDuration; // Paper - configurable invulnerable duration this.hurtDuration = 10; this.hurtTime = this.hurtDuration; - SoundEvent hurtSound = this.getHurtSound(damageSource); -@@ -2084,7 +_,7 @@ + SoundEvent hurtSound = this.getHurtSound(source); +@@ -2139,7 +_,7 @@ @Override protected void onBelowWorld() { @@ -1300,25 +1295,22 @@ } protected void updateSwingTime() { -@@ -2187,8 +_,15 @@ +@@ -2246,7 +_,13 @@ } - public void setItemSlot(EquipmentSlot slot, ItemStack stack) { -- this.onEquipItem(slot, this.equipment.set(slot, stack), stack); -- } -+ // Paper start -+ this.setItemSlot(slot, stack, false); -+ } -+ // CraftBukkit start -+ public void setItemSlot(EquipmentSlot slot, ItemStack stack, boolean silent) { -+ // Paper end -+ this.onEquipItem(slot, this.equipment.set(slot, stack), stack, silent); + public void setItemSlot(final EquipmentSlot slot, final ItemStack itemStack) { +- this.onEquipItem(slot, this.equipment.set(slot, itemStack), itemStack); ++ // Paper start ++ this.setItemSlot(slot, itemStack, false); + } + ++ public void setItemSlot(final EquipmentSlot slot, final ItemStack itemStack, final boolean silent) { ++ this.onEquipItem(slot, this.equipment.set(slot, itemStack), itemStack, silent); ++ // Paper end + } public float getArmorCoverPercentage() { - int i = 0; -@@ -2280,14 +_,27 @@ +@@ -2339,14 +_,27 @@ return this.hasEffect(MobEffects.JUMP_BOOST) ? 0.1F * (this.getEffect(MobEffects.JUMP_BOOST).getAmplifier() + 1.0F) : 0.0F; } @@ -1327,7 +1319,7 @@ public void jumpFromGround() { float jumpPower = this.getJumpPower(); if (!(jumpPower <= 1.0E-5F)) { - Vec3 deltaMovement = this.getDeltaMovement(); + Vec3 movement = this.getDeltaMovement(); + // Paper start - Prevent excessive velocity through repeated crits + long time = System.nanoTime(); + boolean canCrit = true; @@ -1339,14 +1331,14 @@ + } + } + // Paper end - Prevent excessive velocity through repeated crits - this.setDeltaMovement(deltaMovement.x, Math.max((double)jumpPower, deltaMovement.y), deltaMovement.z); + this.setDeltaMovement(movement.x, Math.max((double)jumpPower, movement.y), movement.z); if (this.isSprinting()) { - float f = this.getYRot() * (float) (Math.PI / 180.0); + float angle = this.getYRot() * Mth.DEG_TO_RAD; + if (canCrit) // Paper - Prevent excessive velocity through repeated crits - this.addDeltaMovement(new Vec3(-Mth.sin(f) * 0.2, 0.0, Mth.cos(f) * 0.2)); + this.addDeltaMovement(new Vec3(-Mth.sin(angle) * 0.2, 0.0, Mth.cos(angle) * 0.2)); } -@@ -2470,8 +_,10 @@ +@@ -2533,8 +_,10 @@ } public void stopFallFlying() { @@ -1356,19 +1348,19 @@ + } // Paper } - private Vec3 updateFallFlyingMovement(Vec3 deltaMovement) { -@@ -2615,7 +_,7 @@ + private Vec3 updateFallFlyingMovement(Vec3 movement) { +@@ -2678,7 +_,7 @@ - public void causeExtraKnockback(Entity target, float strength, Vec3 currentMovement) { - if (strength > 0.0F && target instanceof LivingEntity livingEntity) { -- livingEntity.knockback(strength, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)), -Mth.cos(this.getYRot() * (float) (Math.PI / 180.0))); -+ livingEntity.knockback(strength, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)), -Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // Paper - knockback events + public void causeExtraKnockback(final Entity target, final float knockback, final Vec3 oldMovement) { + if (knockback > 0.0F && target instanceof LivingEntity livingTarget) { +- livingTarget.knockback(knockback, Mth.sin(this.getYRot() * Mth.DEG_TO_RAD), -Mth.cos(this.getYRot() * Mth.DEG_TO_RAD)); ++ livingTarget.knockback(knockback, Mth.sin(this.getYRot() * Mth.DEG_TO_RAD), -Mth.cos(this.getYRot() * Mth.DEG_TO_RAD), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // Paper - knockback events this.setDeltaMovement(this.getDeltaMovement().multiply(0.6, 1.0, 0.6)); } } -@@ -2691,37 +_,15 @@ - profilerFiller.pop(); - profilerFiller.push("rangeChecks"); +@@ -2754,37 +_,15 @@ + profiler.pop(); + profiler.push("rangeChecks"); - while (this.getYRot() - this.yRotO < -180.0F) { - this.yRotO -= 360.0F; @@ -1411,12 +1403,12 @@ + this.yHeadRotO += Math.round((this.yHeadRot - this.yHeadRotO) / 360.0F) * 360.0F; + // Paper end - stop large pitch and yaw changes from crashing the server - profilerFiller.pop(); + profiler.pop(); if (this.isFallFlying()) { -@@ -2806,16 +_,39 @@ +@@ -2879,16 +_,39 @@ private @Nullable Map collectEquipmentChanges() { - Map map = null; + Map changedItems = null; + // Paper start - EntityEquipmentChangedEvent + record EquipmentChangeImpl(org.bukkit.inventory.ItemStack oldItem, org.bukkit.inventory.ItemStack newItem) implements io.papermc.paper.event.entity.EntityEquipmentChangedEvent.EquipmentChange { + @Override @@ -1432,28 +1424,28 @@ + Map equipmentChanges = null; + // Paper end - EntityEquipmentChangedEvent - for (EquipmentSlot equipmentSlot : EquipmentSlot.VALUES) { - ItemStack itemStack = this.lastEquipmentItems.get(equipmentSlot); - ItemStack itemBySlot = this.getItemBySlot(equipmentSlot); - if (this.equipmentHasChanged(itemStack, itemBySlot)) { + for (EquipmentSlot slot : EquipmentSlot.VALUES) { + ItemStack previous = this.lastEquipmentItems.get(slot); + ItemStack current = this.getItemBySlot(slot); + if (this.equipmentHasChanged(previous, current)) { + // Paper start - EntityEquipmentChangedEvent, PlayerArmorChangeEvent -+ final org.bukkit.inventory.ItemStack oldItem = CraftItemStack.asBukkitCopy(itemStack); -+ final org.bukkit.inventory.ItemStack newItem = CraftItemStack.asBukkitCopy(itemBySlot); -+ if (this instanceof ServerPlayer && equipmentSlot.getType() == EquipmentSlot.Type.HUMANOID_ARMOR) { -+ new com.destroystokyo.paper.event.player.PlayerArmorChangeEvent((org.bukkit.entity.Player) this.getBukkitEntity(), com.destroystokyo.paper.event.player.PlayerArmorChangeEvent.SlotType.valueOf(equipmentSlot.name()), oldItem, newItem).callEvent(); ++ final org.bukkit.inventory.ItemStack oldItem = CraftItemStack.asBukkitCopy(previous); ++ final org.bukkit.inventory.ItemStack newItem = CraftItemStack.asBukkitCopy(current); ++ if (this instanceof ServerPlayer && slot.getType() == EquipmentSlot.Type.HUMANOID_ARMOR) { ++ new com.destroystokyo.paper.event.player.PlayerArmorChangeEvent((org.bukkit.entity.Player)this.getBukkitEntity(), com.destroystokyo.paper.event.player.PlayerArmorChangeEvent.SlotType.valueOf(slot.name()), oldItem, newItem).callEvent(); + } + // Paper end - EntityEquipmentChangedEvent, PlayerArmorChangeEvent - if (map == null) { - map = Maps.newEnumMap(EquipmentSlot.class); + if (changedItems == null) { + changedItems = Maps.newEnumMap(EquipmentSlot.class); + equipmentChanges = Maps.newEnumMap(org.bukkit.inventory.EquipmentSlot.class); // Paper - EntityEquipmentChangedEvent } - map.put(equipmentSlot, itemBySlot); -+ equipmentChanges.put(org.bukkit.craftbukkit.CraftEquipmentSlot.getSlot(equipmentSlot), new EquipmentChangeImpl(oldItem, newItem)); // Paper - EntityEquipmentChangedEvent + changedItems.put(slot, current); ++ equipmentChanges.put(org.bukkit.craftbukkit.CraftEquipmentSlot.getSlot(slot), new EquipmentChangeImpl(oldItem, newItem)); // Paper - EntityEquipmentChangedEvent AttributeMap attributes = this.getAttributes(); - if (!itemStack.isEmpty()) { - this.stopLocationBasedEffects(itemStack, equipmentSlot, attributes); -@@ -2840,6 +_,8 @@ + if (!previous.isEmpty()) { + this.stopLocationBasedEffects(previous, slot, attributes); +@@ -2913,6 +_,8 @@ } } } @@ -1461,20 +1453,20 @@ + new io.papermc.paper.event.entity.EntityEquipmentChangedEvent(this.getBukkitLivingEntity(), equipmentChanges).callEvent(); // Paper - EntityEquipmentChangedEvent } - return map; -@@ -2871,7 +_,7 @@ - list.add(Pair.of(equipmentSlot, itemStack1)); - this.lastEquipmentItems.put(equipmentSlot, itemStack1); + return changedItems; +@@ -2944,7 +_,7 @@ + itemsToSend.add(Pair.of(slot, newItemToStore)); + this.lastEquipmentItems.put(slot, newItemToStore); }); -- ((ServerLevel)this.level()).getChunkSource().sendToTrackingPlayers(this, new ClientboundSetEquipmentPacket(this.getId(), list)); -+ ((ServerLevel)this.level()).getChunkSource().sendToTrackingPlayers(this, new ClientboundSetEquipmentPacket(this.getId(), list, true)); // Paper - data sanitization +- ((ServerLevel)this.level()).getChunkSource().sendToTrackingPlayers(this, new ClientboundSetEquipmentPacket(this.getId(), itemsToSend)); ++ ((ServerLevel)this.level()).getChunkSource().sendToTrackingPlayers(this, new ClientboundSetEquipmentPacket(this.getId(), itemsToSend, true)); // Paper - data sanitization } - protected void tickHeadTurn(float yBodyRot) { -@@ -2957,8 +_,10 @@ - if (!flag || this.onGround() && !(fluidHeight > fluidJumpThreshold)) { + protected void tickHeadTurn(final float yBodyRotT) { +@@ -3030,8 +_,10 @@ + if (!inWaterAndHasFluidHeight || this.onGround() && !(fluidHeight > fluidJumpThreshold)) { if (!this.isInLava() || this.onGround() && !(fluidHeight > fluidJumpThreshold)) { - if ((this.onGround() || flag && fluidHeight <= fluidJumpThreshold) && this.noJumpDelay == 0) { + if ((this.onGround() || inWaterAndHasFluidHeight && fluidHeight <= fluidJumpThreshold) && this.noJumpDelay == 0) { + if (new com.destroystokyo.paper.event.entity.EntityJumpEvent(getBukkitLivingEntity()).callEvent()) { // Paper - Entity Jump API this.jumpFromGround(); this.noJumpDelay = 10; @@ -1482,19 +1474,19 @@ } } else { this.jumpInLiquid(FluidTags.LAVA); -@@ -2999,7 +_,7 @@ - profilerFiller.pop(); +@@ -3072,7 +_,7 @@ + profiler.pop(); if (this.level() instanceof ServerLevel serverLevel) { - profilerFiller.push("freezing"); + profiler.push("freezing"); - if (!this.isInPowderSnow || !this.canFreeze()) { + if ((!this.isInPowderSnow || !this.canFreeze()) && !this.freezeLocked) { // Paper - Freeze Tick Lock API this.setTicksFrozen(Math.max(0, this.getTicksFrozen() - 2)); } -@@ -3020,6 +_,20 @@ +@@ -3093,6 +_,20 @@ this.pushEntities(); - profilerFiller.pop(); + profiler.pop(); + // Paper start - Add EntityMoveEvent + if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof Player)) { + if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) { @@ -1512,7 +1504,7 @@ if (this.level() instanceof ServerLevel serverLevel && this.isSensitiveToWater() && this.isInWaterOrRain()) { this.hurtServer(serverLevel, this.damageSources().drown(), 1.0F); } -@@ -3042,6 +_,7 @@ +@@ -3115,6 +_,7 @@ this.checkFallDistanceAccumulation(); if (!this.level().isClientSide()) { if (!this.canGlide()) { @@ -1520,7 +1512,7 @@ this.setSharedFlag(Entity.FLAG_FALL_FLYING, false); return; } -@@ -3081,10 +_,25 @@ +@@ -3151,10 +_,25 @@ } protected void pushEntities() { @@ -1534,40 +1526,40 @@ + return; + } + -+ int i = ((ServerLevel) this.level()).getGameRules().get(GameRules.MAX_ENTITY_CRAMMING); -+ if (i <= 0 && this.level().paperConfig().collisions.maxEntityCollisions <= 0) { ++ int maxCramming = ((ServerLevel) this.level()).getGameRules().get(GameRules.MAX_ENTITY_CRAMMING); ++ if (maxCramming <= 0 && this.level().paperConfig().collisions.maxEntityCollisions <= 0) { + return; + } + // Paper end - don't run getEntities if we're not going to use its result List pushableEntities = this.level().getPushableEntities(this, this.getBoundingBox()); if (!pushableEntities.isEmpty()) { if (this.level() instanceof ServerLevel serverLevel) { -- int i = serverLevel.getGameRules().get(GameRules.MAX_ENTITY_CRAMMING); +- int maxCramming = serverLevel.getGameRules().get(GameRules.MAX_ENTITY_CRAMMING); + // Paper - don't run getEntities if we're not going to use its result; moved up - if (i > 0 && pushableEntities.size() > i - 1 && this.random.nextInt(4) == 0) { - int i1 = 0; + if (maxCramming > 0 && pushableEntities.size() > maxCramming - 1 && this.random.nextInt(4) == 0) { + int count = 0; -@@ -3100,7 +_,16 @@ +@@ -3170,7 +_,16 @@ } } + // Paper start - Cap entity collisions + this.numCollisions = Math.max(0, this.numCollisions - this.level().paperConfig().collisions.maxEntityCollisions); - for (Entity entity1 : pushableEntities) { + for (Entity entityx : pushableEntities) { + if (this.numCollisions >= this.level().paperConfig().collisions.maxEntityCollisions) { + break; + } + -+ entity1.numCollisions++; ++ entityx.numCollisions++; + this.numCollisions++; + // Paper end - Cap entity collisions - this.doPush(entity1); + this.doPush(entityx); } } -@@ -3109,16 +_,32 @@ - protected void checkAutoSpinAttack(AABB boundingBoxBeforeSpin, AABB boundingBoxAfterSpin) { - AABB aabb = boundingBoxBeforeSpin.minmax(boundingBoxAfterSpin); - List entities = this.level().getEntities(this, aabb); +@@ -3179,16 +_,32 @@ + protected void checkAutoSpinAttack(final AABB old, final AABB current) { + AABB minmax = old.minmax(current); + List entities = this.level().getEntities(this, minmax); + int skippedAttackedEntitiesCounter = 0; // Paper - entity attempt spin attack event / hidden entities - count entities that are retroactively not part of the list if (!entities.isEmpty()) { for (Entity entity : entities) { @@ -1598,49 +1590,49 @@ this.autoSpinAttackTicks = 0; } -@@ -3141,10 +_,10 @@ +@@ -3211,10 +_,10 @@ } @Override - public void stopRiding() { -+ public void stopRiding(boolean suppressCancellation) { // Paper - Force entity dismount during teleportation - Entity vehicle = this.getVehicle(); ++ public void stopRiding(final boolean suppressCancellation) { // Paper - Force entity dismount during teleportation + Entity oldVehicle = this.getVehicle(); - super.stopRiding(); -- if (vehicle != null && vehicle != this.getVehicle() && !this.level().isClientSide()) { +- if (oldVehicle != null && oldVehicle != this.getVehicle() && !this.level().isClientSide()) { + super.stopRiding(suppressCancellation); // Paper - Force entity dismount during teleportation -+ if (vehicle != null && vehicle != this.getVehicle() && !this.level().isClientSide() && vehicle.valid) { // Paper - don't process on world gen - this.dismountVehicle(vehicle); ++ if (oldVehicle != null && oldVehicle != this.getVehicle() && !this.level().isClientSide() && oldVehicle.valid) { // Paper - don't process on world gen + this.dismountVehicle(oldVehicle); } } -@@ -3171,7 +_,7 @@ +@@ -3241,7 +_,7 @@ } - public void onItemPickup(ItemEntity itemEntity) { -- Entity owner = itemEntity.getOwner(); -+ Entity owner = EntityReference.getEntity(itemEntity.thrower, this.level()::getGlobalPlayerByUUID, Entity.class); // Paper - check global player list where appropriate - if (owner instanceof ServerPlayer) { - CriteriaTriggers.THROWN_ITEM_PICKED_UP_BY_ENTITY.trigger((ServerPlayer)owner, itemEntity.getItem(), this); + public void onItemPickup(final ItemEntity entity) { +- Entity thrower = entity.getOwner(); ++ Entity thrower = EntityReference.getEntity(entity.thrower, this.level()::getGlobalPlayerByUUID, Entity.class); // Paper - check global player list where appropriate + if (thrower instanceof ServerPlayer) { + CriteriaTriggers.THROWN_ITEM_PICKED_UP_BY_ENTITY.trigger((ServerPlayer)thrower, entity.getItem(), this); } -@@ -3183,7 +_,7 @@ +@@ -3253,7 +_,7 @@ && (entity instanceof ItemEntity || entity instanceof AbstractArrow || entity instanceof ExperienceOrb)) { ((ServerLevel)this.level()) .getChunkSource() -- .sendToTrackingPlayers(entity, new ClientboundTakeItemEntityPacket(entity.getId(), this.getId(), amount)); -+ .sendToTrackingPlayersAndSelf(entity, new ClientboundTakeItemEntityPacket(entity.getId(), this.getId(), amount)); // Paper - broadcast with collector as source +- .sendToTrackingPlayers(entity, new ClientboundTakeItemEntityPacket(entity.getId(), this.getId(), orgCount)); ++ .sendToTrackingPlayersAndSelf(entity, new ClientboundTakeItemEntityPacket(entity.getId(), this.getId(), orgCount)); // Paper - broadcast with collector as source } } -@@ -3197,7 +_,8 @@ +@@ -3269,7 +_,8 @@ } else { - Vec3 vec3 = new Vec3(this.getX(), this.getEyeY(), this.getZ()); - Vec3 vec31 = new Vec3(entity.getX(), y, entity.getZ()); -- return !(vec31.distanceTo(vec3) > 128.0) && this.level().clip(new ClipContext(vec3, vec31, block, fluid, this)).getType() == HitResult.Type.MISS; + Vec3 from = new Vec3(this.getX(), this.getEyeY(), this.getZ()); + Vec3 to = new Vec3(target.getX(), eyeHeight, target.getZ()); +- return !(to.distanceTo(from) > 128.0) + // Paper - diff on change - used in CraftLivingEntity#hasLineOfSight(Location) and CraftWorld#lineOfSightExists -+ return !(vec31.distanceToSqr(vec3) > 128.0D * 128.0D) && this.level().clip(new ClipContext(vec3, vec31, block, fluid, this)).getType() == HitResult.Type.MISS; // Paper - Perf: Use distance squared ++ return !(to.distanceToSqr(from) > Mth.square(128.0)) // Paper - Perf: Use distance squared + && this.level().clip(new ClipContext(from, to, blockCollidingContext, fluidCollidingContext, this)).getType() == HitResult.Type.MISS; } } - -@@ -3217,13 +_,27 @@ +@@ -3290,13 +_,27 @@ @Override public boolean isPickable() { @@ -1671,16 +1663,16 @@ @Override public float getYHeadRot() { -@@ -3254,7 +_,7 @@ +@@ -3327,7 +_,7 @@ } - public final void setAbsorptionAmount(float absorptionAmount) { + public final void setAbsorptionAmount(final float absorptionAmount) { - this.internalSetAbsorptionAmount(Mth.clamp(absorptionAmount, 0.0F, this.getMaxAbsorption())); + this.internalSetAbsorptionAmount(!Float.isNaN(absorptionAmount) ? Mth.clamp(absorptionAmount, 0.0F, this.getMaxAbsorption()) : 0.0F); // Paper - Check for NaN } - protected void internalSetAbsorptionAmount(float absorptionAmount) { -@@ -3281,6 +_,15 @@ + protected void internalSetAbsorptionAmount(final float absorptionAmount) { +@@ -3354,6 +_,15 @@ return (this.entityData.get(DATA_LIVING_ENTITY_FLAGS) & 2) > 0 ? InteractionHand.OFF_HAND : InteractionHand.MAIN_HAND; } @@ -1696,43 +1688,43 @@ private void updatingUsingItem() { if (this.isUsingItem()) { if (ItemStack.isSameItem(this.getItemInHand(this.getUsedItemHand()), this.useItem)) { -@@ -3328,7 +_,12 @@ +@@ -3401,7 +_,12 @@ - protected void updateUsingItem(ItemStack usingItem) { - usingItem.onUseTick(this.level(), this, this.getUseItemRemainingTicks()); -- if (--this.useItemRemaining == 0 && !this.level().isClientSide() && !usingItem.useOnRelease()) { + protected void updateUsingItem(final ItemStack useItem) { + useItem.onUseTick(this.level(), this, this.getUseItemRemainingTicks()); +- if (--this.useItemRemaining == 0 && !this.level().isClientSide() && !useItem.useOnRelease()) { + // Paper start - lag compensate eating + // we add 1 to the expected time to avoid lag compensating when we should not + final boolean shouldLagCompensate = this.useItem.has(DataComponents.FOOD) && this.eatStartTime != -1 && (System.nanoTime() - this.eatStartTime) > ((1L + this.totalEatTimeTicks) * 50L * (1000L * 1000L)); -+ if ((--this.useItemRemaining == 0 || shouldLagCompensate) && !this.level().isClientSide() && !usingItem.useOnRelease()) { ++ if ((--this.useItemRemaining == 0 || shouldLagCompensate) && !this.level().isClientSide() && !useItem.useOnRelease()) { + this.useItemRemaining = 0; + // Paper end - lag compensate eating this.completeUsingItem(); } } -@@ -3354,10 +_,19 @@ +@@ -3427,10 +_,19 @@ } - public void startUsingItem(InteractionHand hand) { + public void startUsingItem(final InteractionHand hand) { + // Paper start - Prevent consuming the wrong itemstack + this.startUsingItem(hand, false); + } + -+ public void startUsingItem(InteractionHand hand, boolean forceUpdate) { ++ public void startUsingItem(final InteractionHand hand, final boolean forceUpdate) { + // Paper end - Prevent consuming the wrong itemstack - ItemStack itemInHand = this.getItemInHand(hand); -- if (!itemInHand.isEmpty() && !this.isUsingItem()) { -+ if (!itemInHand.isEmpty() && !this.isUsingItem() || forceUpdate) { // Paper - Prevent consuming the wrong itemstack - this.useItem = itemInHand; -- this.useItemRemaining = itemInHand.getUseDuration(this); + ItemStack itemStack = this.getItemInHand(hand); +- if (!itemStack.isEmpty() && !this.isUsingItem()) { ++ if ((!itemStack.isEmpty() && !this.isUsingItem()) || forceUpdate) { // Paper - Prevent consuming the wrong itemstack + this.useItem = itemStack; +- this.useItemRemaining = itemStack.getUseDuration(this); + // Paper start - lag compensate eating -+ this.useItemRemaining = this.totalEatTimeTicks = itemInHand.getUseDuration(this); ++ this.useItemRemaining = this.totalEatTimeTicks = itemStack.getUseDuration(this); + this.eatStartTime = System.nanoTime(); + // Paper end - lag compensate eating if (!this.level().isClientSide()) { this.setLivingEntityFlag(LIVING_ENTITY_FLAG_IS_USING, true); this.setLivingEntityFlag(LIVING_ENTITY_FLAG_OFF_HAND, hand == InteractionHand.OFF_HAND); -@@ -3384,7 +_,10 @@ +@@ -3457,7 +_,10 @@ } } else if (!this.isUsingItem() && !this.useItem.isEmpty()) { this.useItem = ItemStack.EMPTY; @@ -1744,19 +1736,19 @@ } } } -@@ -3423,9 +_,41 @@ +@@ -3500,9 +_,41 @@ this.releaseUsingItem(); } else { if (!this.useItem.isEmpty() && this.isUsingItem()) { -- ItemStack itemStack = this.useItem.finishUsingItem(this.level(), this); +- ItemStack result = this.useItem.finishUsingItem(this.level(), this); + this.startUsingItem(this.getUsedItemHand(), true); // Paper - Prevent consuming the wrong itemstack + // CraftBukkit start - fire PlayerItemConsumeEvent -+ ItemStack itemStack; ++ ItemStack result; + org.bukkit.event.player.PlayerItemConsumeEvent event = null; // Paper + if (this instanceof ServerPlayer serverPlayer) { + org.bukkit.inventory.ItemStack craftItem = CraftItemStack.asBukkitCopy(this.useItem); -+ org.bukkit.inventory.EquipmentSlot hand = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(usedItemHand); -+ event = new org.bukkit.event.player.PlayerItemConsumeEvent((org.bukkit.entity.Player) this.getBukkitEntity(), craftItem, hand); // Paper ++ org.bukkit.inventory.EquipmentSlot handSlot = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand); ++ event = new org.bukkit.event.player.PlayerItemConsumeEvent((org.bukkit.entity.Player)this.getBukkitEntity(), craftItem, handSlot); // Paper + this.level().getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { @@ -1764,38 +1756,38 @@ + if (consumable != null) { + consumable.cancelUsingItem(serverPlayer, this.useItem); + } -+ serverPlayer.containerMenu.forceHeldSlot(usedItemHand); ++ serverPlayer.containerMenu.forceHeldSlot(hand); + serverPlayer.getBukkitEntity().updateScaledHealth(); + this.stopUsingItem(); // Paper - event is using an item, clear active item to reset its use + return; + } + -+ itemStack = (craftItem.equals(event.getItem())) ? this.useItem.finishUsingItem(this.level(), this) : CraftItemStack.asNMSCopy(event.getItem()).finishUsingItem(this.level(), this); ++ result = (craftItem.equals(event.getItem())) ? this.useItem.finishUsingItem(this.level(), this) : CraftItemStack.asNMSCopy(event.getItem()).finishUsingItem(this.level(), this); + } else { -+ itemStack = this.useItem.finishUsingItem(this.level(), this); ++ result = this.useItem.finishUsingItem(this.level(), this); + } + // Paper start - save the default replacement item and change it if necessary -+ final ItemStack defaultReplacement = itemStack; ++ final ItemStack defaultReplacement = result; + if (event != null && event.getReplacement() != null) { -+ itemStack = CraftItemStack.asNMSCopy(event.getReplacement()); ++ result = CraftItemStack.asNMSCopy(event.getReplacement()); + } + // Paper end + // CraftBukkit end - if (itemStack != this.useItem) { - this.setItemInHand(usedItemHand, itemStack); -+ if (event != null && event.getReplacement() != null && this instanceof final ServerPlayer player) player.containerMenu.forceHeldSlot(usedItemHand); // Paper - Fix inventory desync; Paper#13253 + if (result != this.useItem) { + this.setItemInHand(hand, result); ++ if (event != null && event.getReplacement() != null && this instanceof final ServerPlayer player) player.containerMenu.forceHeldSlot(hand); // Paper - Fix inventory desync; Paper#13253 } this.stopUsingItem(); -@@ -3457,6 +_,7 @@ - ItemStack itemInHand = this.getItemInHand(this.getUsedItemHand()); - if (!this.useItem.isEmpty() && ItemStack.isSameItem(itemInHand, this.useItem)) { - this.useItem = itemInHand; +@@ -3534,6 +_,7 @@ + ItemStack itemInUsedHand = this.getItemInHand(this.getUsedItemHand()); + if (!this.useItem.isEmpty() && ItemStack.isSameItem(itemInUsedHand, this.useItem)) { + this.useItem = itemInUsedHand; + if (this instanceof ServerPlayer) new io.papermc.paper.event.player.PlayerStopUsingItemEvent((org.bukkit.entity.Player) getBukkitEntity(), useItem.asBukkitMirror(), getTicksUsingItem()).callEvent(); // Paper - Add PlayerStopUsingItemEvent this.useItem.releaseUsing(this.level(), this, this.getUseItemRemainingTicks()); if (this.useItem.useOnRelease()) { this.updatingUsingItem(); -@@ -3477,7 +_,10 @@ +@@ -3554,7 +_,10 @@ } this.useItem = ItemStack.EMPTY; @@ -1807,7 +1799,7 @@ } public boolean isBlocking() { -@@ -3500,6 +_,60 @@ +@@ -3577,6 +_,60 @@ } } @@ -1841,9 +1833,9 @@ + Vec3 direction = this.getLookAngle(); + Vec3 end = start.add(direction.x * maxDistance, direction.y * maxDistance, direction.z * maxDistance); + -+ List entityList = this.level().getEntities(this, getBoundingBox().expandTowards(direction.x * maxDistance, direction.y * maxDistance, direction.z * maxDistance).inflate(1.0D, 1.0D, 1.0D), EntitySelector.NO_SPECTATORS.and(Entity::isPickable)); ++ List entityList = this.level().getEntities(this, getBoundingBox().expandTowards(direction.x * maxDistance, direction.y * maxDistance, direction.z * maxDistance).inflate(1.0), EntitySelector.NO_SPECTATORS.and(Entity::isPickable)); + -+ double distance = 0.0D; ++ double distance = 0.0; + net.minecraft.world.phys.EntityHitResult result = null; + + for (Entity entity : entityList) { @@ -1854,7 +1846,7 @@ + if (rayTraceResult.isPresent()) { + Vec3 rayTrace = rayTraceResult.get(); + double distanceTo = start.distanceToSqr(rayTrace); -+ if (distanceTo < distance || distance == 0.0D) { ++ if (distanceTo < distance || distance == 0.0) { + result = new net.minecraft.world.phys.EntityHitResult(entity, rayTrace); + distance = distanceTo; + } @@ -1868,36 +1860,36 @@ public boolean isSuppressingSlidingDownLadder() { return this.isShiftKeyDown(); } -@@ -3518,6 +_,12 @@ +@@ -3595,6 +_,12 @@ } - public boolean randomTeleport(double x, double y, double z, boolean broadcastTeleport) { + public boolean randomTeleport(final double xx, final double yy, final double zz, final boolean showParticles) { + // CraftBukkit start -+ return this.randomTeleport(x, y, z, broadcastTeleport, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN).orElse(false); ++ return this.randomTeleport(xx, yy, zz, showParticles, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN).orElse(false); + } + -+ public Optional randomTeleport(double x, double y, double z, boolean broadcastTeleport, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { ++ public Optional randomTeleport(final double xx, final double yy, final double zz, final boolean showParticles, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { + // CraftBukkit end - double x1 = this.getX(); - double y1 = this.getY(); - double z1 = this.getZ(); -@@ -3540,16 +_,39 @@ + double xo = this.getX(); + double yo = this.getY(); + double zo = this.getZ(); +@@ -3617,16 +_,39 @@ } - if (flag1) { -- this.teleportTo(x, d, z); + if (landed) { +- this.teleportTo(xx, y, zz); + // CraftBukkit start - Teleport event + // first set position, to check if the place to teleport is valid -+ this.setPos(x, d, z); ++ this.setPos(xx, y, zz); if (level.noCollision(this) && !level.containsAnyLiquid(this.getBoundingBox())) { - flag = true; + ok = true; } + // now revert and call event if the teleport place is valid -+ this.setPos(x1, y1, z1); ++ this.setPos(xo, yo, zo); + -+ if (flag) { ++ if (ok) { + if (!(this instanceof ServerPlayer)) { -+ org.bukkit.event.entity.EntityTeleportEvent teleport = new org.bukkit.event.entity.EntityTeleportEvent(this.getBukkitEntity(), new Location(this.level().getWorld(), x1, y1, z1), new Location(this.level().getWorld(), x, d, z)); ++ org.bukkit.event.entity.EntityTeleportEvent teleport = new org.bukkit.event.entity.EntityTeleportEvent(this.getBukkitEntity(), new Location(this.level().getWorld(), xo, yo, zo), new Location(this.level().getWorld(), xx, y, zz)); + this.level().getCraftServer().getPluginManager().callEvent(teleport); + if (!teleport.isCancelled() && teleport.getTo() != null) { // Paper + Location to = teleport.getTo(); @@ -1907,7 +1899,7 @@ + } + } else { + // player teleport event is called in the underlining code -+ if (!((ServerPlayer) this).connection.teleport(x, d, z, this.getYRot(), this.getXRot(), cause)) { ++ if (!((ServerPlayer) this).connection.teleport(xx, y, zz, this.getYRot(), this.getXRot(), cause)) { + return Optional.empty(); + } + } @@ -1916,15 +1908,15 @@ } } - if (!flag) { -- this.teleportTo(x1, y1, z1); + if (!ok) { +- this.teleportTo(xo, yo, zo); - return false; -+ // this.teleportTo(x1, y1, z1); // CraftBukkit - already set the location back ++ // this.teleportTo(xo, yo, zo); // CraftBukkit - already set the location back + return Optional.of(false); // CraftBukkit } else { - if (broadcastTeleport) { + if (showParticles) { level.broadcastEntityEvent(this, EntityEvent.TELEPORT); -@@ -3559,7 +_,7 @@ +@@ -3636,7 +_,7 @@ pathfinderMob.getNavigation().stop(); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Mob.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Mob.java.patch index 49ab35ca45a1..fa3e3bce9f23 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/Mob.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/Mob.java.patch @@ -12,7 +12,7 @@ +import org.bukkit.event.entity.EntityUnleashEvent; +// CraftBukkit end + - public abstract class Mob extends LivingEntity implements EquipmentUser, Leashable, Targeting { + public abstract class Mob extends LivingEntity implements Targeting, EquipmentUser, Leashable { private static final EntityDataAccessor DATA_MOB_FLAGS_ID = SynchedEntityData.defineId(Mob.class, EntityDataSerializers.BYTE); private static final int MOB_FLAG_NO_AI = 1; @@ -127,6 +_,7 @@ @@ -30,62 +30,55 @@ + public boolean aware = true; // CraftBukkit + public net.kyori.adventure.util.TriState despawnInPeacefulOverride = net.kyori.adventure.util.TriState.NOT_SET; // Paper - allow changing despawnInPeaceful - protected Mob(EntityType type, Level level) { + protected Mob(final EntityType type, final Level level) { super(type, level); -@@ -155,6 +_,12 @@ - } - } - -+ // CraftBukkit start -+ public void setPersistenceRequired(boolean persistenceRequired) { -+ this.persistenceRequired = persistenceRequired; -+ } -+ // CraftBukkit end -+ - protected void registerGoals() { - } - -@@ -232,7 +_,39 @@ +@@ -244,7 +_,44 @@ } - public void setTarget(@Nullable LivingEntity target) { + public void setTarget(final @Nullable LivingEntity target) { +- this.target = this.asValidTarget(target); + // CraftBukkit start - fire event + this.setTarget(target, EntityTargetEvent.TargetReason.UNKNOWN); + } + + public boolean setTarget(@Nullable LivingEntity target, EntityTargetEvent.@Nullable TargetReason reason) { -+ if (this.getTarget() == target) { ++ LivingEntity currentTarget = this.getTargetUnchecked(); ++ if (Objects.equals(currentTarget, target)) { + return false; + } ++ LivingEntity originalTarget = target; ++ target = asValidTarget(target); + if (reason != null) { -+ if (reason == EntityTargetEvent.TargetReason.UNKNOWN && this.getTarget() != null && target == null) { -+ reason = this.getTarget().isAlive() ? EntityTargetEvent.TargetReason.FORGOT_TARGET : EntityTargetEvent.TargetReason.TARGET_DIED; ++ if (target != originalTarget) { // target got pruned in asValidTarget ++ reason = org.bukkit.craftbukkit.event.CraftEventFactory.getForgotTargetReason(this, currentTarget, true); ++ originalTarget = target; ++ } else if (reason == EntityTargetEvent.TargetReason.FORGOT_TARGET || (reason == EntityTargetEvent.TargetReason.UNKNOWN && target == null)) { // try to get a more relevant reason ++ reason = org.bukkit.craftbukkit.event.CraftEventFactory.getForgotTargetReason(this, currentTarget, false); + } + if (reason == EntityTargetEvent.TargetReason.UNKNOWN) { + this.level().getCraftServer().getLogger().log(java.util.logging.Level.WARNING, "Unknown target reason, please report on the issue tracker", new Exception()); + } -+ org.bukkit.craftbukkit.entity.CraftLivingEntity ctarget = null; -+ if (target != null) { -+ ctarget = (org.bukkit.craftbukkit.entity.CraftLivingEntity) target.getBukkitEntity(); -+ } -+ org.bukkit.event.entity.EntityTargetLivingEntityEvent event = new org.bukkit.event.entity.EntityTargetLivingEntityEvent(this.getBukkitEntity(), ctarget, reason); -+ if (!event.callEvent()) { ++ org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(this, target, reason); ++ if (event.isCancelled()) { + return false; + } + + if (event.getTarget() != null) { + target = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle(); ++ if (target != originalTarget) { ++ target = asValidTarget(target); ++ } + } else { + target = null; + } + } - this.target = target; ++ this.target = target; + return true; + // CraftBukkit end } @Override -@@ -371,13 +_,27 @@ +@@ -383,13 +_,27 @@ if (this.isNoAi()) { output.putBoolean("NoAI", this.isNoAi()); } @@ -98,7 +91,7 @@ } @Override - protected void readAdditionalSaveData(ValueInput input) { + protected void readAdditionalSaveData(final ValueInput input) { super.readAdditionalSaveData(input); - this.setCanPickUpLoot(input.getBooleanOr("CanPickUpLoot", false)); - this.persistenceRequired = input.getBooleanOr("PersistenceRequired", false); @@ -115,7 +108,7 @@ this.dropChances = input.read("drop_chances", DropChances.CODEC).orElse(DropChances.DEFAULT); this.readLeashData(input); this.homeRadius = input.getIntOr("home_radius", -1); -@@ -389,6 +_,13 @@ +@@ -401,6 +_,13 @@ this.lootTable = input.read("DeathLootTable", LootTable.KEY_CODEC); this.lootTableSeed = input.getLongOr("DeathLootTableSeed", 0L); this.setNoAi(input.getBooleanOr("NoAI", false)); @@ -129,67 +122,67 @@ } @Override -@@ -456,6 +_,11 @@ - && !itemEntity.getItem().isEmpty() - && !itemEntity.hasPickUpDelay() - && this.wantsToPickUp(serverLevel, itemEntity.getItem())) { +@@ -465,6 +_,11 @@ + for (ItemEntity entity : this.level() + .getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(pickupReach.getX(), pickupReach.getY(), pickupReach.getZ()))) { + if (!entity.isRemoved() && !entity.getItem().isEmpty() && !entity.hasPickUpDelay() && this.wantsToPickUp(serverLevel, entity.getItem())) { + // Paper start - Item#canEntityPickup -+ if (!itemEntity.canMobPickup) { ++ if (!entity.canMobPickup) { + continue; + } + // Paper end - Item#canEntityPickup - this.pickUpItem(serverLevel, itemEntity); + this.pickUpItem(serverLevel, entity); } } -@@ -509,18 +_,24 @@ +@@ -515,18 +_,24 @@ - protected void pickUpItem(ServerLevel level, ItemEntity entity) { - ItemStack item = entity.getItem(); -- ItemStack itemStack = this.equipItemIfPossible(level, item.copy()); -+ ItemStack itemStack = this.equipItemIfPossible(level, item.copy(), entity); // CraftBukkit - add item - if (!itemStack.isEmpty()) { + protected void pickUpItem(final ServerLevel level, final ItemEntity entity) { + ItemStack itemStack = entity.getItem(); +- ItemStack equippedWithStack = this.equipItemIfPossible(level, itemStack.copy()); ++ ItemStack equippedWithStack = this.equipItemIfPossible(level, itemStack.copy(), entity); // CraftBukkit - add item + if (!equippedWithStack.isEmpty()) { this.onItemPickup(entity); - this.take(entity, itemStack.getCount()); - item.shrink(itemStack.getCount()); - if (item.isEmpty()) { + this.take(entity, equippedWithStack.getCount()); + itemStack.shrink(equippedWithStack.getCount()); + if (itemStack.isEmpty()) { - entity.discard(); + entity.discard(EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause } } } - public ItemStack equipItemIfPossible(ServerLevel level, ItemStack stack) { + public ItemStack equipItemIfPossible(final ServerLevel level, final ItemStack itemStack) { + // CraftBukkit start - add item -+ return this.equipItemIfPossible(level, stack, null); ++ return this.equipItemIfPossible(level, itemStack, null); + } + -+ public ItemStack equipItemIfPossible(ServerLevel level, ItemStack stack, @Nullable ItemEntity entity) { ++ public ItemStack equipItemIfPossible(final ServerLevel level, final ItemStack itemStack, final @Nullable ItemEntity entity) { + // CraftBukkit end - EquipmentSlot equipmentSlotForItem = this.getEquipmentSlotForItem(stack); - if (!this.isEquippableInSlot(stack, equipmentSlotForItem)) { + EquipmentSlot slot = this.getEquipmentSlotForItem(itemStack); + if (!this.isEquippableInSlot(itemStack, slot)) { return ItemStack.EMPTY; -@@ -533,10 +_,18 @@ - canReplaceCurrentItem = itemBySlot.isEmpty(); +@@ -539,10 +_,18 @@ + canReplace = current.isEmpty(); } -- if (canReplaceCurrentItem && this.canHoldItem(stack)) { +- if (canReplace && this.canHoldItem(itemStack)) { + // CraftBukkit start -+ boolean canPickup = canReplaceCurrentItem && this.canHoldItem(stack); ++ boolean canPickup = canReplace && this.canHoldItem(itemStack); + if (entity != null) { + canPickup = !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(this, entity, 0, !canPickup).isCancelled(); + } + if (canPickup) { + // CraftBukkit end - double d = this.dropChances.byEquipment(equipmentSlotForItem); - if (!itemBySlot.isEmpty() && Math.max(this.random.nextFloat() - 0.1F, 0.0F) < d) { + double dropChance = this.dropChances.byEquipment(slot); + if (!current.isEmpty() && Math.max(this.random.nextFloat() - 0.1F, 0.0F) < dropChance) { + this.forceDrops = true; // CraftBukkit - this.spawnAtLocation(level, itemBySlot); + this.spawnAtLocation(level, current); + this.forceDrops = false; // CraftBukkit } - ItemStack itemStack = equipmentSlotForItem.limit(stack); -@@ -649,25 +_,38 @@ - return this.isPassenger(); + ItemStack toEquip = slot.limit(itemStack); +@@ -651,25 +_,38 @@ + return this.isPassenger() || this.isLeashed(); } + // Paper start - allow changing despawnInPeaceful @@ -205,33 +198,32 @@ + if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) { // Paper - allow changing despawnInPeaceful + this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause } else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) { -- Entity nearestPlayer = this.level().getNearestPlayer(this, -1.0); -+ Entity nearestPlayer = this.level().findNearbyPlayer(this, -1.0, EntitySelector.PLAYER_AFFECTS_SPAWNING); // Paper - Affects Spawning API - if (nearestPlayer != null) { -- double d = nearestPlayer.distanceToSqr(this); -- int despawnDistance = this.getType().getCategory().getDespawnDistance(); -- int i = despawnDistance * despawnDistance; -- if (d > i && this.removeWhenFarAway(d)) { +- Entity player = this.level().getNearestPlayer(this, -1.0); ++ Entity player = this.level().findNearbyPlayer(this, -1.0, EntitySelector.PLAYER_AFFECTS_SPAWNING); // Paper - Affects Spawning API + if (player != null) { +- double distSqr = player.distanceToSqr(this); +- int instantDespawnDistance = this.getType().getCategory().getDespawnDistance(); +- int despawnDistanceSqr = instantDespawnDistance * instantDespawnDistance; +- if (distSqr > despawnDistanceSqr && this.removeWhenFarAway(distSqr)) { - this.discard(); -- } + // Paper start - Configurable despawn distances + final io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DespawnRangePair despawnRangePair = this.level().paperConfig().entities.spawning.despawnRanges.get(this.getType().getCategory()); + final io.papermc.paper.configuration.type.DespawnRange.Shape shape = this.level().paperConfig().entities.spawning.despawnRangeShape; -+ final double dy = Math.abs(nearestPlayer.getY() - this.getY()); ++ final double dy = Math.abs(player.getY() - this.getY()); + final double dySqr = Mth.square(dy); -+ final double dxSqr = Mth.square(nearestPlayer.getX() - this.getX()); -+ final double dzSqr = Mth.square(nearestPlayer.getZ() - this.getZ()); ++ final double dxSqr = Mth.square(player.getX() - this.getX()); ++ final double dzSqr = Mth.square(player.getZ() - this.getZ()); + final double distanceSquared = dxSqr + dzSqr + dySqr; + // Despawn if hard/soft limit is exceeded + if (despawnRangePair.hard().shouldDespawn(shape, dxSqr, dySqr, dzSqr, dy) && this.removeWhenFarAway(distanceSquared)) { + this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause -+ } + } - int noDespawnDistance = this.getType().getCategory().getNoDespawnDistance(); -- int i1 = noDespawnDistance * noDespawnDistance; -- if (this.noActionTime > 600 && this.random.nextInt(800) == 0 && d > i1 && this.removeWhenFarAway(d)) { +- int noDespawnDistanceSqr = noDespawnDistance * noDespawnDistance; +- if (this.noActionTime > 600 && this.random.nextInt(800) == 0 && distSqr > noDespawnDistanceSqr && this.removeWhenFarAway(distSqr)) { - this.discard(); -- } else if (d < i1) { +- } else if (distSqr < noDespawnDistanceSqr) { + if (despawnRangePair.soft().shouldDespawn(shape, dxSqr, dySqr, dzSqr, dy)) { + if (this.noActionTime > 600 && this.random.nextInt(800) == 0 && this.removeWhenFarAway(distanceSquared)) { + this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause @@ -241,7 +233,7 @@ this.noActionTime = 0; } } -@@ -679,6 +_,15 @@ +@@ -681,6 +_,15 @@ @Override protected final void serverAiStep() { this.noActionTime++; @@ -254,11 +246,11 @@ + return; + } + // Paper end - Allow nerfed mobs to jump and float - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("sensing"); + ProfilerFiller profiler = Profiler.get(); + profiler.push("sensing"); this.sensing.tick(); -@@ -853,14 +_,69 @@ - public boolean stillValid(Player player) { +@@ -859,14 +_,69 @@ + public boolean stillValid(final Player player) { return player.getVehicle() == Mob.this || player.isWithinEntityInteractionRange(Mob.this, 4.0); } + @@ -319,115 +311,116 @@ + // Paper end + @Override - protected void dropCustomDeathLoot(ServerLevel level, DamageSource damageSource, boolean recentlyHit) { - super.dropCustomDeathLoot(level, damageSource, recentlyHit); + protected void dropCustomDeathLoot(final ServerLevel level, final DamageSource source, final boolean killedByPlayer) { + super.dropCustomDeathLoot(level, source, killedByPlayer); - for (EquipmentSlot equipmentSlot : EquipmentSlot.VALUES) { -+ if (this.shouldSkipLoot(equipmentSlot)) continue; // Paper - ItemStack itemBySlot = this.getItemBySlot(equipmentSlot); - float f = this.dropChances.byEquipment(equipmentSlot); - if (f != 0.0F) { -@@ -880,7 +_,13 @@ + for (EquipmentSlot slot : EquipmentSlot.VALUES) { ++ if (this.shouldSkipLoot(slot)) continue; // Paper + ItemStack itemStack = this.getItemBySlot(slot); + float dropChance = this.dropChances.byEquipment(slot); + if (dropChance != 0.0F) { +@@ -886,7 +_,13 @@ } - this.spawnAtLocation(level, itemBySlot); + this.spawnAtLocation(level, itemStack); + if (this.clearEquipmentSlots) { // Paper - this.setItemSlot(equipmentSlot, ItemStack.EMPTY); + this.setItemSlot(slot, ItemStack.EMPTY); + // Paper start + } else { -+ this.clearedEquipmentSlots.add(equipmentSlot); ++ this.clearedEquipmentSlots.add(slot); + } + // Paper end } } } -@@ -904,7 +_,9 @@ - set.add(equipmentSlot); - } else if (this.dropChances.isPreserved(equipmentSlot)) { - this.setItemSlot(equipmentSlot, ItemStack.EMPTY); +@@ -910,7 +_,9 @@ + slotsPreventedFromDropping.add(slot); + } else if (this.dropChances.isPreserved(slot)) { + this.setItemSlot(slot, ItemStack.EMPTY); + this.forceDrops = true; // Paper - Add missing forceDrop toggles - this.spawnAtLocation(level, itemBySlot); + this.spawnAtLocation(level, itemStack); + this.forceDrops = false; // Paper - Add missing forceDrop toggles } } } -@@ -1195,6 +_,21 @@ - public @Nullable T convertTo( - EntityType entityType, ConversionParams conversionParams, EntitySpawnReason spawnReason, ConversionParams.AfterConversion afterConversion +@@ -1210,6 +_,22 @@ + final EntitySpawnReason spawnReason, + final ConversionParams.AfterConversion afterConversion ) { + // Paper start - entity zap event - allow cancellation of conversion post creation -+ return this.convertTo(entityType, conversionParams, spawnReason, afterConversion, EntityTransformEvent.TransformReason.UNKNOWN, CreatureSpawnEvent.SpawnReason.DEFAULT); ++ return this.convertTo(entityType, params, spawnReason, afterConversion, EntityTransformEvent.TransformReason.UNKNOWN, CreatureSpawnEvent.SpawnReason.DEFAULT); + } + + @Nullable + public T convertTo( -+ EntityType entityType, ConversionParams conversionParams, EntitySpawnReason spawnReason, ConversionParams.AfterConversion afterConversion, EntityTransformEvent.@Nullable TransformReason transformReason, CreatureSpawnEvent.@Nullable SpawnReason creatureSpawnReason ++ EntityType entityType, ConversionParams params, EntitySpawnReason spawnReason, ConversionParams.AfterConversion afterConversion, EntityTransformEvent.@Nullable TransformReason transformReason, CreatureSpawnEvent.@Nullable SpawnReason creatureSpawnReason + ) { -+ return this.convertTo(entityType, conversionParams, spawnReason, e -> { afterConversion.finalizeConversion(e); return true; }, transformReason, creatureSpawnReason); ++ return this.convertTo(entityType, params, spawnReason, e -> { afterConversion.finalizeConversion(e); return true; }, transformReason, creatureSpawnReason); + } ++ + @Nullable + public T convertTo( -+ EntityType entityType, ConversionParams conversionParams, EntitySpawnReason spawnReason, ConversionParams.CancellingAfterConversion afterConversion, EntityTransformEvent.@Nullable TransformReason transformReason, CreatureSpawnEvent.@Nullable SpawnReason creatureSpawnReason ++ EntityType entityType, ConversionParams params, EntitySpawnReason spawnReason, ConversionParams.CancellingAfterConversion afterConversion, EntityTransformEvent.@Nullable TransformReason transformReason, CreatureSpawnEvent.@Nullable SpawnReason creatureSpawnReason + ) { + // Paper end - entity zap event - allow cancellation of conversion post creation if (this.isRemoved()) { return null; } else { -@@ -1203,13 +_,23 @@ +@@ -1218,13 +_,23 @@ return null; } else { - conversionParams.type().convert(this, mob, conversionParams); -- afterConversion.finalizeConversion(mob); -+ if (!afterConversion.finalizeConversionOrCancel(mob)) return null; // Paper - entity zap event - return null if conversion was cancelled + params.type().convert(this, newMob, params); +- afterConversion.finalizeConversion(newMob); ++ if (!afterConversion.finalizeConversionOrCancel(newMob)) return null; // Paper - entity zap event - return null if conversion was cancelled + // CraftBukkit start + if (transformReason == null) { + // Special handling for slime split and pig lightning -+ return mob; ++ return newMob; + } + -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTransformEvent(this, mob, transformReason).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTransformEvent(this, newMob, transformReason).isCancelled()) { + return null; + } + // CraftBukkit end if (this.level() instanceof ServerLevel serverLevel) { -- serverLevel.addFreshEntity(mob); -+ serverLevel.addFreshEntity(mob, creatureSpawnReason); // CraftBukkit +- serverLevel.addFreshEntity(newMob); ++ serverLevel.addFreshEntity(newMob, creatureSpawnReason); // CraftBukkit } - if (conversionParams.type().shouldDiscardAfterConversion()) { + if (params.type().shouldDiscardAfterConversion()) { - this.discard(); + this.discard(EntityRemoveEvent.Cause.TRANSFORMATION); // CraftBukkit - add Bukkit remove cause } - return mob; -@@ -1220,7 +_,17 @@ + return newMob; +@@ -1235,7 +_,17 @@ public @Nullable T convertTo( - EntityType entityType, ConversionParams conversionParams, ConversionParams.AfterConversion afterConversion + final EntityType entityType, final ConversionParams params, final ConversionParams.AfterConversion afterConversion ) { -- return this.convertTo(entityType, conversionParams, EntitySpawnReason.CONVERSION, afterConversion); +- return this.convertTo(entityType, params, EntitySpawnReason.CONVERSION, afterConversion); + // Paper start - entity zap event - allow cancellation of conversion post creation -+ return this.convertTo(entityType, conversionParams, afterConversion, EntityTransformEvent.TransformReason.UNKNOWN, CreatureSpawnEvent.SpawnReason.DEFAULT); ++ return this.convertTo(entityType, params, afterConversion, EntityTransformEvent.TransformReason.UNKNOWN, CreatureSpawnEvent.SpawnReason.DEFAULT); + } + -+ public @Nullable T convertTo(EntityType entityType, ConversionParams conversionParams, ConversionParams.AfterConversion afterConversion, EntityTransformEvent.@Nullable TransformReason transformReason, CreatureSpawnEvent.@Nullable SpawnReason creatureSpawnReason) { -+ return this.convertTo(entityType, conversionParams, e -> { afterConversion.finalizeConversion(e); return true; }, transformReason, creatureSpawnReason); ++ public @Nullable T convertTo(EntityType entityType, ConversionParams params, ConversionParams.AfterConversion afterConversion, EntityTransformEvent.@Nullable TransformReason transformReason, CreatureSpawnEvent.@Nullable SpawnReason creatureSpawnReason) { ++ return this.convertTo(entityType, params, e -> { afterConversion.finalizeConversion(e); return true; }, transformReason, creatureSpawnReason); + } + -+ public @Nullable T convertTo(EntityType entityType, ConversionParams conversionParams, ConversionParams.CancellingAfterConversion afterConversion, EntityTransformEvent.@Nullable TransformReason transformReason, CreatureSpawnEvent.@Nullable SpawnReason creatureSpawnReason) { -+ return this.convertTo(entityType, conversionParams, EntitySpawnReason.CONVERSION, afterConversion, transformReason, creatureSpawnReason); ++ public @Nullable T convertTo(EntityType entityType, ConversionParams params, ConversionParams.CancellingAfterConversion afterConversion, EntityTransformEvent.@Nullable TransformReason transformReason, CreatureSpawnEvent.@Nullable SpawnReason creatureSpawnReason) { ++ return this.convertTo(entityType, params, EntitySpawnReason.CONVERSION, afterConversion, transformReason, creatureSpawnReason); + // Paper end - entity zap event - allow cancellation of conversion post creation } @Override -@@ -1261,7 +_,17 @@ - public boolean startRiding(Entity entity, boolean force, boolean triggerEvents) { - boolean flag = super.startRiding(entity, force, triggerEvents); - if (flag && this.isLeashed()) { +@@ -1276,7 +_,17 @@ + public boolean startRiding(final Entity entity, final boolean force, final boolean sendEventAndTriggers) { + boolean result = super.startRiding(entity, force, sendEventAndTriggers); + if (result && this.isLeashed()) { - this.dropLeash(); + // Paper start - Expand EntityUnleashEvent + EntityUnleashEvent event = new EntityUnleashEvent(this.getBukkitEntity(), EntityUnleashEvent.UnleashReason.UNKNOWN, true); + if (!event.callEvent()) { -+ return flag; ++ return result; + } + if (event.isDropLeash()) { + this.dropLeash(); @@ -437,13 +430,13 @@ + // Paper end - Expand EntityUnleashEvent } - return flag; -@@ -1426,7 +_,7 @@ - List list = new ArrayList<>(availableGoals.size()); + return result; +@@ -1439,7 +_,7 @@ + Set availableGoals = this.goalSelector.getAvailableGoals(); + List goalInfo = new ArrayList<>(availableGoals.size()); availableGoals.forEach( - wrappedGoal -> list.add( -- new DebugGoalInfo.DebugGoal(wrappedGoal.getPriority(), wrappedGoal.isRunning(), wrappedGoal.getGoal().getClass().getSimpleName()) -+ new DebugGoalInfo.DebugGoal(wrappedGoal.getPriority(), wrappedGoal.isRunning(), wrappedGoal.getGoal() instanceof final com.destroystokyo.paper.entity.ai.PaperCustomGoal customGoal ? customGoal.getHandle().getClass().getSimpleName() + "*" : wrappedGoal.getGoal().getClass().getSimpleName()) // Paper - display right custom goal in debugging - ) +- goal -> goalInfo.add(new DebugGoalInfo.DebugGoal(goal.getPriority(), goal.isRunning(), goal.getGoal().getClass().getSimpleName())) ++ goal -> goalInfo.add(new DebugGoalInfo.DebugGoal(goal.getPriority(), goal.isRunning(), goal.getGoal() instanceof final com.destroystokyo.paper.entity.ai.PaperCustomGoal customGoal ? customGoal.getHandle().getClass().getSimpleName() + "*" : goal.getGoal().getClass().getSimpleName())) // Paper - display right custom goal in debugging ); - return new DebugGoalInfo(list); + return new DebugGoalInfo(goalInfo); + } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/NeutralMob.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/NeutralMob.java.patch index 1cdc0f94f540..d225c0e9b8a8 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/NeutralMob.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/NeutralMob.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/NeutralMob.java +++ b/net/minecraft/world/entity/NeutralMob.java -@@ -50,7 +_,11 @@ +@@ -51,7 +_,11 @@ if (level instanceof ServerLevel) { this.setPersistentAngerTarget(EntityReference.read(input, "angry_at")); @@ -13,26 +13,7 @@ } } -@@ -61,11 +_,16 @@ - this.stopBeingAngry(); - } else { - if (target != null) { -- if (persistentAngerTarget == null || !persistentAngerTarget.matches(target)) { -+ // Paper start - Backport fix for MC-305388 from 26.1-snapshot-5 -+ boolean newTarget = persistentAngerTarget == null || !persistentAngerTarget.matches(target); -+ if (newTarget) { - this.setPersistentAngerTarget(EntityReference.of(target)); - } - -- this.startPersistentAngerTimer(); -+ if (newTarget || updateAnger) { -+ this.startPersistentAngerTimer(); -+ } -+ // Paper end - Backport fix for MC-305388 from 26.1-snapshot-5 - } - - if (persistentAngerTarget != null && !this.isAngry() && (target == null || !isValidPlayerTarget(target) || !updateAnger)) { -@@ -120,7 +_,7 @@ +@@ -134,7 +_,7 @@ default void stopBeingAngry() { this.setLastHurtByMob(null); this.setPersistentAngerTarget(null); @@ -41,15 +22,17 @@ this.setPersistentAngerEndTime(-1L); } -@@ -130,7 +_,19 @@ +@@ -144,9 +_,21 @@ - void setTarget(@Nullable LivingEntity target); + void setTarget(final @Nullable LivingEntity target); + boolean setTarget(@Nullable LivingEntity target, org.bukkit.event.entity.EntityTargetEvent.@Nullable TargetReason reason); // CraftBukkit + - boolean canAttack(LivingEntity entity); + boolean canAttack(final LivingEntity target); @Nullable LivingEntity getTarget(); + + @Nullable LivingEntity getTargetUnchecked(); + + // Paper start - Prevent entity loading causing async lookups + // Update last hurt when ticking diff --git a/paper-server/patches/sources/net/minecraft/world/entity/OminousItemSpawner.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/OminousItemSpawner.java.patch index e3f3fdd9e410..5fc22c0ceaf4 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/OminousItemSpawner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/OminousItemSpawner.java.patch @@ -1,23 +1,23 @@ --- a/net/minecraft/world/entity/OminousItemSpawner.java +++ b/net/minecraft/world/entity/OminousItemSpawner.java @@ -78,7 +_,7 @@ - entity = this.spawnProjectile(serverLevel, projectileItem, item); + spawnedEntity = this.spawnProjectile(level, projectileItem, item); } else { - entity = new ItemEntity(serverLevel, this.getX(), this.getY(), this.getZ(), item); -- serverLevel.addFreshEntity(entity); -+ serverLevel.addFreshEntity(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.OMINOUS_ITEM_SPAWNER); // Paper - fixes and addition to spawn reason API + spawnedEntity = new ItemEntity(level, this.getX(), this.getY(), this.getZ(), item); +- level.addFreshEntity(spawnedEntity); ++ level.addFreshEntity(spawnedEntity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.OMINOUS_ITEM_SPAWNER); // Paper - fixes and addition to spawn reason API } - serverLevel.levelEvent(LevelEvent.PARTICLES_TRIAL_SPAWNER_SPAWN_ITEM, this.blockPosition(), 1); + level.levelEvent(LevelEvent.PARTICLES_TRIAL_SPAWNER_SPAWN_ITEM, this.blockPosition(), 1); @@ -92,7 +_,7 @@ ProjectileItem.DispenseConfig dispenseConfig = projectileItem.createDispenseConfig(); - dispenseConfig.overrideDispenseEvent().ifPresent(i -> level.levelEvent(i, this.blockPosition(), 0)); + dispenseConfig.overrideDispenseEvent().ifPresent(event -> level.levelEvent(event, this.blockPosition(), 0)); Direction direction = Direction.DOWN; - Projectile projectile = Projectile.spawnProjectileUsingShoot( + Projectile projectile = Projectile.spawnProjectileUsingShootDelayed( // Paper - fixes and addition to spawn reason API - projectileItem.asProjectile(level, this.position(), stack, direction), + projectileItem.asProjectile(level, this.position(), item, direction), level, - stack, + item, @@ -101,7 +_,7 @@ direction.getStepZ(), dispenseConfig.power(), diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Shearable.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Shearable.java.patch index 7ce39abff887..7b511ad56f67 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/Shearable.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/Shearable.java.patch @@ -4,15 +4,15 @@ import net.minecraft.world.item.ItemStack; public interface Shearable { -+ default void shear(ServerLevel level, SoundSource source, ItemStack shears, java.util.List drops) { this.shear(level, source, shears); } // Paper - Add drops to shear events - void shear(ServerLevel level, SoundSource source, ItemStack shears); ++ default void shear(ServerLevel level, SoundSource soundSource, ItemStack tool, java.util.List drops) { this.shear(level, soundSource, tool); } // Paper - Add drops to shear events + void shear(ServerLevel level, SoundSource soundSource, ItemStack tool); boolean readyForShearing(); + + net.minecraft.world.level.Level level(); // Shearable API - expose default level needed for shearing. + + // Paper start - custom shear drops; ensure all implementing entities override this -+ default java.util.List generateDefaultDrops(final ServerLevel level, final ItemStack shears) { ++ default java.util.List generateDefaultDrops(final ServerLevel level, final ItemStack tool) { + return java.util.Collections.emptyList(); + } + // Paper end - custom shear drops diff --git a/paper-server/patches/sources/net/minecraft/world/entity/TamableAnimal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/TamableAnimal.java.patch index cc8c0bbb2921..e298b1df0110 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/TamableAnimal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/TamableAnimal.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/TamableAnimal.java +++ b/net/minecraft/world/entity/TamableAnimal.java -@@ -75,7 +_,7 @@ +@@ -80,7 +_,7 @@ } this.orderedToSit = input.getBooleanOr("Sitting", false); @@ -9,21 +9,30 @@ } @Override -@@ -133,6 +_,13 @@ +@@ -136,7 +_,7 @@ + protected void feed(final Player player, final InteractionHand hand, final ItemStack itemStack, final float healingFactor, final float defaultHeal) { + FoodProperties foodProperties = itemStack.get(DataComponents.FOOD); + this.usePlayerItem(player, hand, itemStack); +- this.heal(foodProperties != null ? healingFactor * foodProperties.nutrition() : defaultHeal); ++ this.heal(foodProperties != null ? healingFactor * foodProperties.nutrition() : defaultHeal, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // Paper - Add missing regain reason + this.playEatingSound(); } - public void setInSittingPose(boolean sitting) { +@@ -145,6 +_,13 @@ + } + + public void setInSittingPose(final boolean value) { + // Paper start - Add EntityToggleSitEvent -+ this.setInSittingPose(sitting, true); ++ this.setInSittingPose(value, true); + } + -+ public void setInSittingPose(boolean sitting, boolean callEvent) { -+ if (callEvent && !new io.papermc.paper.event.entity.EntityToggleSitEvent(this.getBukkitEntity(), sitting).callEvent()) return; ++ public void setInSittingPose(boolean value, boolean callEvent) { ++ if (callEvent && !new io.papermc.paper.event.entity.EntityToggleSitEvent(this.getBukkitEntity(), value).callEvent()) return; + // Paper end - Add EntityToggleSitEvent - byte b = this.entityData.get(DATA_FLAGS_ID); - if (sitting) { - this.entityData.set(DATA_FLAGS_ID, (byte)(b | 1)); -@@ -213,7 +_,12 @@ + byte current = this.entityData.get(DATA_FLAGS_ID); + if (value) { + this.entityData.set(DATA_FLAGS_ID, (byte)(current | 1)); +@@ -225,7 +_,12 @@ if (this.level() instanceof ServerLevel serverLevel && serverLevel.getGameRules().get(GameRules.SHOW_DEATH_MESSAGES) && this.getOwner() instanceof ServerPlayer serverPlayer) { @@ -36,8 +45,8 @@ + // Paper end - Add TameableDeathMessageEvent } - super.die(damageSource); -@@ -256,7 +_,14 @@ + super.die(source); +@@ -268,7 +_,14 @@ if (!this.canTeleportTo(new BlockPos(x, y, z))) { return false; } else { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/attributes/AttributeInstance.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/attributes/AttributeInstance.java.patch index 38c76aeede01..36af4acb6414 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/attributes/AttributeInstance.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/attributes/AttributeInstance.java.patch @@ -1,27 +1,27 @@ --- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java @@ -148,20 +_,20 @@ - double baseValue = this.getBaseValue(); + double base = this.getBaseValue(); - for (AttributeModifier attributeModifier : this.getModifiersOrEmpty(AttributeModifier.Operation.ADD_VALUE)) { -- baseValue += attributeModifier.amount(); -+ baseValue += attributeModifier.amount(); // Paper - destroy speed API - diff on change + for (AttributeModifier modifier : this.getModifiersOrEmpty(AttributeModifier.Operation.ADD_VALUE)) { +- base += modifier.amount(); ++ base += modifier.amount(); // Paper - destroy speed API - diff on change } - double d = baseValue; + double result = base; - for (AttributeModifier attributeModifier1 : this.getModifiersOrEmpty(AttributeModifier.Operation.ADD_MULTIPLIED_BASE)) { -- d += baseValue * attributeModifier1.amount(); -+ d += baseValue * attributeModifier1.amount(); // Paper - destroy speed API - diff on change + for (AttributeModifier modifier : this.getModifiersOrEmpty(AttributeModifier.Operation.ADD_MULTIPLIED_BASE)) { +- result += base * modifier.amount(); ++ result += base * modifier.amount(); // Paper - destroy speed API - diff on change } - for (AttributeModifier attributeModifier1 : this.getModifiersOrEmpty(AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL)) { -- d *= 1.0 + attributeModifier1.amount(); -+ d *= 1.0 + attributeModifier1.amount(); // Paper - destroy speed API - diff on change + for (AttributeModifier modifier : this.getModifiersOrEmpty(AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL)) { +- result *= 1.0 + modifier.amount(); ++ result *= 1.0 + modifier.amount(); // Paper - destroy speed API - diff on change } -- return this.attribute.value().sanitizeValue(d); -+ return this.attribute.value().sanitizeValue(d); // Paper - destroy speed API - diff on change +- return this.attribute.value().sanitizeValue(result); ++ return this.attribute.value().sanitizeValue(result); // Paper - destroy speed API - diff on change } - private Collection getModifiersOrEmpty(AttributeModifier.Operation operation) { + private Collection getModifiersOrEmpty(final AttributeModifier.Operation operation) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/AcquirePoi.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/AcquirePoi.java.patch index 2e67b4cf3bf1..e4e2c1f5d828 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/AcquirePoi.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/AcquirePoi.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +++ b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -@@ -69,6 +_,7 @@ - return false; - } else { - mutableLong.setValue(time + 20L + level.getRandom().nextInt(20)); -+ if (level.paperConfig().entities.behavior.stuckEntityPoiRetryDelay.enabled() && mob.getNavigation().isStuck()) mutableLong.add(level.paperConfig().entities.behavior.stuckEntityPoiRetryDelay.intValue()); // Paper - Next stuck check delay config - PoiManager poiManager = level.getPoiManager(); - map.long2ObjectEntrySet().removeIf(entry -> !entry.getValue().isStillValid(time)); - Predicate predicate1 = pos -> { +@@ -74,6 +_,7 @@ + return false; + } else { + nextScheduledStart.setValue(timestamp + 20L + random.nextInt(20)); ++ if (level.paperConfig().entities.behavior.stuckEntityPoiRetryDelay.enabled() && body.getNavigation().isStuck()) nextScheduledStart.add(level.paperConfig().entities.behavior.stuckEntityPoiRetryDelay.intValue()); // Paper - Next stuck check delay config + PoiManager poiManager = level.getPoiManager(); + batchCache.long2ObjectEntrySet().removeIf(entry -> !entry.getValue().isStillValid(timestamp)); + Predicate cacheTest = pos -> { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/AssignProfessionFromJobSite.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/AssignProfessionFromJobSite.java.patch index cfc60b55d7ee..d6f0def83860 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/AssignProfessionFromJobSite.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/AssignProfessionFromJobSite.java.patch @@ -3,16 +3,16 @@ @@ -39,7 +_,14 @@ .findFirst() ) - .ifPresent(reference -> { -- villager.setVillagerData(villager.getVillagerData().withProfession(reference)); + .ifPresent(profession -> { +- body.setVillagerData(body.getVillagerData().withProfession(profession)); + // CraftBukkit start - Fire VillagerCareerChangeEvent where Villager gets employed -+ org.bukkit.event.entity.VillagerCareerChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callVillagerCareerChangeEvent(villager, org.bukkit.craftbukkit.entity.CraftVillager.CraftProfession.minecraftHolderToBukkit(reference), org.bukkit.event.entity.VillagerCareerChangeEvent.ChangeReason.EMPLOYED); ++ org.bukkit.event.entity.VillagerCareerChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callVillagerCareerChangeEvent(body, org.bukkit.craftbukkit.entity.CraftVillager.CraftProfession.minecraftHolderToBukkit(profession), org.bukkit.event.entity.VillagerCareerChangeEvent.ChangeReason.EMPLOYED); + if (event.isCancelled()) { + return; + } + -+ villager.setVillagerData(villager.getVillagerData().withProfession(org.bukkit.craftbukkit.entity.CraftVillager.CraftProfession.bukkitToMinecraftHolder(event.getProfession()))); ++ body.setVillagerData(body.getVillagerData().withProfession(org.bukkit.craftbukkit.entity.CraftVillager.CraftProfession.bukkitToMinecraftHolder(event.getProfession()))); + // CraftBukkit end - villager.refreshBrain(level); + body.refreshBrain(level); }); return true; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/BabyFollowAdult.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/BabyFollowAdult.java.patch index 1ba0031bec4f..76dd72d72617 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/BabyFollowAdult.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/BabyFollowAdult.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/world/entity/ai/behavior/BabyFollowAdult.java +++ b/net/minecraft/world/entity/ai/behavior/BabyFollowAdult.java -@@ -30,6 +_,17 @@ +@@ -28,6 +_,17 @@ } else { - LivingEntity livingEntity = instance.get(memoryAccessor); - if (entity.closerThan(livingEntity, followRange.getMaxValue() + 1) && !entity.closerThan(livingEntity, followRange.getMinValue())) { + LivingEntity adult = i.get(nearestAdult); + if (body.closerThan(adult, followRange.maxInclusive() + 1) && !body.closerThan(adult, followRange.minInclusive())) { + // CraftBukkit start -+ org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(entity, livingEntity, org.bukkit.event.entity.EntityTargetEvent.TargetReason.FOLLOW_LEADER); ++ org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(body, adult, org.bukkit.event.entity.EntityTargetEvent.TargetReason.FOLLOW_LEADER); + if (event.isCancelled()) { + return false; + } + if (event.getTarget() == null) { -+ memoryAccessor.erase(); ++ walkTarget.erase(); + return true; + } -+ livingEntity = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle(); ++ adult = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle(); + // CraftBukkit end - WalkTarget walkTarget = new WalkTarget( - new EntityTracker(livingEntity, targetEyeHeight, targetEyeHeight), - speedModifier.apply(entity), + WalkTarget target = new WalkTarget( + new EntityTracker(adult, targetEye, targetEye), speedModifier.apply(body), followRange.minInclusive() - 1 + ); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/Behavior.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/Behavior.java.patch index 5088503a0803..7133db894cec 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/Behavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/Behavior.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/entity/ai/behavior/Behavior.java +++ b/net/minecraft/world/entity/ai/behavior/Behavior.java -@@ -14,6 +_,7 @@ +@@ -15,6 +_,7 @@ private long endTimestamp; private final int minDuration; private final int maxDuration; + private final String configKey; // Paper - configurable behavior tick rate and timings - public Behavior(Map, MemoryStatus> entryCondition) { - this(entryCondition, 60); -@@ -27,6 +_,14 @@ + public Behavior(final Map, MemoryStatus> entryCondition) { + this(entryCondition, DEFAULT_DURATION); +@@ -28,6 +_,14 @@ this.minDuration = minDuration; this.maxDuration = maxDuration; this.entryCondition = entryCondition; + // Paper start - configurable behavior tick rate and timings -+ String key = io.papermc.paper.util.MappingEnvironment.reobf() ? io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName()) : this.getClass().getName(); ++ String key = this.getClass().getName(); + int lastSeparator = key.lastIndexOf('.'); + if (lastSeparator != -1) { + key = key.substring(lastSeparator + 1); @@ -23,16 +23,16 @@ } @Override -@@ -36,6 +_,12 @@ +@@ -42,6 +_,12 @@ @Override - public final boolean tryStart(ServerLevel level, E owner, long gameTime) { + public final boolean tryStart(final ServerLevel level, final E body, final long timestamp) { + // Paper start - configurable behavior tick rate and timings -+ int tickRate = java.util.Objects.requireNonNullElse(level.paperConfig().tickRates.behavior.get(owner.getType(), this.configKey), -1); -+ if (tickRate > -1 && gameTime < this.endTimestamp + tickRate) { ++ int tickRate = java.util.Objects.requireNonNullElse(level.paperConfig().tickRates.behavior.get(body.getType(), this.configKey), -1); ++ if (tickRate > -1 && timestamp < this.endTimestamp + tickRate) { + return false; + } + // Paper end - configurable behavior tick rate and timings - if (this.hasRequiredMemories(owner) && this.checkExtraStartConditions(level, owner)) { + if (this.hasRequiredMemories(body) && this.checkExtraStartConditions(level, body)) { this.status = Behavior.Status.RUNNING; - int i = this.minDuration + level.getRandom().nextInt(this.maxDuration + 1 - this.minDuration); + int duration = this.minDuration + level.getRandom().nextInt(this.maxDuration + 1 - this.minDuration); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java.patch index 77f8530cc4ed..d25530175304 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java.patch @@ -1,24 +1,24 @@ --- a/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java +++ b/net/minecraft/world/entity/ai/behavior/BehaviorUtils.java -@@ -80,6 +_,7 @@ - } - - public static void throwItem(LivingEntity entity, ItemStack stack, Vec3 offset, Vec3 speedMultiplier, float yOffset) { -+ if (stack.isEmpty()) return; // CraftBukkit - SPIGOT-4940: no empty loot - double d = entity.getEyeY() - yOffset; - ItemEntity itemEntity = new ItemEntity(entity.level(), entity.getX(), d, entity.getZ(), stack); - itemEntity.setThrower(entity); -@@ -87,6 +_,13 @@ - vec3 = vec3.normalize().multiply(speedMultiplier.x, speedMultiplier.y, speedMultiplier.z); - itemEntity.setDeltaMovement(vec3); +@@ -94,6 +_,7 @@ + public static void throwItem( + final LivingEntity thrower, final ItemStack item, final Vec3 targetPos, final Vec3 throwVelocity, final float handYDistanceFromEye + ) { ++ if (item.isEmpty()) return; // CraftBukkit - SPIGOT-4940: no empty loot + double yHandPos = thrower.getEyeY() - handYDistanceFromEye; + ItemEntity itemEntity = new ItemEntity(thrower.level(), thrower.getX(), yHandPos, thrower.getZ(), item); + itemEntity.setThrower(thrower); +@@ -101,6 +_,13 @@ + throwVector = throwVector.normalize().multiply(throwVelocity.x, throwVelocity.y, throwVelocity.z); + itemEntity.setDeltaMovement(throwVector); itemEntity.setDefaultPickUpDelay(); + // CraftBukkit start -+ org.bukkit.event.entity.EntityDropItemEvent event = new org.bukkit.event.entity.EntityDropItemEvent(entity.getBukkitEntity(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity()); ++ org.bukkit.event.entity.EntityDropItemEvent event = new org.bukkit.event.entity.EntityDropItemEvent(thrower.getBukkitEntity(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity()); + itemEntity.level().getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + // CraftBukkit end - entity.level().addFreshEntity(itemEntity); + thrower.level().addFreshEntity(itemEntity); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/GateBehavior.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/GateBehavior.java.patch index d7a1d368e053..736ebb2919f8 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/GateBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/GateBehavior.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/ai/behavior/GateBehavior.java +++ b/net/minecraft/world/entity/ai/behavior/GateBehavior.java -@@ -18,7 +_,7 @@ +@@ -19,7 +_,7 @@ private final Set> exitErasedMemories; private final GateBehavior.OrderPolicy orderPolicy; private final GateBehavior.RunningPolicy runningPolicy; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/GoToWantedItem.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/GoToWantedItem.java.patch index b4c97cad9b5f..f70e5c7e3a59 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/GoToWantedItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/GoToWantedItem.java.patch @@ -1,24 +1,24 @@ --- a/net/minecraft/world/entity/ai/behavior/GoToWantedItem.java +++ b/net/minecraft/world/entity/ai/behavior/GoToWantedItem.java -@@ -35,6 +_,21 @@ - && itemEntity.closerThan(entity, maxDistToWalk) - && entity.level().getWorldBorder().isWithinBounds(itemEntity.blockPosition()) - && entity.canPickUpLoot()) { +@@ -37,6 +_,21 @@ + && item.closerThan(body, maxDistToWalk) + && body.level().getWorldBorder().isWithinBounds(item.blockPosition()) + && body.canPickUpLoot()) { + // CraftBukkit start -+ if (entity instanceof net.minecraft.world.entity.animal.allay.Allay) { -+ org.bukkit.event.entity.EntityTargetEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetEvent(entity, itemEntity, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY); ++ if (body instanceof net.minecraft.world.entity.animal.allay.Allay) { ++ org.bukkit.event.entity.EntityTargetEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetEvent(body, item, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY); + + if (event.isCancelled()) { + return false; + } + if (!(event.getTarget() instanceof org.bukkit.craftbukkit.entity.CraftItem targetItem)) { // Paper - only erase allay memory on non-item targets -+ nearestVisibleWantedItem.erase(); ++ wantedItem.erase(); + return false; // Paper - only erase allay memory on non-item targets + } + -+ itemEntity = targetItem.getHandle(); ++ item = targetItem.getHandle(); + } + // CraftBukkit end - WalkTarget walkTarget1 = new WalkTarget(new EntityTracker(itemEntity, false), speedModifier, 0); - lookTarget.set(new EntityTracker(itemEntity, true)); - walkTarget.set(walkTarget1); + WalkTarget target = new WalkTarget(new EntityTracker(item, false), speedModifier, 0); + lookTarget.set(new EntityTracker(item, true)); + walkTarget.set(target); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch index 4f07a53c89a7..8d8868ad1659 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch @@ -2,23 +2,23 @@ +++ b/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java @@ -108,7 +_,9 @@ Block block = blockState.getBlock(); - Block block1 = level.getBlockState(this.aboveFarmlandPos.below()).getBlock(); + Block blockBelow = level.getBlockState(this.aboveFarmlandPos.below()).getBlock(); if (block instanceof CropBlock && ((CropBlock)block).isMaxAge(blockState)) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(owner, this.aboveFarmlandPos, blockState.getFluidState().createLegacyBlock())) { // CraftBukkit // Paper - fix wrong block state - level.destroyBlock(this.aboveFarmlandPos, true, owner); ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(body, this.aboveFarmlandPos, blockState.getFluidState().createLegacyBlock())) { // CraftBukkit // Paper - fix wrong block state + level.destroyBlock(this.aboveFarmlandPos, true, body); + } // CraftBukkit } - if (blockState.isAir() && block1 instanceof FarmBlock && owner.hasFarmSeeds()) { + if (blockState.isAir() && blockBelow instanceof FarmlandBlock && body.hasFarmSeeds()) { @@ -119,9 +_,11 @@ - boolean flag = false; - if (!item.isEmpty() && item.is(ItemTags.VILLAGER_PLANTABLE_SEEDS) && item.getItem() instanceof BlockItem blockItem) { - BlockState blockState1 = blockItem.getBlock().defaultBlockState(); -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(owner, this.aboveFarmlandPos, blockState1)) { // CraftBukkit - level.setBlockAndUpdate(this.aboveFarmlandPos, blockState1); - level.gameEvent(GameEvent.BLOCK_PLACE, this.aboveFarmlandPos, GameEvent.Context.of(owner, blockState1)); - flag = true; + boolean ok = false; + if (!itemStack.isEmpty() && itemStack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS) && itemStack.getItem() instanceof BlockItem blockItem) { + BlockState place = blockItem.getBlock().defaultBlockState(); ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(body, this.aboveFarmlandPos, place)) { // CraftBukkit + level.setBlockAndUpdate(this.aboveFarmlandPos, place); + level.gameEvent(GameEvent.BLOCK_PLACE, this.aboveFarmlandPos, GameEvent.Context.of(body, place)); + ok = true; + } // CraftBukkit } - if (flag) { + if (ok) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java.patch index 05862afb2985..fa4f0576a159 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java.patch @@ -1,28 +1,28 @@ --- a/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java +++ b/net/minecraft/world/entity/ai/behavior/InteractWithDoor.java -@@ -58,6 +_,12 @@ - if (blockState.is(BlockTags.MOB_INTERACTABLE_DOORS, state -> state.getBlock() instanceof DoorBlock)) { - DoorBlock doorBlock = (DoorBlock)blockState.getBlock(); - if (!doorBlock.isOpen(blockState)) { +@@ -56,6 +_,12 @@ + if (fromState.is(BlockTags.MOB_INTERACTABLE_DOORS, s -> s.getBlock() instanceof DoorBlock)) { + DoorBlock fromBlock = (DoorBlock)fromState.getBlock(); + if (!fromBlock.isOpen(fromState)) { + // CraftBukkit start - entities opening doors -+ org.bukkit.event.entity.EntityInteractEvent event = new org.bukkit.event.entity.EntityInteractEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(entity.level(), blockPos)); ++ org.bukkit.event.entity.EntityInteractEvent event = new org.bukkit.event.entity.EntityInteractEvent(body.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(body.level(), fromPos)); + if (!event.callEvent()) { + return false; + } + // CraftBukkit end - entities opening doors - doorBlock.setOpen(entity, level, blockState, blockPos, true); + fromBlock.setOpen(body, level, fromState, fromPos, true); } -@@ -69,6 +_,12 @@ - if (blockState1.is(BlockTags.MOB_INTERACTABLE_DOORS, state -> state.getBlock() instanceof DoorBlock)) { - DoorBlock doorBlock1 = (DoorBlock)blockState1.getBlock(); - if (!doorBlock1.isOpen(blockState1)) { +@@ -67,6 +_,12 @@ + if (toState.is(BlockTags.MOB_INTERACTABLE_DOORS, s -> s.getBlock() instanceof DoorBlock)) { + DoorBlock door = (DoorBlock)toState.getBlock(); + if (!door.isOpen(toState)) { + // CraftBukkit start - entities opening doors -+ org.bukkit.event.entity.EntityInteractEvent event = new org.bukkit.event.entity.EntityInteractEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(entity.level(), blockPos1)); ++ org.bukkit.event.entity.EntityInteractEvent event = new org.bukkit.event.entity.EntityInteractEvent(body.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(body.level(), toPos)); + if (!event.callEvent()) { + return false; + } + // CraftBukkit end - entities opening doors - doorBlock1.setOpen(entity, level, blockState1, blockPos1, true); - optional = rememberDoorToClose(doorsToClose, optional, level, blockPos1); + door.setOpen(body, level, toState, toPos, true); + doors = rememberDoorToClose(doorsMemory, doors, level, toPos); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/PoiCompetitorScan.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/PoiCompetitorScan.java.patch index 2158504f91e1..caaee018aa0c 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/PoiCompetitorScan.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/PoiCompetitorScan.java.patch @@ -2,35 +2,35 @@ +++ b/net/minecraft/world/entity/ai/behavior/PoiCompetitorScan.java @@ -22,13 +_,32 @@ level.getPoiManager() - .getType(globalPos.pos()) + .getType(pos.pos()) .ifPresent( -- poi -> instance.>get(nearestLivingEntities) +- poiType -> i.>get(nearestEntities) - .stream() -- .filter(entity -> entity instanceof Villager && entity != villager) -- .map(entity -> (Villager)entity) +- .filter(v -> v instanceof Villager && v != body) +- .map(v -> (Villager)v) - .filter(LivingEntity::isAlive) -- .filter(v -> competesForSameJobsite(globalPos, poi, v)) -- .reduce(villager, PoiCompetitorScan::selectWinner) +- .filter(nearbyVillager -> competesForSameJobsite(pos, poiType, nearbyVillager)) +- .reduce(body, PoiCompetitorScan::selectWinner) + // Paper start - Improve performance of PoiCompetitorScan by unrolling stream + // The previous logic used Stream#reduce to simulate a form of single-iteration bubble sort + // in which the "winning" villager would maintain MemoryModuleType.JOB_SITE while all others + // would lose said memory module type by passing each "current winner" and incoming next + // villager to #selectWinner. -+ poi -> { -+ final List livingEntities = instance.get(nearestLivingEntities); ++ poiType -> { ++ final List livingEntities = i.get(nearestEntities); + -+ Villager winner = villager; ++ Villager winner = body; + for (final LivingEntity other : livingEntities) { -+ if (other == villager) { ++ if (other == body) { + continue; + } -+ if (!(other instanceof final net.minecraft.world.entity.npc.villager.Villager otherVillager)) { ++ if (!(other instanceof final Villager otherVillager)) { + continue; + } + if (!other.isAlive()) { + continue; + } -+ if (!competesForSameJobsite(globalPos, poi, otherVillager)) { ++ if (!competesForSameJobsite(pos, poiType, otherVillager)) { + continue; + } + winner = selectWinner(winner, otherVillager); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/PrepareRamNearestTarget.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/PrepareRamNearestTarget.java.patch index 7a3f753af534..ec955530a856 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/PrepareRamNearestTarget.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/PrepareRamNearestTarget.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/entity/ai/behavior/PrepareRamNearestTarget.java +++ b/net/minecraft/world/entity/ai/behavior/PrepareRamNearestTarget.java -@@ -76,6 +_,16 @@ - .flatMap( - nearestVisibleLivingEntities -> nearestVisibleLivingEntities.findClosest(livingEntity -> this.ramTargeting.test(level, entity, livingEntity)) - ) +@@ -74,6 +_,16 @@ + Brain brain = body.getBrain(); + brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES) + .flatMap(livingEntities -> livingEntities.findClosest(entity -> this.ramTargeting.test(level, body, entity))) + // CraftBukkit start + .map((livingEntity) -> { -+ org.bukkit.event.entity.EntityTargetEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(entity, livingEntity, (livingEntity instanceof net.minecraft.server.level.ServerPlayer) ? org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER : org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY); ++ org.bukkit.event.entity.EntityTargetEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(body, livingEntity, (livingEntity instanceof net.minecraft.server.level.ServerPlayer) ? org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER : org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY); + if (event.isCancelled() || event.getTarget() == null) { + return null; + } @@ -14,6 +14,6 @@ + return livingEntity; + }) + // CraftBukkit end - .ifPresent(entity1 -> this.chooseRamPosition(entity, entity1)); + .ifPresent(livingEntity -> this.chooseRamPosition(body, livingEntity)); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/RamTarget.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/RamTarget.java.patch index f1551b7ce4ca..2cc236f61e81 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/RamTarget.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/RamTarget.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/ai/behavior/RamTarget.java +++ b/net/minecraft/world/entity/ai/behavior/RamTarget.java -@@ -93,7 +_,7 @@ - DamageSource damageSource1 = level.damageSources().mobAttack(owner); - float f3 = livingEntity.applyItemBlocking(level, damageSource1, f); - float f4 = f3 > 0.0F ? 0.5F : 1.0F; -- livingEntity.knockback(f4 * f2 * this.getKnockbackForce.applyAsDouble(owner), this.ramDirection.x(), this.ramDirection.z()); -+ livingEntity.knockback(f4 * f2 * this.getKnockbackForce.applyAsDouble(owner), this.ramDirection.x(), this.ramDirection.z(), owner, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent - this.finishRam(level, owner); - level.playSound(null, owner, this.getImpactSound.apply(owner), SoundSource.NEUTRAL, 1.0F, 1.0F); - } else if (this.hasRammedHornBreakingBlock(level, owner)) { +@@ -96,7 +_,7 @@ + DamageSource source = level.damageSources().mobAttack(body); + float blockedDamage = ramTarget.applyItemBlocking(level, source, damage); + float blockingFactor = blockedDamage > 0.0F ? 0.5F : 1.0F; +- ramTarget.knockback(blockingFactor * speedFactor * this.getKnockbackForce.applyAsDouble(body), this.ramDirection.x(), this.ramDirection.z()); ++ ramTarget.knockback(blockingFactor * speedFactor * this.getKnockbackForce.applyAsDouble(body), this.ramDirection.x(), this.ramDirection.z(), body, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent + this.finishRam(level, body); + level.playSound(null, body, this.getImpactSound.apply(body), SoundSource.NEUTRAL, 1.0F, 1.0F); + } else if (this.hasRammedHornBreakingBlock(level, body)) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/ResetProfession.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/ResetProfession.java.patch index 00704ac6966f..0bc02b959fdf 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/ResetProfession.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/ResetProfession.java.patch @@ -1,18 +1,18 @@ --- a/net/minecraft/world/entity/ai/behavior/ResetProfession.java +++ b/net/minecraft/world/entity/ai/behavior/ResetProfession.java -@@ -13,7 +_,14 @@ - VillagerData villagerData = villager.getVillagerData(); - boolean flag = !villagerData.profession().is(VillagerProfession.NONE) && !villagerData.profession().is(VillagerProfession.NITWIT); - if (flag && villager.getVillagerXp() == 0 && villagerData.level() <= 1) { -- villager.setVillagerData(villager.getVillagerData().withProfession(level.registryAccess(), VillagerProfession.NONE)); -+ // CraftBukkit start -+ org.bukkit.event.entity.VillagerCareerChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callVillagerCareerChangeEvent(villager, org.bukkit.craftbukkit.entity.CraftVillager.CraftProfession.minecraftHolderToBukkit(level.registryAccess().getOrThrow(VillagerProfession.NONE)), org.bukkit.event.entity.VillagerCareerChangeEvent.ChangeReason.LOSING_JOB); -+ if (event.isCancelled()) { -+ return false; -+ } +@@ -12,7 +_,14 @@ + VillagerData bodyData = body.getVillagerData(); + boolean canBeFired = !bodyData.profession().is(VillagerProfession.NONE) && !bodyData.profession().is(VillagerProfession.NITWIT); + if (canBeFired && body.getVillagerXp() == 0 && bodyData.level() <= 1) { +- body.setVillagerData(body.getVillagerData().withProfession(level.registryAccess(), VillagerProfession.NONE)); ++ // CraftBukkit start ++ org.bukkit.event.entity.VillagerCareerChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callVillagerCareerChangeEvent(body, org.bukkit.craftbukkit.entity.CraftVillager.CraftProfession.minecraftHolderToBukkit(level.registryAccess().getOrThrow(VillagerProfession.NONE)), org.bukkit.event.entity.VillagerCareerChangeEvent.ChangeReason.LOSING_JOB); ++ if (event.isCancelled()) { ++ return false; ++ } + -+ villager.setVillagerData(villager.getVillagerData().withProfession(org.bukkit.craftbukkit.entity.CraftVillager.CraftProfession.bukkitToMinecraftHolder(event.getProfession()))); -+ // CraftBukkit end - villager.refreshBrain(level); - return true; - } else { ++ body.setVillagerData(body.getVillagerData().withProfession(org.bukkit.craftbukkit.entity.CraftVillager.CraftProfession.bukkitToMinecraftHolder(event.getProfession()))); ++ // CraftBukkit end + body.refreshBrain(level); + return true; + } else { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/ShufflingList.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/ShufflingList.java.patch index ddad0d36980c..fccff1ee82d3 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/ShufflingList.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/ShufflingList.java.patch @@ -16,7 +16,7 @@ this.entries = Lists.newArrayList(); } - private ShufflingList(List> entries) { + private ShufflingList(final List> entries) { + // Paper start - Fix Concurrency issue in ShufflingList during worldgen + this(entries, true); + } @@ -30,12 +30,12 @@ } public ShufflingList shuffle() { -- this.entries.forEach(entry -> entry.setRandom(this.random.nextFloat())); +- this.entries.forEach(k -> k.setRandom(this.random.nextFloat())); - this.entries.sort(Comparator.comparingDouble(ShufflingList.WeightedEntry::getRandWeight)); - return this; + // Paper start - Fix Concurrency issue in ShufflingList during worldgen + List> list = this.isUnsafe ? Lists.newArrayList(this.entries) : this.entries; -+ list.forEach(entry -> entry.setRandom(this.random.nextFloat())); ++ list.forEach(k -> k.setRandom(this.random.nextFloat())); + list.sort(Comparator.comparingDouble(ShufflingList.WeightedEntry::getRandWeight)); + return this.isUnsafe ? new ShufflingList<>(list, this.isUnsafe) : this; + // Paper end - Fix Concurrency issue in ShufflingList during worldgen diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/SleepInBed.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/SleepInBed.java.patch index cfe4ec05792b..c293d55736e8 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/SleepInBed.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/SleepInBed.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/entity/ai/behavior/SleepInBed.java +++ b/net/minecraft/world/entity/ai/behavior/SleepInBed.java -@@ -42,7 +_,8 @@ +@@ -55,7 +_,8 @@ } } -- BlockState blockState = level.getBlockState(globalPos.pos()); -+ BlockState blockState = level.getBlockStateIfLoaded(globalPos.pos()); // Paper - Prevent sync chunk loads when villagers try to find beds +- BlockState blockState = level.getBlockState(target.pos()); ++ BlockState blockState = level.getBlockStateIfLoaded(target.pos()); // Paper - Prevent sync chunk loads when villagers try to find beds + if (blockState == null) { return false; } // Paper - Prevent sync chunk loads when villagers try to find beds - return globalPos.pos().closerToCenterThan(owner.position(), 2.0) && blockState.is(BlockTags.BEDS) && !blockState.getValue(BedBlock.OCCUPIED); + return target.pos().closerToCenterThan(body.position(), 2.0) && blockState.is(BlockTags.BEDS) && !blockState.getValue(BedBlock.OCCUPIED); } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/StartAttacking.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/StartAttacking.java.patch index 4915d1fd6b44..8dcd2a659c5e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/StartAttacking.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/StartAttacking.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/world/entity/ai/behavior/StartAttacking.java +++ b/net/minecraft/world/entity/ai/behavior/StartAttacking.java -@@ -27,6 +_,17 @@ - if (!entity.canAttack(livingEntity)) { +@@ -29,6 +_,17 @@ + if (!body.canAttack(targetEntity)) { return false; } else { + // CraftBukkit start -+ org.bukkit.event.entity.EntityTargetEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(entity, livingEntity, (livingEntity instanceof net.minecraft.server.level.ServerPlayer) ? org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER : org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY); ++ org.bukkit.event.entity.EntityTargetEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(body, targetEntity, (targetEntity instanceof net.minecraft.server.level.ServerPlayer) ? org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER : org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY); + if (event.isCancelled()) { + return false; + } + if (event.getTarget() == null) { -+ memoryAccessor.erase(); ++ attackTarget.erase(); + return true; + } -+ livingEntity = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle(); ++ targetEntity = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle(); + // CraftBukkit end - memoryAccessor.set(livingEntity); - memoryAccessor1.erase(); + attackTarget.set(targetEntity); + cantReachSince.erase(); return true; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/StopAttackingIfTargetInvalid.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/StopAttackingIfTargetInvalid.java.patch index 24a503eca08c..92fca8fa6ab9 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/StopAttackingIfTargetInvalid.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/StopAttackingIfTargetInvalid.java.patch @@ -1,33 +1,33 @@ --- a/net/minecraft/world/entity/ai/behavior/StopAttackingIfTargetInvalid.java +++ b/net/minecraft/world/entity/ai/behavior/StopAttackingIfTargetInvalid.java @@ -40,6 +_,30 @@ - && !canStopAttacking.test(level, livingEntity)) { + && !stopAttackingWhen.test(level, target)) { return true; } else { + // Paper start - better track target change reason + final org.bukkit.event.entity.EntityTargetEvent.TargetReason reason; -+ if (!entity.canAttack(livingEntity)) { ++ if (!body.canAttack(target)) { + reason = org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_INVALID; -+ } else if (canGrowTiredOfTryingToReachTarget && StopAttackingIfTargetInvalid.isTiredOfTryingToReachTarget(entity, instance.tryGet(memoryAccessor1))) { ++ } else if (canGrowTiredOfTryingToReachTarget && StopAttackingIfTargetInvalid.isTiredOfTryingToReachTarget(body, i.tryGet(cantReachSince))) { + reason = org.bukkit.event.entity.EntityTargetEvent.TargetReason.FORGOT_TARGET; -+ } else if (!livingEntity.isAlive()) { ++ } else if (!target.isAlive()) { + reason = org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_DIED; -+ } else if (livingEntity.level() != entity.level()) { ++ } else if (target.level() != body.level()) { + reason = org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_OTHER_LEVEL; + } else { + reason = org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_INVALID; + } + // Paper end + // CraftBukkit start -+ org.bukkit.event.entity.EntityTargetEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(entity, null, reason); // Paper ++ org.bukkit.event.entity.EntityTargetEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(body, null, reason); // Paper + if (event.isCancelled()) { + return false; + } + if (event.getTarget() != null) { -+ entity.getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle()); ++ body.getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle()); + return true; + } + // CraftBukkit end - onStopAttacking.accept(level, entity, livingEntity); - memoryAccessor.erase(); + onTargetErased.accept(level, body, target); + attackTarget.erase(); return true; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch index 6d62fb17905f..d5c3556c7c9a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java +++ b/net/minecraft/world/entity/ai/behavior/TransportItemsBetweenContainers.java -@@ -320,7 +_,10 @@ +@@ -327,7 +_,10 @@ } else { - boolean flag1 = this.isWantedBlock(mob, transportItemTarget.state) + boolean isValidTarget = this.isWantedBlock(body, transportItemTarget.state) && !this.isPositionAlreadyVisited(visitedPositions, unreachablePositions, transportItemTarget, level) - && !this.isContainerLocked(transportItemTarget); + // Paper start - ItemTransportingEntityValidateTargetEvent + && !this.isContainerLocked(transportItemTarget) -+ && org.bukkit.craftbukkit.event.CraftEventFactory.callTransporterValidateTarget(mob, level, transportItemTarget.pos); ++ && org.bukkit.craftbukkit.event.CraftEventFactory.callTransporterValidateTarget(body, level, transportItemTarget.pos); + // Paper end - ItemTransportingEntityValidateTargetEvent - return flag1 ? transportItemTarget : null; + return isValidTarget ? transportItemTarget : null; } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TryLaySpawnOnFluidNearLand.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TryLaySpawnOnFluidNearLand.java.patch new file mode 100644 index 000000000000..6660933f61d9 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TryLaySpawnOnFluidNearLand.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/world/entity/ai/behavior/TryLaySpawnOnFluidNearLand.java ++++ b/net/minecraft/world/entity/ai/behavior/TryLaySpawnOnFluidNearLand.java +@@ -33,6 +_,12 @@ + BlockPos spawnPos = relativePos.above(); + if (level.getBlockState(spawnPos).isAir()) { + BlockState newState = spawnBlock.defaultBlockState(); ++ // CraftBukkit start ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(body, spawnPos, newState)) { ++ pregnant.erase(); ++ return true; ++ } ++ // CraftBukkit end + level.setBlock(spawnPos, newState, Block.UPDATE_ALL); + level.gameEvent(GameEvent.BLOCK_PLACE, spawnPos, GameEvent.Context.of(body, newState)); + level.playSound(null, body, SoundEvents.FROG_LAY_SPAWN, SoundSource.BLOCKS, 1.0F, 1.0F); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TryLaySpawnOnWaterNearLand.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TryLaySpawnOnWaterNearLand.java.patch deleted file mode 100644 index be0a7c556113..000000000000 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/TryLaySpawnOnWaterNearLand.java.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- a/net/minecraft/world/entity/ai/behavior/TryLaySpawnOnWaterNearLand.java -+++ b/net/minecraft/world/entity/ai/behavior/TryLaySpawnOnWaterNearLand.java -@@ -33,6 +_,12 @@ - BlockPos blockPos2 = blockPos1.above(); - if (level.getBlockState(blockPos2).isAir()) { - BlockState blockState = spawnBlock.defaultBlockState(); -+ // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, blockPos2, blockState)) { -+ isPregnant.erase(); -+ return true; -+ } -+ // CraftBukkit end - level.setBlock(blockPos2, blockState, Block.UPDATE_ALL); - level.gameEvent(GameEvent.BLOCK_PLACE, blockPos2, GameEvent.Context.of(entity, blockState)); - level.playSound(null, entity, SoundEvents.FROG_LAY_SPAWN, SoundSource.BLOCKS, 1.0F, 1.0F); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java.patch index 0a009423ec25..5ad8af6c0131 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java.patch @@ -1,23 +1,23 @@ --- a/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java +++ b/net/minecraft/world/entity/ai/behavior/VillagerMakeLove.java @@ -111,11 +_,17 @@ - if (breedOffspring == null) { + if (child == null) { return Optional.empty(); } else { -- parent.setAge(6000); -- partner.setAge(6000); - breedOffspring.setAge(-24000); +- source.setAge(6000); +- target.setAge(6000); + child.setAge(-24000); + // Paper - Move age setting down - breedOffspring.snapTo(parent.getX(), parent.getY(), parent.getZ(), 0.0F, 0.0F); -- level.addFreshEntityWithPassengers(breedOffspring); + child.snapTo(source.getX(), source.getY(), source.getZ(), 0.0F, 0.0F); +- level.addFreshEntityWithPassengers(child); + // CraftBukkit start - call EntityBreedEvent -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(breedOffspring, parent, partner, null, null, 0).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(child, source, target, null, null, 0).isCancelled()) { + return Optional.empty(); + } -+ parent.setAge(6000); -+ partner.setAge(6000); -+ level.addFreshEntityWithPassengers(breedOffspring, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); ++ source.setAge(6000); ++ target.setAge(6000); ++ level.addFreshEntityWithPassengers(child, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); + // CraftBukkit end - call EntityBreedEvent - level.broadcastEntityEvent(breedOffspring, EntityEvent.LOVE_HEARTS); - return Optional.of(breedOffspring); + level.broadcastEntityEvent(child, EntityEvent.LOVE_HEARTS); + return Optional.of(child); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/WorkAtComposter.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/WorkAtComposter.java.patch index 55a834a3c650..80f6741907df 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/WorkAtComposter.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/WorkAtComposter.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/entity/ai/behavior/WorkAtComposter.java +++ b/net/minecraft/world/entity/ai/behavior/WorkAtComposter.java @@ -87,7 +_,9 @@ - inventory.removeItemType(Items.WHEAT, i3); - ItemStack itemStack = inventory.addItem(new ItemStack(Items.BREAD, min)); - if (!itemStack.isEmpty()) { -+ villager.forceDrops = true; // Paper - Add missing forceDrop toggles - villager.spawnAtLocation(level, itemStack, 0.5F); -+ villager.forceDrops = false; // Paper - Add missing forceDrop toggles + inventory.removeItemType(Items.WHEAT, howMuchWheatToUse); + ItemStack breadICantCarry = inventory.addItem(new ItemStack(Items.BREAD, howMuchBreadToMake)); + if (!breadICantCarry.isEmpty()) { ++ body.forceDrops = true; // Paper - Add missing forceDrop toggles + body.spawnAtLocation(level, breadICantCarry, 0.5F); ++ body.forceDrops = false; // Paper - Add missing forceDrop toggles } } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/warden/Digging.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/warden/Digging.java.patch index c571e45b5ed3..797cbc0d9ce7 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/warden/Digging.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/warden/Digging.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/entity/ai/behavior/warden/Digging.java @@ -39,7 +_,7 @@ @Override - protected void stop(ServerLevel level, E entity, long gameTime) { - if (entity.getRemovalReason() == null) { -- entity.remove(Entity.RemovalReason.DISCARDED); -+ entity.remove(Entity.RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - Add bukkit remove cause + protected void stop(final ServerLevel level, final E body, final long timestamp) { + if (body.getRemovalReason() == null) { +- body.remove(Entity.RemovalReason.DISCARDED); ++ body.remove(Entity.RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - Add bukkit remove cause } } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/warden/SonicBoom.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/warden/SonicBoom.java.patch index 37f0c908483d..fb04f9af196f 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/warden/SonicBoom.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/behavior/warden/SonicBoom.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/ai/behavior/warden/SonicBoom.java +++ b/net/minecraft/world/entity/ai/behavior/warden/SonicBoom.java @@ -84,7 +_,7 @@ - if (livingEntity.hurtServer(level, level.damageSources().sonicBoom(owner), 10.0F)) { - double d = 0.5 * (1.0 - livingEntity.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE)); - double d1 = 2.5 * (1.0 - livingEntity.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE)); -- livingEntity.push(vec32.x() * d1, vec32.y() * d, vec32.z() * d1); -+ livingEntity.push(vec32.x() * d1, vec32.y() * d, vec32.z() * d1, owner); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent + if (target.hurtServer(level, level.damageSources().sonicBoom(body), 10.0F)) { + double knockbackVertical = 0.5 * (1.0 - target.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE)); + double knockbackHorizontal = 2.5 * (1.0 - target.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE)); +- target.push(normalize.x() * knockbackHorizontal, normalize.y() * knockbackVertical, normalize.z() * knockbackHorizontal); ++ target.push(normalize.x() * knockbackHorizontal, normalize.y() * knockbackVertical, normalize.z() * knockbackHorizontal, body); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent } }); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/EatBlockGoal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/EatBlockGoal.java.patch index 4ecb07b24c57..fff4a9b18ff1 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/EatBlockGoal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/EatBlockGoal.java.patch @@ -3,21 +3,21 @@ @@ -62,8 +_,9 @@ this.eatAnimationTick = Math.max(0, this.eatAnimationTick - 1); if (this.eatAnimationTick == this.adjustedTickDelay(4)) { - BlockPos blockPos = this.mob.blockPosition(); -- if (IS_EDIBLE.test(this.level.getBlockState(blockPos))) { + BlockPos pos = this.mob.blockPosition(); +- if (IS_EDIBLE.test(this.level.getBlockState(pos))) { - if (getServerLevel(this.level).getGameRules().get(GameRules.MOB_GRIEFING)) { -+ final BlockState blockState = this.level.getBlockState(blockPos); // Paper - fix wrong block state ++ final BlockState blockState = this.level.getBlockState(pos); // Paper - fix wrong block state + if (IS_EDIBLE.test(blockState)) { // Paper - fix wrong block state -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockPos, blockState.getFluidState().createLegacyBlock(), !getServerLevel(this.level).getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit // Paper - fix wrong block state - this.level.destroyBlock(blockPos, false); ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, pos, blockState.getFluidState().createLegacyBlock(), !getServerLevel(this.level).getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit // Paper - fix wrong block state + this.level.destroyBlock(pos, false); } @@ -71,7 +_,7 @@ } else { - BlockPos blockPos1 = blockPos.below(); - if (this.level.getBlockState(blockPos1).is(Blocks.GRASS_BLOCK)) { + BlockPos below = pos.below(); + if (this.level.getBlockState(below).is(Blocks.GRASS_BLOCK)) { - if (getServerLevel(this.level).getGameRules().get(GameRules.MOB_GRIEFING)) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockPos1, Blocks.DIRT.defaultBlockState(), !getServerLevel(this.level).getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit // Paper - Fix wrong block state - this.level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, blockPos1, Block.getId(Blocks.GRASS_BLOCK.defaultBlockState())); - this.level.setBlock(blockPos1, Blocks.DIRT.defaultBlockState(), Block.UPDATE_CLIENTS); ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, below, Blocks.DIRT.defaultBlockState(), !getServerLevel(this.level).getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit // Paper - Fix wrong block state + this.level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, below, Block.getId(Blocks.GRASS_BLOCK.defaultBlockState())); + this.level.setBlock(below, Blocks.DIRT.defaultBlockState(), Block.UPDATE_CLIENTS); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/FloatGoal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/FloatGoal.java.patch index f73e97c9036c..737d4e23de77 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/FloatGoal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/FloatGoal.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/entity/ai/goal/FloatGoal.java @@ -9,6 +_,7 @@ - public FloatGoal(Mob mob) { + public FloatGoal(final Mob mob) { this.mob = mob; + if (mob.level().paperConfig().entities.behavior.spawnerNerfedMobsShouldJump) mob.goalFloat = this; // Paper - Allow nerfed mobs to jump and float this.setFlags(EnumSet.of(Goal.Flag.JUMP)); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/FollowOwnerGoal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/FollowOwnerGoal.java.patch index 29d8247ac576..f1da063b5129 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/FollowOwnerGoal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/FollowOwnerGoal.java.patch @@ -2,8 +2,8 @@ +++ b/net/minecraft/world/entity/ai/goal/FollowOwnerGoal.java @@ -71,7 +_,7 @@ public void tick() { - boolean shouldTryTeleportToOwner = this.tamable.shouldTryTeleportToOwner(); - if (!shouldTryTeleportToOwner) { + boolean isOwnerFarAway = this.tamable.shouldTryTeleportToOwner(); + if (!isOwnerFarAway) { - this.tamable.getLookControl().setLookAt(this.owner, 10.0F, this.tamable.getMaxHeadXRot()); + if (this.tamable.distanceToSqr(this.owner) <= 16 * 16) this.tamable.getLookControl().setLookAt(this.owner, 10.0F, this.tamable.getMaxHeadXRot()); // Paper - Limit pet look distance } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/Goal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/Goal.java.patch index e64289b68543..6a67fd5bf5a9 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/Goal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/Goal.java.patch @@ -15,8 +15,8 @@ + } + // Paper end - Mob Goal API + - protected int adjustedTickDelay(int adjustment) { - return this.requiresUpdateEveryTick() ? adjustment : reducedTickDelay(adjustment); + protected int adjustedTickDelay(final int ticks) { + return this.requiresUpdateEveryTick() ? ticks : reducedTickDelay(ticks); } @@ -62,7 +_,19 @@ return (ServerLevel)level; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java.patch index a42f7a01a855..624465630772 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java.patch @@ -1,40 +1,40 @@ --- a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java +++ b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java -@@ -104,6 +_,11 @@ +@@ -101,6 +_,11 @@ } if (this.ticksSinceReachedGoal > 60) { + // CraftBukkit start - Step on eggs -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityInteractEvent(this.removerMob, org.bukkit.craftbukkit.block.CraftBlock.at(level, posWithBlock))) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityInteractEvent(this.removerMob, org.bukkit.craftbukkit.block.CraftBlock.at(level, eatPos))) { + return; + } + // CraftBukkit end - level.removeBlock(posWithBlock, false); + level.removeBlock(eatPos, false); if (!level.isClientSide()) { for (int i = 0; i < 20; i++) { -@@ -123,13 +_,16 @@ +@@ -119,13 +_,16 @@ } - private @Nullable BlockPos getPosWithBlock(BlockPos pos, BlockGetter level) { + private @Nullable BlockPos getPosWithBlock(final BlockPos pos, final BlockGetter level) { - if (level.getBlockState(pos).is(this.blockToRemove)) { -+ net.minecraft.world.level.block.state.BlockState block = level.getBlockStateIfLoaded(pos); // Paper - Prevent AI rules from loading chunks -+ if (block == null) return null; // Paper - Prevent AI rules from loading chunks -+ if (block.is(this.blockToRemove)) { // Paper - Prevent AI rules from loading chunks ++ net.minecraft.world.level.block.state.BlockState state = level.getBlockStateIfLoaded(pos); // Paper - Prevent AI rules from loading chunks ++ if (state == null) return null; // Paper - Prevent AI rules from loading chunks ++ if (state.is(this.blockToRemove)) { // Paper - Prevent AI rules from loading chunks return pos; } else { - BlockPos[] blockPoss = new BlockPos[]{pos.below(), pos.west(), pos.east(), pos.north(), pos.south(), pos.below().below()}; + BlockPos[] neighbours = new BlockPos[]{pos.below(), pos.west(), pos.east(), pos.north(), pos.south(), pos.below().below()}; - for (BlockPos blockPos : blockPoss) { -- if (level.getBlockState(blockPos).is(this.blockToRemove)) { -+ net.minecraft.world.level.block.state.BlockState block2 = level.getBlockStateIfLoaded(blockPos); // Paper - Prevent AI rules from loading chunks -+ if (block2 != null && block2.is(this.blockToRemove)) { // Paper - Prevent AI rules from loading chunks - return blockPos; + for (BlockPos neighborPos : neighbours) { +- if (level.getBlockState(neighborPos).is(this.blockToRemove)) { ++ net.minecraft.world.level.block.state.BlockState neighborState = level.getBlockStateIfLoaded(neighborPos); // Paper - Prevent AI rules from loading chunks ++ if (neighborState != null && neighborState.is(this.blockToRemove)) { // Paper - Prevent AI rules from loading chunks + return neighborPos; } } -@@ -140,7 +_,7 @@ +@@ -136,7 +_,7 @@ @Override - protected boolean isValidTarget(LevelReader level, BlockPos pos) { + protected boolean isValidTarget(final LevelReader level, final BlockPos pos) { - ChunkAccess chunk = level.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()), ChunkStatus.FULL, false); + ChunkAccess chunk = level.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); // Paper - Prevent AI rules from loading chunks return chunk != null diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java.patch index 402f2ab1caa5..41c76628a4e7 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java +++ b/net/minecraft/world/entity/ai/goal/RunAroundLikeCrazyGoal.java @@ -59,7 +_,7 @@ - if (firstPassenger instanceof Player player) { + if (passenger instanceof Player player) { int temper = this.horse.getTemper(); int maxTemper = this.horse.getMaxTemper(); - if (maxTemper > 0 && this.horse.getRandom().nextInt(maxTemper) < temper) { -+ if (maxTemper > 0 && this.horse.getRandom().nextInt(maxTemper) < temper && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.horse, ((org.bukkit.craftbukkit.entity.CraftHumanEntity) this.horse.getBukkitEntity().getPassenger()).getHandle()).isCancelled()) { // CraftBukkit - fire EntityTameEvent ++ if (maxTemper > 0 && this.horse.getRandom().nextInt(maxTemper) < temper && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.horse, player).isCancelled()) { // CraftBukkit - fire EntityTameEvent this.horse.tameWithName(player); return; } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/SwellGoal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/SwellGoal.java.patch index 90559c5ec7ab..ed42ecb6dffa 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/SwellGoal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/SwellGoal.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/entity/ai/goal/SwellGoal.java +++ b/net/minecraft/world/entity/ai/goal/SwellGoal.java @@ -20,6 +_,7 @@ - return this.creeper.getSwellDir() > 0 || target != null && this.creeper.distanceToSqr(target) < 9.0; + return this.creeper.getSwellDir() > 0 || target != null && !target.isDeadOrDying() && this.creeper.distanceToSqr(target) < 9.0; } + @Override public void start() { this.creeper.getNavigation().stop(); -@@ -40,7 +_,7 @@ +@@ -39,7 +_,7 @@ + @Override public void tick() { - if (this.target == null) { - this.creeper.setSwellDir(-1); -- } else if (this.creeper.distanceToSqr(this.target) > 49.0) { -+ } else if (this.creeper.distanceToSqr(this.target) > 49.0 || !net.minecraft.world.entity.EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(this.target)) { // Paper - Fix MC-179072 - consider creative / spectator players as out of reach - this.creeper.setSwellDir(-1); - } else if (!this.creeper.getSensing().hasLineOfSight(this.target)) { - this.creeper.setSwellDir(-1); + if (this.target != null && !this.target.isDeadOrDying()) { +- if (this.creeper.distanceToSqr(this.target) > 49.0) { ++ if (this.creeper.distanceToSqr(this.target) > 49.0 || !net.minecraft.world.entity.EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(this.target)) { // Paper - Fix MC-179072 - consider creative / spectator players as out of reach + this.creeper.setSwellDir(-1); + } else if (!this.creeper.getSensing().hasLineOfSight(this.target)) { + this.creeper.setSwellDir(-1); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/TemptGoal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/TemptGoal.java.patch index 1a838aa9c160..230976671c2e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/TemptGoal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/TemptGoal.java.patch @@ -29,8 +29,8 @@ this.mob.getNavigation().stop(); } -- protected void navigateTowards(Player player) { -+ protected void navigateTowards(LivingEntity player) { // Paper +- protected void navigateTowards(final Player player) { ++ protected void navigateTowards(final LivingEntity player) { // Paper this.mob.getNavigation().moveTo(player, this.speedModifier); } @@ -38,8 +38,8 @@ } @Override -- protected void navigateTowards(Player player) { -+ protected void navigateTowards(LivingEntity player) { // Paper - Vec3 vec3 = player.getEyePosition().subtract(this.mob.position()).scale(this.mob.getRandom().nextDouble()).add(this.mob.position()); - this.mob.getMoveControl().setWantedPosition(vec3.x, vec3.y, vec3.z, this.speedModifier); +- protected void navigateTowards(final Player player) { ++ protected void navigateTowards(final LivingEntity player) { // Paper + Vec3 target = player.getEyePosition().subtract(this.mob.position()).scale(this.mob.getRandom().nextDouble()).add(this.mob.position()); + this.mob.getMoveControl().setWantedPosition(target.x, target.y, target.z, this.speedModifier); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java.patch index b1cd473fdd52..abbf20097618 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java.patch @@ -9,11 +9,11 @@ this.targetMob = this.mob.getTarget(); this.timestamp = this.mob.getLastHurtByMobTimestamp(); this.unseenMemoryTicks = 300; -@@ -113,6 +_,6 @@ +@@ -111,6 +_,6 @@ } - protected void alertOther(Mob mob, LivingEntity target) { -- mob.setTarget(target); -+ mob.setTarget(target, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY); // CraftBukkit - reason + protected void alertOther(final Mob other, final LivingEntity hurtByMob) { +- other.setTarget(hurtByMob); ++ other.setTarget(hurtByMob, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY); // CraftBukkit - reason } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java.patch index d2987e4f20d7..f5d40856a48e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java +++ b/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java -@@ -72,7 +_,7 @@ +@@ -77,7 +_,7 @@ @Override public void start() { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/gossip/GossipContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/gossip/GossipContainer.java.patch index 72a27d8f56e0..a1c292908c53 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/gossip/GossipContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/gossip/GossipContainer.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/ai/gossip/GossipContainer.java +++ b/net/minecraft/world/entity/ai/gossip/GossipContainer.java -@@ -220,6 +_,44 @@ - public void remove(GossipType gossipType) { - this.entries.removeInt(gossipType); +@@ -215,6 +_,44 @@ + public void remove(final GossipType type) { + this.entries.removeInt(type); } + + // Paper start - Add villager reputation API + private static final GossipType[] TYPES = GossipType.values(); + -+ public com.destroystokyo.paper.entity.villager.Reputation getPaperReputation() { ++ public com.destroystokyo.paper.entity.villager.Reputation asReputation() { + Map map = new java.util.EnumMap<>(com.destroystokyo.paper.entity.villager.ReputationType.class); + for (Object2IntMap.Entry type : this.entries.object2IntEntrySet()) { + map.put(toApi(type.getKey()), type.getIntValue()); @@ -17,7 +17,7 @@ + return new com.destroystokyo.paper.entity.villager.Reputation(map); + } + -+ public void assignFromPaperReputation(com.destroystokyo.paper.entity.villager.Reputation rep) { ++ public void assignFromReputation(com.destroystokyo.paper.entity.villager.Reputation rep) { + for (GossipType type : TYPES) { + com.destroystokyo.paper.entity.villager.ReputationType api = toApi(type); + @@ -44,4 +44,4 @@ + // Paper end - Add villager reputation API } - record GossipEntry(UUID target, GossipType type, int value) { + private record GossipEntry(UUID target, GossipType type, int value) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java.patch index d9d5d3eea556..35cafdc37eca 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java.patch @@ -3,9 +3,9 @@ @@ -38,7 +_,7 @@ @Override - public Path createPath(Entity entity, int reachRange) { -- return this.createPath(entity.blockPosition(), reachRange); -+ return this.createPath(entity.blockPosition(), entity, reachRange); // Paper - EntityPathfindEvent + public Path createPath(final Entity target, final int reachRange) { +- return this.createPath(target.blockPosition(), reachRange); ++ return this.createPath(target.blockPosition(), target, reachRange); // Paper - EntityPathfindEvent } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java.patch index 1d1a8daaa97a..690e75c20979 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java.patch @@ -4,13 +4,13 @@ } @Override -- public Path createPath(BlockPos pos, int reachRange) { -+ public Path createPath(BlockPos pos, @javax.annotation.Nullable Entity entity, int reachRange) { // Paper - EntityPathfindEvent - LevelChunk chunkNow = this.level.getChunkSource().getChunkNow(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); - if (chunkNow == null) { +- public Path createPath(BlockPos pos, final int reachRange) { ++ public Path createPath(BlockPos pos, @org.jspecify.annotations.Nullable Entity entity, final int reachRange) { // Paper - EntityPathfindEvent + LevelChunk chunk = this.level.getChunkSource().getChunkNow(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); + if (chunk == null) { return null; @@ -51,7 +_,7 @@ - pos = this.findSurfacePosition(chunkNow, pos, reachRange); + pos = this.findSurfacePosition(chunk, pos, reachRange); } - return super.createPath(pos, reachRange); @@ -21,9 +21,9 @@ @@ -91,7 +_,7 @@ @Override - public Path createPath(Entity entity, int reachRange) { -- return this.createPath(entity.blockPosition(), reachRange); -+ return this.createPath(entity.blockPosition(), entity, reachRange); // Paper - EntityPathfindEvent + public Path createPath(final Entity target, final int reachRange) { +- return this.createPath(target.blockPosition(), reachRange); ++ return this.createPath(target.blockPosition(), target, reachRange); // Paper - EntityPathfindEvent } private int getSurfaceY() { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/PathNavigation.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/PathNavigation.java.patch index 788c01d186c7..eabd006e9a8a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/PathNavigation.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/PathNavigation.java.patch @@ -1,41 +1,41 @@ --- a/net/minecraft/world/entity/ai/navigation/PathNavigation.java +++ b/net/minecraft/world/entity/ai/navigation/PathNavigation.java -@@ -124,7 +_,12 @@ +@@ -122,7 +_,12 @@ } - public @Nullable Path createPath(BlockPos pos, int reachRange) { + public @Nullable Path createPath(final BlockPos pos, final int reachRange) { - return this.createPath(ImmutableSet.of(pos), 8, false, reachRange); + // Paper start - EntityPathfindEvent + return this.createPath(pos, null, reachRange); + } -+ public @Nullable Path createPath(BlockPos target, @Nullable Entity entity, int reachRange) { -+ return this.createPath(ImmutableSet.of(target), entity, 8, false, reachRange); ++ public @Nullable Path createPath(BlockPos pos, @Nullable Entity entity, int reachRange) { ++ return this.createPath(ImmutableSet.of(pos), entity, 8, false, reachRange); + // Paper end - EntityPathfindEvent } - public @Nullable Path createPath(BlockPos pos, int reachRange, int followRange) { -@@ -132,7 +_,7 @@ + public @Nullable Path createPath(final BlockPos pos, final int reachRange, final int maxPathLength) { +@@ -130,7 +_,7 @@ } - public @Nullable Path createPath(Entity entity, int reachRange) { -- return this.createPath(ImmutableSet.of(entity.blockPosition()), 16, true, reachRange); -+ return this.createPath(ImmutableSet.of(entity.blockPosition()), entity, 16, true, reachRange); // Paper - EntityPathfindEvent + public @Nullable Path createPath(final Entity target, final int reachRange) { +- return this.createPath(ImmutableSet.of(target.blockPosition()), 16, true, reachRange); ++ return this.createPath(ImmutableSet.of(target.blockPosition()), target, 16, true, reachRange); // Paper - EntityPathfindEvent } - protected @Nullable Path createPath(Set targets, int regionOffset, boolean offsetUpward, int reachRange) { + protected @Nullable Path createPath(final Set targets, final int radiusOffset, final boolean above, final int reachRange) { @@ -140,6 +_,16 @@ - } - - protected @Nullable Path createPath(Set targets, int regionOffset, boolean offsetUpward, int reachRange, float followRange) { + protected @Nullable Path createPath( + final Set targets, final int radiusOffset, final boolean above, final int reachRange, final float maxPathLength + ) { + // Paper start - EntityPathfindEvent -+ return this.createPath(targets, null, regionOffset, offsetUpward, reachRange, followRange); ++ return this.createPath(targets, null, radiusOffset, above, reachRange, maxPathLength); + } + -+ protected @Nullable Path createPath(Set targets, @Nullable Entity target, int regionOffset, boolean offsetUpward, int reachRange) { -+ return this.createPath(targets, target, regionOffset, offsetUpward, reachRange, this.getMaxPathLength()); ++ protected @Nullable Path createPath(Set targets, @Nullable Entity target, int radiusOffset, boolean above, int reachRange) { ++ return this.createPath(targets, target, radiusOffset, above, reachRange, this.getMaxPathLength()); + } + -+ protected @Nullable Path createPath(Set targets, @Nullable Entity target, int regionOffset, boolean offsetUpward, int reachRange, float followRange) { ++ protected @Nullable Path createPath(Set targets, @Nullable Entity target, int radiusOffset, boolean above, int reachRange, float maxPathLength) { + // Paper end - EntityPathfindEvent if (targets.isEmpty()) { return null; @@ -61,10 +61,10 @@ + } + } + // Paper end - EntityPathfindEvent - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("pathfind"); - BlockPos blockPos = offsetUpward ? this.mob.blockPosition().above() : this.mob.blockPosition(); -@@ -166,6 +_,11 @@ + ProfilerFiller profiler = Profiler.get(); + profiler.push("pathfind"); + BlockPos fromPos = above ? this.mob.blockPosition().above() : this.mob.blockPosition(); +@@ -168,6 +_,11 @@ } } @@ -73,22 +73,22 @@ + private int pathfindFailures = 0; + // Paper end - Perf: Optimise pathfinding + - public boolean moveTo(double x, double y, double z, double speedModifier) { + public boolean moveTo(final double x, final double y, final double z, final double speedModifier) { return this.moveTo(this.createPath(x, y, z, 1), speedModifier); } -@@ -175,8 +_,23 @@ +@@ -177,8 +_,23 @@ } - public boolean moveTo(Entity entity, double speedModifier) { + public boolean moveTo(final Entity target, final double speedModifier) { + // Paper start - Perf: Optimise pathfinding + if (this.pathfindFailures > 10 && this.path == null && net.minecraft.server.MinecraftServer.currentTick < this.lastFailure + 40) { + return false; + } + // Paper end - Perf: Optimise pathfinding - Path path = this.createPath(entity, 1); -- return path != null && this.moveTo(path, speedModifier); + Path newPath = this.createPath(target, 1); +- return newPath != null && this.moveTo(newPath, speedModifier); + // Paper start - Perf: Optimise pathfinding -+ if (path != null && this.moveTo(path, speedModifier)) { ++ if (newPath != null && this.moveTo(newPath, speedModifier)) { + this.lastFailure = 0; + this.pathfindFailures = 0; + return true; @@ -100,4 +100,4 @@ + // Paper end - Perf: Optimise pathfinding } - public boolean moveTo(@Nullable Path path, double speedModifier) { + public boolean moveTo(final @Nullable Path newPath, final double speedModifier) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java.patch index 470cc35f4485..7b8011ca7c7a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java.patch @@ -4,8 +4,8 @@ } @Override -- public Path createPath(BlockPos pos, int reachRange) { -+ public Path createPath(BlockPos pos, @Nullable Entity entity, int reachRange) { +- public Path createPath(final BlockPos pos, final int reachRange) { ++ public Path createPath(final BlockPos pos, @Nullable Entity entity, final int reachRange) { this.pathToPosition = pos; - return super.createPath(pos, reachRange); + return super.createPath(pos, entity, reachRange); // Paper - EntityPathfindEvent diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/sensing/Sensor.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/sensing/Sensor.java.patch index c74a750263fa..bdbf026b80ab 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/sensing/Sensor.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/sensing/Sensor.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/ai/sensing/Sensor.java +++ b/net/minecraft/world/entity/ai/sensing/Sensor.java -@@ -29,8 +_,17 @@ +@@ -28,8 +_,17 @@ .ignoreInvisibilityTesting(); private final int scanRate; private long timeToTick; + private final String configKey; // Paper - configurable sensor tick rate and timings - public Sensor(int scanRate) { + public Sensor(final int scanRate) { + // Paper start - configurable sensor tick rate and timings -+ String key = io.papermc.paper.util.MappingEnvironment.reobf() ? io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName()) : this.getClass().getName(); ++ String key = this.getClass().getName(); + int lastSeparator = key.lastIndexOf('.'); + if (lastSeparator != -1) { + key = key.substring(lastSeparator + 1); @@ -16,14 +16,14 @@ + this.configKey = key.toLowerCase(java.util.Locale.ROOT); + // Paper end this.scanRate = scanRate; - this.timeToTick = RANDOM.nextInt(scanRate); } -@@ -41,7 +_,7 @@ - public final void tick(ServerLevel level, E entity) { +@@ -43,7 +_,7 @@ + + public final void tick(final ServerLevel level, final E body) { if (--this.timeToTick <= 0L) { - this.timeToTick = this.scanRate; -+ this.timeToTick = java.util.Objects.requireNonNullElse(level.paperConfig().tickRates.sensor.get(entity.getType(), this.configKey), this.scanRate); // Paper - configurable sensor tick rate and timings - this.updateTargetingConditionRanges(entity); - this.doTick(level, entity); ++ this.timeToTick = java.util.Objects.requireNonNullElse(level.paperConfig().tickRates.sensor.get(body.getType(), this.configKey), this.scanRate); // Paper - configurable sensor tick rate and timings + this.updateTargetingConditionRanges(body); + this.doTick(level, body); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/sensing/TemptingSensor.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/sensing/TemptingSensor.java.patch index ac8b07e679f5..67840e2b1193 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/sensing/TemptingSensor.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/sensing/TemptingSensor.java.patch @@ -2,12 +2,12 @@ +++ b/net/minecraft/world/entity/ai/sensing/TemptingSensor.java @@ -48,7 +_,19 @@ .collect(Collectors.toList()); - if (!list.isEmpty()) { - Player player = list.get(0); + if (!players.isEmpty()) { + Player player = players.get(0); - brain.setMemory(MemoryModuleType.TEMPTING_PLAYER, player); + // CraftBukkit start + org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent( -+ entity, player, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TEMPT ++ body, player, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TEMPT + ); + if (event.isCancelled()) { + return; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/village/VillageSiege.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/village/VillageSiege.java.patch index 5d20e026f9ab..c4ded0a08a6a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ai/village/VillageSiege.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/village/VillageSiege.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/ai/village/VillageSiege.java +++ b/net/minecraft/world/entity/ai/village/VillageSiege.java -@@ -95,11 +_,12 @@ +@@ -102,11 +_,12 @@ zombie.finalizeSpawn(level, level.getCurrentDifficultyAt(zombie.blockPosition()), EntitySpawnReason.EVENT, null); } catch (Exception var5) { - LOGGER.warn("Failed to create zombie for village siege at {}", vec3, var5); + LOGGER.warn("Failed to create zombie for village siege at {}", spawnPos, var5); + com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(var5); // Paper - ServerExceptionEvent return; } - zombie.snapTo(vec3.x, vec3.y, vec3.z, level.random.nextFloat() * 360.0F, 0.0F); + zombie.snapTo(spawnPos.x, spawnPos.y, spawnPos.z, level.getRandom().nextFloat() * 360.0F, 0.0F); - level.addFreshEntityWithPassengers(zombie); + level.addFreshEntityWithPassengers(zombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_INVASION); // CraftBukkit } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ambient/Bat.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ambient/Bat.java.patch index 43f464a614ba..78d43b3d6a2a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ambient/Bat.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ambient/Bat.java.patch @@ -17,25 +17,25 @@ + if (level.getNearestPlayer(BAT_RESTING_TARGETING, this) != null && org.bukkit.craftbukkit.event.CraftEventFactory.handleBatToggleSleepEvent(this, true)) { // CraftBukkit - Call BatToggleSleepEvent this.setResting(false); if (!isSilent) { - level.levelEvent(null, LevelEvent.SOUND_BAT_LIFTOFF, blockPos, 0); + level.levelEvent(null, LevelEvent.SOUND_BAT_LIFTOFF, pos, 0); } } - } else { + } else if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBatToggleSleepEvent(this, true)) { // CraftBukkit - Call BatToggleSleepEvent this.setResting(false); if (!isSilent) { - level.levelEvent(null, LevelEvent.SOUND_BAT_LIFTOFF, blockPos, 0); -@@ -177,7 +_,7 @@ - float f1 = Mth.wrapDegrees(f - this.getYRot()); + level.levelEvent(null, LevelEvent.SOUND_BAT_LIFTOFF, pos, 0); +@@ -175,7 +_,7 @@ + float rotDiff = Mth.wrapDegrees(yRotD - this.getYRot()); this.zza = 0.5F; - this.setYRot(this.getYRot() + f1); -- if (this.random.nextInt(100) == 0 && level.getBlockState(blockPos1).isRedstoneConductor(level, blockPos1)) { -+ if (this.random.nextInt(100) == 0 && level.getBlockState(blockPos1).isRedstoneConductor(level, blockPos1) && org.bukkit.craftbukkit.event.CraftEventFactory.handleBatToggleSleepEvent(this, false)) { // CraftBukkit - Call BatToggleSleepEvent + this.setYRot(this.getYRot() + rotDiff); +- if (this.random.nextInt(100) == 0 && level.getBlockState(above).isRedstoneConductor(level, above)) { ++ if (this.random.nextInt(100) == 0 && level.getBlockState(above).isRedstoneConductor(level, above) && org.bukkit.craftbukkit.event.CraftEventFactory.handleBatToggleSleepEvent(this, false)) { // CraftBukkit - Call BatToggleSleepEvent this.setResting(true); } } -@@ -202,7 +_,7 @@ - if (this.isInvulnerableTo(level, damageSource)) { +@@ -200,7 +_,7 @@ + if (this.isInvulnerableTo(level, source)) { return false; } else { - if (this.isResting()) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/AgeableWaterCreature.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/AgeableWaterCreature.java.patch index a88fad2a6164..fa39b973453d 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/AgeableWaterCreature.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/AgeableWaterCreature.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/world/entity/animal/AgeableWaterCreature.java +++ b/net/minecraft/world/entity/animal/AgeableWaterCreature.java -@@ -68,6 +_,10 @@ +@@ -72,6 +_,10 @@ ) { int seaLevel = level.getSeaLevel(); - int i = seaLevel - 13; + int minSpawnLevel = seaLevel - 13; + // Paper start - Make water animal spawn height configurable + seaLevel = level.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.maximum.or(seaLevel); -+ i = level.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.minimum.or(i); ++ minSpawnLevel = level.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.minimum.or(minSpawnLevel); + // Paper end - Make water animal spawn height configurable - return pos.getY() >= i + return pos.getY() >= minSpawnLevel && pos.getY() <= seaLevel && level.getFluidState(pos.below()).is(FluidTags.WATER) diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/Animal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/Animal.java.patch index 2fea692e7ee6..9d09a0a9ef99 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/Animal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/Animal.java.patch @@ -6,30 +6,30 @@ public @Nullable EntityReference loveCause; + public @Nullable ItemStack breedItem; // CraftBukkit - Add breedItem variable - protected Animal(EntityType type, Level level) { + protected Animal(final EntityType type, final Level level) { super(type, level); @@ -84,9 +_,13 @@ } @Override -- protected void actuallyHurt(ServerLevel level, DamageSource damageSource, float amount) { +- protected void actuallyHurt(final ServerLevel level, final DamageSource source, final float dmg) { + // CraftBukkit start - void -> boolean -+ public boolean actuallyHurt(ServerLevel level, DamageSource damageSource, float amount, org.bukkit.event.entity.EntityDamageEvent event) { -+ boolean damageResult = super.actuallyHurt(level, damageSource, amount, event); ++ public boolean actuallyHurt(final ServerLevel level, final DamageSource source, final float dmg, final org.bukkit.event.entity.EntityDamageEvent event) { ++ boolean damageResult = super.actuallyHurt(level, source, dmg, event); + if (!damageResult) return false; this.resetLove(); -- super.actuallyHurt(level, damageSource, amount); +- super.actuallyHurt(level, source, dmg); + return true; + // CraftBukkit end } @Override @@ -142,8 +_,9 @@ - if (this.isFood(itemInHand)) { + if (this.isFood(itemStack)) { int age = this.getAge(); if (player instanceof ServerPlayer serverPlayer && age == 0 && this.canFallInLove()) { -+ final ItemStack breedCopy = itemInHand.copy(); // Paper - Fix EntityBreedEvent copying - this.usePlayerItem(player, hand, itemInHand); ++ final ItemStack breedCopy = itemStack.copy(); // Paper - Fix EntityBreedEvent copying + this.usePlayerItem(player, hand, itemStack); - this.setInLove(serverPlayer); + this.setInLove(serverPlayer, breedCopy); // Paper - Fix EntityBreedEvent copying this.playEatingSound(); @@ -40,13 +40,13 @@ } + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Fix EntityBreedEvent copying - public void setInLove(@Nullable Player player) { + public void setInLove(final @Nullable Player player) { - this.inLove = 600; + // Paper start - Fix EntityBreedEvent copying + this.setInLove(player, null); + } + -+ public void setInLove(@Nullable Player player, @Nullable ItemStack breedItemCopy) { ++ public void setInLove(final @Nullable Player player, final @Nullable ItemStack breedItemCopy) { + if (breedItemCopy != null) this.breedItem = breedItemCopy; + // Paper end - Fix EntityBreedEvent copying + // CraftBukkit start @@ -61,15 +61,15 @@ this.loveCause = EntityReference.of(serverPlayer); } @@ -208,23 +_,45 @@ - if (breedOffspring != null) { - breedOffspring.setBaby(true); - breedOffspring.snapTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F); -- this.finalizeSpawnChildFromBreeding(level, partner, breedOffspring); -- level.addFreshEntityWithPassengers(breedOffspring); + if (offspring != null) { + offspring.setBaby(true); + offspring.snapTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F); +- this.finalizeSpawnChildFromBreeding(level, partner, offspring); +- level.addFreshEntityWithPassengers(offspring); + // CraftBukkit start - Call EntityBreedEvent + ServerPlayer breeder = Optional.ofNullable(this.getLoveCause()).or(() -> Optional.ofNullable(partner.getLoveCause())).orElse(null); + int experience = this.getRandom().nextInt(7) + 1; -+ org.bukkit.event.entity.EntityBreedEvent entityBreedEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(breedOffspring, this, partner, breeder, this.breedItem, experience); ++ org.bukkit.event.entity.EntityBreedEvent entityBreedEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(offspring, this, partner, breeder, this.breedItem, experience); + if (entityBreedEvent.isCancelled()) { + this.resetLove(); + partner.resetLove(); @@ -77,38 +77,40 @@ + } + experience = entityBreedEvent.getExperience(); + -+ this.finalizeSpawnChildFromBreeding(level, partner, breedOffspring, experience); -+ level.addFreshEntityWithPassengers(breedOffspring, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); ++ this.finalizeSpawnChildFromBreeding(level, partner, offspring, experience); ++ level.addFreshEntityWithPassengers(offspring, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); + // CraftBukkit end - Call EntityBreedEvent } } - public void finalizeSpawnChildFromBreeding(ServerLevel level, Animal animal, @Nullable AgeableMob baby) { -- Optional.ofNullable(this.getLoveCause()).or(() -> Optional.ofNullable(animal.getLoveCause())).ifPresent(player -> { + public void finalizeSpawnChildFromBreeding(final ServerLevel level, final Animal partner, final @Nullable AgeableMob offspring) { +- Optional.ofNullable(this.getLoveCause()).or(() -> Optional.ofNullable(partner.getLoveCause())).ifPresent(cause -> { +- cause.awardStat(Stats.ANIMALS_BRED); +- CriteriaTriggers.BRED_ANIMALS.trigger(cause, this, partner, offspring); +- }); + // CraftBukkit start - Call EntityBreedEvent -+ this.finalizeSpawnChildFromBreeding(level, animal, baby, this.getRandom().nextInt(7) + 1); ++ this.finalizeSpawnChildFromBreeding(level, partner, offspring, this.getRandom().nextInt(7) + 1); + } + -+ public void finalizeSpawnChildFromBreeding(ServerLevel level, Animal animal, @Nullable AgeableMob baby, int experience) { ++ public void finalizeSpawnChildFromBreeding(final ServerLevel level, final Animal partner, final @Nullable AgeableMob offspring, final int experience) { + // CraftBukkit end - Call EntityBreedEvent + // Paper start - Call EntityBreedEvent + ServerPlayer player = this.getLoveCause(); -+ if (player == null) player = animal.getLoveCause(); ++ if (player == null) player = partner.getLoveCause(); + if (player != null) { + // Paper end - Call EntityBreedEvent - player.awardStat(Stats.ANIMALS_BRED); - CriteriaTriggers.BRED_ANIMALS.trigger(player, this, animal, baby); -- }); ++ player.awardStat(Stats.ANIMALS_BRED); ++ CriteriaTriggers.BRED_ANIMALS.trigger(player, this, partner, offspring); + } // Paper - Call EntityBreedEvent this.setAge(6000); - animal.setAge(6000); + partner.setAge(6000); this.resetLove(); - animal.resetLove(); + partner.resetLove(); level.broadcastEntityEvent(this, EntityEvent.IN_LOVE_HEARTS); - if (level.getGameRules().get(GameRules.MOB_DROPS)) { - level.addFreshEntity(new ExperienceOrb(level, this.getX(), this.getY(), this.getZ(), this.getRandom().nextInt(7) + 1)); + if (experience > 0 && level.getGameRules().get(GameRules.MOB_DROPS)) { // Paper - Call EntityBreedEvent -+ level.addFreshEntity(new ExperienceOrb(level, this.position(), net.minecraft.world.phys.Vec3.ZERO, experience, org.bukkit.entity.ExperienceOrb.SpawnReason.BREED, player, baby)); // Paper - Call EntityBreedEvent, add spawn context ++ level.addFreshEntity(new ExperienceOrb(level, this.position(), Vec3.ZERO, experience, org.bukkit.entity.ExperienceOrb.SpawnReason.BREED, player, offspring)); // Paper - Call EntityBreedEvent, add spawn context } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch index ffc51bd9ef2d..09872335226f 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch @@ -1,38 +1,38 @@ --- a/net/minecraft/world/entity/animal/Bucketable.java +++ b/net/minecraft/world/entity/animal/Bucketable.java -@@ -71,9 +_,25 @@ - static Optional bucketMobPickup(Player player, InteractionHand hand, T entity) { - ItemStack itemInHand = player.getItemInHand(hand); - if (itemInHand.getItem() == Items.WATER_BUCKET && entity.isAlive()) { -- entity.playSound(entity.getPickupSound(), 1.0F, 1.0F); +@@ -73,9 +_,25 @@ + ) { + ItemStack itemStack = player.getItemInHand(hand); + if (itemStack.getItem() == Items.WATER_BUCKET && pickupEntity.isAlive()) { +- pickupEntity.playSound(pickupEntity.getPickupSound(), 1.0F, 1.0F); + // CraftBukkit start -+ // entity.playSound(entity.getPickupSound(), 1.0F, 1.0F); // CraftBukkit - moved down - ItemStack bucketItemStack = entity.getBucketItemStack(); - entity.saveToBucketTag(bucketItemStack); -+ org.bukkit.event.player.PlayerBucketEntityEvent playerBucketFishEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerFishBucketEvent(entity, player, itemInHand, bucketItemStack, hand); -+ bucketItemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(playerBucketFishEvent.getEntityBucket()); ++ // pickupEntity.playSound(pickupEntity.getPickupSound(), 1.0F, 1.0F); // CraftBukkit - moved down + ItemStack bucket = pickupEntity.getBucketItemStack(); + pickupEntity.saveToBucketTag(bucket); ++ org.bukkit.event.player.PlayerBucketEntityEvent playerBucketFishEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerFishBucketEvent(pickupEntity, player, itemStack, bucket, hand); ++ bucket = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(playerBucketFishEvent.getEntityBucket()); + if (playerBucketFishEvent.isCancelled()) { + // Paper start - Fix inventory desync -+ if (player.hasInfiniteMaterials() || itemInHand.getCount() > 1) { ++ if (player.hasInfiniteMaterials() || itemStack.getCount() > 1) { + player.containerMenu.sendAllDataToRemote(); + } else { + player.containerMenu.forceHeldSlot(hand); + } + // Paper end - Fix inventory desync -+ entity.resendPossiblyDesyncedEntityData((ServerPlayer) player); // Paper ++ pickupEntity.resendPossiblyDesyncedEntityData((ServerPlayer) player); // Paper + return Optional.of(InteractionResult.FAIL); + } -+ entity.playSound(entity.getPickupSound(), 1.0F, 1.0F); ++ pickupEntity.playSound(pickupEntity.getPickupSound(), 1.0F, 1.0F); + // CraftBukkit end - ItemStack itemStack = ItemUtils.createFilledResult(itemInHand, player, bucketItemStack, false); - player.setItemInHand(hand, itemStack); - Level level = entity.level(); -@@ -81,7 +_,7 @@ - CriteriaTriggers.FILLED_BUCKET.trigger((ServerPlayer)player, bucketItemStack); + ItemStack result = ItemUtils.createFilledResult(itemStack, player, bucket, false); + player.setItemInHand(hand, result); + Level level = pickupEntity.level(); +@@ -83,7 +_,7 @@ + CriteriaTriggers.FILLED_BUCKET.trigger((ServerPlayer)player, bucket); } -- entity.discard(); -+ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause +- pickupEntity.discard(); ++ pickupEntity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause return Optional.of(InteractionResult.SUCCESS); } else { return Optional.empty(); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/allay/Allay.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/allay/Allay.java.patch index ec19e8dbe40e..6bff6c78716a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/allay/Allay.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/allay/Allay.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/animal/allay/Allay.java +++ b/net/minecraft/world/entity/animal/allay/Allay.java -@@ -112,6 +_,7 @@ +@@ -99,6 +_,7 @@ private float dancingAnimationTicks; private float spinningAnimationTicks; private float spinningAnimationTicks0; + public boolean forceDancing = false; // CraftBukkit - public Allay(EntityType type, Level level) { + public Allay(final EntityType type, final Level level) { super(type, level); -@@ -125,6 +_,12 @@ +@@ -112,6 +_,12 @@ ); } @@ -19,9 +19,9 @@ + // CraftBukkit end + @Override - protected Brain.Provider brainProvider() { - return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); -@@ -232,7 +_,7 @@ + protected Brain makeBrain(final Brain.Packed packedBrain) { + return BRAIN_PROVIDER.makeBrain(this, packedBrain); +@@ -214,7 +_,7 @@ public void aiStep() { super.aiStep(); if (!this.level().isClientSide() && this.isAlive() && this.tickCount % 10 == 0) { @@ -30,10 +30,10 @@ } if (this.isDancing() && this.shouldStopDancing() && this.tickCount % 20 == 0) { -@@ -300,7 +_,12 @@ - ItemStack itemInHand = player.getItemInHand(hand); - ItemStack itemInHand1 = this.getItemInHand(InteractionHand.MAIN_HAND); - if (this.isDancing() && itemInHand.is(ItemTags.DUPLICATES_ALLAYS) && this.canDuplicate()) { +@@ -282,7 +_,12 @@ + ItemStack interactionItem = player.getItemInHand(hand); + ItemStack itemInHand = this.getItemInHand(InteractionHand.MAIN_HAND); + if (this.isDancing() && interactionItem.is(ItemTags.DUPLICATES_ALLAYS) && this.canDuplicate()) { - this.duplicateAllay(); + // CraftBukkit start - handle cancel duplication + Allay allay = this.duplicateAllay(); @@ -43,8 +43,8 @@ + // CraftBukkit end this.level().broadcastEntityEvent(this, EntityEvent.IN_LOVE_HEARTS); this.level().playSound(player, this, SoundEvents.AMETHYST_BLOCK_CHIME, SoundSource.NEUTRAL, 2.0F, 1.0F); - this.removeInteractionItem(player, itemInHand); -@@ -399,6 +_,7 @@ + this.removeInteractionItem(player, interactionItem); +@@ -381,6 +_,7 @@ } private boolean shouldStopDancing() { @@ -52,7 +52,7 @@ return this.jukeboxPos == null || !this.jukeboxPos.closerToCenterThan(this.position(), GameEvent.JUKEBOX_PLAY.value().notificationRadius()) || !this.level().getBlockState(this.jukeboxPos).is(Blocks.JUKEBOX); -@@ -451,7 +_,7 @@ +@@ -433,7 +_,7 @@ super.readAdditionalSaveData(input); this.readInventoryFromTag(input); this.vibrationData = input.read("listener", VibrationSystem.Data.CODEC).orElseGet(VibrationSystem.Data::new); @@ -61,7 +61,7 @@ } @Override -@@ -470,15 +_,17 @@ +@@ -452,15 +_,17 @@ this.entityData.set(DATA_CAN_DUPLICATE, duplicationCooldown == 0L); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/armadillo/Armadillo.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/armadillo/Armadillo.java.patch index 81ee48b69608..77e4f5dc38d0 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/armadillo/Armadillo.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/armadillo/Armadillo.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java +++ b/net/minecraft/world/entity/animal/armadillo/Armadillo.java -@@ -137,10 +_,12 @@ +@@ -145,10 +_,12 @@ ArmadilloAi.updateActivity(this); - profilerFiller.pop(); + profiler.pop(); if (this.isAlive() && --this.scuteTime <= 0 && this.shouldDropLoot(level)) { + this.forceDrops = true; // CraftBukkit if (this.dropFromGiftLootTable(level, BuiltInLootTables.ARMADILLO_SHED, this::spawnAtLocation)) { @@ -13,21 +13,21 @@ this.scuteTime = this.pickNextScuteDropTime(); } -@@ -277,8 +_,11 @@ +@@ -285,8 +_,11 @@ } @Override -- protected void actuallyHurt(ServerLevel level, DamageSource damageSource, float amount) { -- super.actuallyHurt(level, damageSource, amount); +- protected void actuallyHurt(final ServerLevel level, final DamageSource source, final float dmg) { +- super.actuallyHurt(level, source, dmg); + // CraftBukkit start - void -> boolean -+ public boolean actuallyHurt(ServerLevel level, DamageSource damageSource, float amount, org.bukkit.event.entity.EntityDamageEvent event) { -+ boolean damageResult = super.actuallyHurt(level, damageSource, amount, event); ++ public boolean actuallyHurt(final ServerLevel level, final DamageSource source, final float dmg, org.bukkit.event.entity.EntityDamageEvent event) { ++ boolean damageResult = super.actuallyHurt(level, source, dmg, event); + if (!damageResult) return false; + // CraftBukkit end if (!this.isNoAi() && !this.isDeadOrDying()) { - if (damageSource.getEntity() instanceof LivingEntity) { + if (source.getEntity() instanceof LivingEntity) { this.getBrain().setMemoryWithExpiry(MemoryModuleType.DANGER_DETECTED_RECENTLY, true, 80L); -@@ -289,6 +_,7 @@ +@@ -297,6 +_,7 @@ this.rollOut(); } } @@ -35,12 +35,12 @@ } @Override -@@ -307,7 +_,9 @@ +@@ -315,7 +_,9 @@ return false; } else { - if (this.level() instanceof ServerLevel serverLevel) { + if (this.level() instanceof ServerLevel level) { + this.forceDrops = true; // CraftBukkit - this.dropFromEntityInteractLootTable(serverLevel, BuiltInLootTables.ARMADILLO_BRUSH, entity, stack, this::spawnAtLocation); + this.dropFromEntityInteractLootTable(level, BuiltInLootTables.ARMADILLO_BRUSH, interactingEntity, tool, this::spawnAtLocation); + this.forceDrops = false; // CraftBukkit this.playSound(SoundEvents.ARMADILLO_BRUSH); this.gameEvent(GameEvent.ENTITY_INTERACT); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/axolotl/Axolotl.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/axolotl/Axolotl.java.patch index db1f768482cf..4b25ea0df6ad 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/axolotl/Axolotl.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/axolotl/Axolotl.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/axolotl/Axolotl.java +++ b/net/minecraft/world/entity/animal/axolotl/Axolotl.java -@@ -232,7 +_,7 @@ +@@ -270,7 +_,7 @@ @Override public int getMaxAirSupply() { @@ -9,12 +9,12 @@ } public Axolotl.Variant getVariant() { -@@ -448,10 +_,10 @@ - if (effect == null || effect.endsWithin(2399)) { - int i = effect != null ? effect.getDuration() : 0; - int min = Math.min(2400, 100 + i); -- player.addEffect(new MobEffectInstance(MobEffects.REGENERATION, min, 0), this); -+ player.addEffect(new MobEffectInstance(MobEffects.REGENERATION, min, 0), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.AXOLOTL); // CraftBukkit +@@ -484,10 +_,10 @@ + if (regenEffect == null || regenEffect.endsWithin(2399)) { + int previousDuration = regenEffect != null ? regenEffect.getDuration() : 0; + int regenDuration = Math.min(2400, 100 + previousDuration); +- player.addEffect(new MobEffectInstance(MobEffects.REGENERATION, regenDuration, 0), this); ++ player.addEffect(new MobEffectInstance(MobEffects.REGENERATION, regenDuration, 0), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.AXOLOTL); // CraftBukkit } - player.removeEffect(MobEffects.MINING_FATIGUE); @@ -22,7 +22,7 @@ } @Override -@@ -530,6 +_,13 @@ +@@ -565,6 +_,13 @@ ) { return level.getBlockState(pos.below()).is(BlockTags.AXOLOTLS_SPAWNABLE_ON); } @@ -34,5 +34,5 @@ + } + // CraftBukkit end - public static enum AnimationState { - PLAYING_DEAD, + @Override + public EntityDimensions getDefaultDimensions(final Pose pose) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/bee/Bee.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/bee/Bee.java.patch index f215390a758b..9764afc7614d 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/bee/Bee.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/bee/Bee.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/entity/animal/bee/Bee.java +++ b/net/minecraft/world/entity/animal/bee/Bee.java -@@ -149,10 +_,26 @@ - Bee.BeeGoToHiveGoal goToHiveGoal; +@@ -150,10 +_,26 @@ + private Bee.BeeGoToHiveGoal goToHiveGoal; private Bee.BeeGoToKnownFlowerGoal goToKnownFlowerGoal; private int underWaterTicks; + public net.kyori.adventure.util.TriState rollingOverride = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Rolling override - public Bee(EntityType type, Level level) { + public Bee(final EntityType type, final Level level) { super(type, level); - this.moveControl = new FlyingMoveControl(this, 20, true); + // Paper start - Fix MC-167279 @@ -26,18 +26,18 @@ + this.moveControl = new BeeFlyingMoveControl(this, 20, true); + // Paper end - Fix MC-167279 this.lookControl = new Bee.BeeLookControl(this); - this.setPathfindingMalus(PathType.DANGER_FIRE, -1.0F); + this.setPathfindingMalus(PathType.FIRE_IN_NEIGHBOR, -1.0F); this.setPathfindingMalus(PathType.WATER, -1.0F); -@@ -199,9 +_,18 @@ +@@ -200,9 +_,18 @@ @Override - protected void addAdditionalSaveData(ValueOutput output) { + protected void addAdditionalSaveData(final ValueOutput output) { + // CraftBukkit start - selectively save data + this.addAdditionalSaveData(output, true); + } + + @Override -+ public void addAdditionalSaveData(ValueOutput output, boolean saveAll) { ++ public void addAdditionalSaveData(final ValueOutput output, final boolean saveAll) { + // CraftBukkit end super.addAdditionalSaveData(output); + if (saveAll) { // Paper @@ -47,16 +47,16 @@ output.putBoolean("HasNectar", this.hasNectar()); output.putBoolean("HasStung", this.hasStung()); output.putInt("TicksSincePollination", this.ticksWithoutNectarSinceExitingHive); -@@ -239,7 +_,7 @@ +@@ -240,7 +_,7 @@ } - if (i > 0) { -- livingEntity.addEffect(new MobEffectInstance(MobEffects.POISON, i * 20, 0), this); -+ livingEntity.addEffect(new MobEffectInstance(MobEffects.POISON, i * 20, 0), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + if (poisonTime > 0) { +- livingTarget.addEffect(new MobEffectInstance(MobEffects.POISON, poisonTime * 20, 0), this); ++ livingTarget.addEffect(new MobEffectInstance(MobEffects.POISON, poisonTime * 20, 0), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit } } -@@ -480,7 +_,11 @@ +@@ -483,7 +_,11 @@ if (this.hivePos == null) { return null; } else { @@ -69,58 +69,60 @@ } } -@@ -513,6 +_,7 @@ +@@ -515,7 +_,8 @@ + return this.getFlag(FLAG_ROLL); } - public void setRolling(boolean isRolling) { -+ isRolling = this.rollingOverride.toBooleanOrElse(isRolling); // Paper - Rolling override - this.setFlag(FLAG_ROLL, isRolling); +- public void setRolling(final boolean rolling) { ++ public void setRolling(boolean rolling) { // Paper - Rolling override ++ rolling = this.rollingOverride.toBooleanOrElse(rolling); // Paper - Rolling override + this.setFlag(FLAG_ROLL, rolling); } -@@ -569,7 +_,7 @@ - if (beeInteractionEffect != null) { - this.usePlayerItem(player, hand, itemInHand); +@@ -576,7 +_,7 @@ + if (effect != null) { + this.usePlayerItem(player, hand, heldItem); if (!this.level().isClientSide()) { -- this.addEffect(beeInteractionEffect); -+ this.addEffect(beeInteractionEffect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); // Paper - Add missing effect cause +- this.addEffect(effect); ++ this.addEffect(effect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); // Paper - Add missing effect cause } return InteractionResult.SUCCESS; -@@ -637,8 +_,9 @@ - if (this.isInvulnerableTo(level, damageSource)) { +@@ -644,8 +_,9 @@ + if (this.isInvulnerableTo(level, source)) { return false; } else { -+ if (!super.hurtServer(level, damageSource, amount)) return false; // CraftBukkit - Only stop pollinating if entity was damaged ++ if (!super.hurtServer(level, source, damage)) return false; // CraftBukkit - Only stop pollinating if entity was damaged this.beePollinateGoal.stopPollinating(); -- return super.hurtServer(level, damageSource, amount); +- return super.hurtServer(level, source, damage); + return true; // CraftBukkit - Only stop pollinating if entity was damaged } } -@@ -979,7 +_,7 @@ +@@ -1007,7 +_,7 @@ } } -- if (blockState1 != null) { -+ if (blockState1 != null && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(Bee.this, blockPos, blockState1)) { // CraftBukkit - Bee.this.level().levelEvent(LevelEvent.PARTICLES_BEE_GROWTH, blockPos, 15); - Bee.this.level().setBlockAndUpdate(blockPos, blockState1); +- if (growState != null) { ++ if (growState != null && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(Bee.this, belowPos, growState)) { // CraftBukkit + Bee.this.level().levelEvent(LevelEvent.PARTICLES_BEE_GROWTH, belowPos, 15); + Bee.this.level().setBlockAndUpdate(belowPos, growState); Bee.this.incrementNumCropsGrownSincePollination(); -@@ -1003,7 +_,7 @@ +@@ -1032,7 +_,7 @@ @Override - protected void alertOther(Mob mob, LivingEntity target) { - if (mob instanceof Bee && this.mob.hasLineOfSight(target)) { -- mob.setTarget(target); -+ mob.setTarget(target, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_ENTITY); // CraftBukkit - reason + protected void alertOther(final Mob other, final LivingEntity hurtByMob) { + if (other instanceof Bee && this.mob.hasLineOfSight(hurtByMob)) { +- other.setTarget(hurtByMob); ++ other.setTarget(hurtByMob, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_ENTITY); // CraftBukkit - reason } } } -@@ -1160,7 +_,7 @@ +@@ -1198,7 +_,7 @@ Bee.this.dropFlower(); this.pollinating = false; Bee.this.remainingCooldownBeforeLocatingNewFlower = 200; - } else { + } else if (Bee.this.savedFlowerPos != null) { // Paper - add null check since API can manipulate this - Vec3 vec3 = Vec3.atBottomCenterOf(Bee.this.savedFlowerPos).add(0.0, 0.6F, 0.0); - if (vec3.distanceTo(Bee.this.position()) > 1.0) { - this.hoverPos = vec3; + Vec3 flowerPos = Vec3.atBottomCenterOf(Bee.this.savedFlowerPos).add(0.0, 0.6F, 0.0); + if (flowerPos.distanceTo(Bee.this.position()) > 1.0) { + this.hoverPos = flowerPos; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/camel/Camel.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/camel/Camel.java.patch index 39caee1ceda0..1514f5e2c95c 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/camel/Camel.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/camel/Camel.java.patch @@ -1,37 +1,37 @@ --- a/net/minecraft/world/entity/animal/camel/Camel.java +++ b/net/minecraft/world/entity/animal/camel/Camel.java -@@ -411,12 +_,12 @@ +@@ -419,12 +_,12 @@ } else { - boolean flag = this.getHealth() < this.getMaxHealth(); - if (flag) { + boolean couldHeal = this.getHealth() < this.getMaxHealth(); + if (couldHeal) { - this.heal(2.0F); + this.heal(2.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // Paper - Add missing regain reason } - boolean flag1 = this.isTamed() && this.getAge() == 0 && this.canFallInLove(); - if (flag1) { + boolean couldSetInLove = this.isTamed() && this.getAge() == 0 && this.canFallInLove(); + if (couldSetInLove) { - this.setInLove(player); -+ this.setInLove(player, stack.copy()); // Paper - Fix EntityBreedEvent copying ++ this.setInLove(player, itemStack.copy()); // Paper - Fix EntityBreedEvent copying } - boolean isBaby = this.isBaby(); -@@ -474,9 +_,13 @@ + boolean couldAgeUp = this.canAgeUp(); +@@ -482,9 +_,13 @@ } @Override -- protected void actuallyHurt(ServerLevel level, DamageSource damageSource, float amount) { +- protected void actuallyHurt(final ServerLevel level, final DamageSource source, final float dmg) { + // CraftBukkit start - void -> boolean -+ public boolean actuallyHurt(ServerLevel level, DamageSource damageSource, float amount, org.bukkit.event.entity.EntityDamageEvent event) { -+ boolean damageResult = super.actuallyHurt(level, damageSource, amount, event); ++ public boolean actuallyHurt(final ServerLevel level, final DamageSource source, final float dmg, final org.bukkit.event.entity.EntityDamageEvent event) { ++ boolean damageResult = super.actuallyHurt(level, source, dmg, event); + if (!damageResult) return false; + // CraftBukkit end this.standUpInstantly(); -- super.actuallyHurt(level, damageSource, amount); +- super.actuallyHurt(level, source, dmg); + return true; // CraftBukkit } @Override -@@ -571,7 +_,7 @@ +@@ -581,7 +_,7 @@ } public void sitDown() { @@ -40,7 +40,7 @@ this.makeSound(this.getSitDownSound()); this.setPose(Pose.SITTING); this.gameEvent(GameEvent.ENTITY_ACTION); -@@ -580,7 +_,7 @@ +@@ -590,7 +_,7 @@ } public void standUp() { @@ -49,7 +49,7 @@ this.makeSound(this.getStandUpSound()); this.setPose(Pose.STANDING); this.gameEvent(GameEvent.ENTITY_ACTION); -@@ -597,6 +_,7 @@ +@@ -607,6 +_,7 @@ } public void standUpInstantly() { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/chicken/Chicken.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/chicken/Chicken.java.patch index b462eb81c29d..87b77809a3cd 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/chicken/Chicken.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/chicken/Chicken.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/animal/chicken/Chicken.java +++ b/net/minecraft/world/entity/animal/chicken/Chicken.java -@@ -113,10 +_,12 @@ +@@ -128,10 +_,12 @@ this.flap = this.flap + this.flapping * 2.0F; - if (this.level() instanceof ServerLevel serverLevel && this.isAlive() && !this.isBaby() && !this.isChickenJockey() && --this.eggTime <= 0) { + if (this.level() instanceof ServerLevel level && this.isAlive() && !this.isBaby() && !this.isChickenJockey() && --this.eggTime <= 0) { + this.forceDrops = true; // CraftBukkit - if (this.dropFromGiftLootTable(serverLevel, BuiltInLootTables.CHICKEN_LAY, this::spawnAtLocation)) { + if (this.dropFromGiftLootTable(level, BuiltInLootTables.CHICKEN_LAY, this::spawnAtLocation)) { this.playSound(SoundEvents.CHICKEN_EGG, 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); this.gameEvent(GameEvent.ENTITY_PLACE); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/cow/AbstractCow.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/cow/AbstractCow.java.patch index 396d38adc60d..0d24fa52ee3b 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/cow/AbstractCow.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/cow/AbstractCow.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/entity/animal/cow/AbstractCow.java +++ b/net/minecraft/world/entity/animal/cow/AbstractCow.java -@@ -85,8 +_,15 @@ - public InteractionResult mobInteract(Player player, InteractionHand hand) { - ItemStack itemInHand = player.getItemInHand(hand); - if (itemInHand.is(Items.BUCKET) && !this.isBaby()) { +@@ -89,8 +_,15 @@ + public InteractionResult mobInteract(final Player player, final InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); + if (itemStack.is(Items.BUCKET) && !this.isBaby()) { + // CraftBukkit start - Got milk? -+ org.bukkit.event.player.PlayerBucketFillEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(player.level(), player, this.blockPosition(), this.blockPosition(), null, itemInHand, Items.MILK_BUCKET, hand); ++ org.bukkit.event.player.PlayerBucketFillEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(player.level(), player, this.blockPosition(), this.blockPosition(), null, itemStack, Items.MILK_BUCKET, hand); + if (event.isCancelled()) { + player.containerMenu.sendAllDataToRemote(); // Paper - Fix inventory desync + return InteractionResult.PASS; + } + // CraftBukkit end player.playSound(SoundEvents.COW_MILK, 1.0F, 1.0F); -- ItemStack itemStack = ItemUtils.createFilledResult(itemInHand, player, Items.MILK_BUCKET.getDefaultInstance()); -+ ItemStack itemStack = ItemUtils.createFilledResult(itemInHand, player, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack())); // CraftBukkit - player.setItemInHand(hand, itemStack); +- ItemStack bucketOrMilkBucket = ItemUtils.createFilledResult(itemStack, player, Items.MILK_BUCKET.getDefaultInstance()); ++ ItemStack bucketOrMilkBucket = ItemUtils.createFilledResult(itemStack, player, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack())); // CraftBukkit + player.setItemInHand(hand, bucketOrMilkBucket); return InteractionResult.SUCCESS; } else { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/cow/MushroomCow.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/cow/MushroomCow.java.patch index c0e2f6acbeff..8f0b1d75a5ef 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/cow/MushroomCow.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/cow/MushroomCow.java.patch @@ -2,49 +2,49 @@ +++ b/net/minecraft/world/entity/animal/cow/MushroomCow.java @@ -116,7 +_,17 @@ return InteractionResult.SUCCESS; - } else if (itemInHand.is(Items.SHEARS) && this.readyForShearing()) { - if (this.level() instanceof ServerLevel serverLevel) { -- this.shear(serverLevel, SoundSource.PLAYERS, itemInHand); + } else if (itemStack.is(Items.SHEARS) && this.readyForShearing()) { + if (this.level() instanceof ServerLevel level) { +- this.shear(level, SoundSource.PLAYERS, itemStack); + // CraftBukkit start + // Paper start - custom shear drops -+ java.util.List drops = this.generateDefaultDrops(serverLevel, itemInHand); -+ org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemInHand, hand, drops); ++ java.util.List drops = this.generateDefaultDrops(level, itemStack); ++ org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemStack, hand, drops); + if (event != null) { + if (event.isCancelled()) return InteractionResult.PASS; + drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops()); + // Paper end - custom shear drops + } + // CraftBukkit end -+ this.shear(serverLevel, SoundSource.PLAYERS, itemInHand, drops); // Paper - custom shear drops ++ this.shear(level, SoundSource.PLAYERS, itemStack, drops); // Paper - custom shear drops this.gameEvent(GameEvent.SHEAR, player); - itemInHand.hurtAndBreak(1, player, hand.asEquipmentSlot()); + itemStack.hurtAndBreak(1, player, hand.asEquipmentSlot()); } @@ -170,15 +_,31 @@ @Override - public void shear(ServerLevel level, SoundSource source, ItemStack shears) { + public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool) { + // Paper start - custom shear drops -+ this.shear(level, source, shears, this.generateDefaultDrops(level, shears)); ++ this.shear(level, soundSource, tool, this.generateDefaultDrops(level, tool)); + } + + @Override -+ public java.util.List generateDefaultDrops(final ServerLevel level, final ItemStack shears) { ++ public java.util.List generateDefaultDrops(final ServerLevel level, final ItemStack tool) { + final java.util.List drops = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); -+ this.dropFromShearingLootTable(level, BuiltInLootTables.SHEAR_MOOSHROOM, shears, (ignored, stack) -> { ++ this.dropFromShearingLootTable(level, BuiltInLootTables.SHEAR_MOOSHROOM, tool, (ignored, stack) -> { + for (int i = 0; i < stack.getCount(); ++i) drops.add(stack.copyWithCount(1)); + }); + return drops; + } + + @Override -+ public void shear(ServerLevel level, SoundSource source, ItemStack shears, java.util.List drops) { ++ public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool, java.util.List drops) { + // Paper end - level.playSound(null, this, SoundEvents.MOOSHROOM_SHEAR, source, 1.0F, 1.0F); + level.playSound(null, this, SoundEvents.MOOSHROOM_SHEAR, soundSource, 1.0F, 1.0F); this.convertTo(EntityType.COW, ConversionParams.single(this, false, false), cow -> { level.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(0.5), this.getZ(), 1, 0.0, 0.0, 0.0, 0.0); -- this.dropFromShearingLootTable(level, BuiltInLootTables.SHEAR_MOOSHROOM, shears, (serverLevel, stack) -> { -- for (int i = 0; i < stack.getCount(); i++) { -- serverLevel.addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(1.0), this.getZ(), stack.copyWithCount(1))); +- this.dropFromShearingLootTable(level, BuiltInLootTables.SHEAR_MOOSHROOM, tool, (l, drop) -> { +- for (int i = 0; i < drop.getCount(); i++) { +- l.addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(1.0), this.getZ(), drop.copyWithCount(1))); - } + // Paper start - custom shear drops; moved drop generation to separate method + drops.forEach(drop -> { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/dolphin/Dolphin.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/dolphin/Dolphin.java.patch index 91bed267dcc2..566982fdccc6 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/dolphin/Dolphin.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/dolphin/Dolphin.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/dolphin/Dolphin.java +++ b/net/minecraft/world/entity/animal/dolphin/Dolphin.java -@@ -98,6 +_,13 @@ +@@ -100,6 +_,13 @@ return EntityType.DOLPHIN.create(level, EntitySpawnReason.BREEDING); } @@ -14,7 +14,7 @@ @Override public float getAgeScale() { return this.isBaby() ? 0.65F : 1.0F; -@@ -182,7 +_,7 @@ +@@ -185,7 +_,7 @@ @Override public int getMaxAirSupply() { @@ -23,24 +23,24 @@ } @Override -@@ -215,11 +_,15 @@ +@@ -218,11 +_,15 @@ if (this.getItemBySlot(EquipmentSlot.MAINHAND).isEmpty()) { - ItemStack item = entity.getItem(); - if (this.canHoldItem(item)) { + ItemStack itemStack = entity.getItem(); + if (this.canHoldItem(itemStack)) { + // CraftBukkit start - call EntityPickupItemEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(this, entity, 0).isCancelled()) return; -+ item = entity.getItem(); // CraftBukkit- update ItemStack from event ++ itemStack = entity.getItem(); // CraftBukkit- update ItemStack from event + // CraftBukkit end this.onItemPickup(entity); - this.setItemSlot(EquipmentSlot.MAINHAND, item); + this.setItemSlot(EquipmentSlot.MAINHAND, itemStack); this.setGuaranteedDrop(EquipmentSlot.MAINHAND); - this.take(entity, item.getCount()); + this.take(entity, itemStack.getCount()); - entity.discard(); + entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause } } } -@@ -479,7 +_,7 @@ +@@ -480,7 +_,7 @@ @Override public void start() { @@ -49,21 +49,21 @@ } @Override -@@ -498,7 +_,7 @@ +@@ -499,7 +_,7 @@ } - if (this.player.isSwimming() && this.player.level().random.nextInt(6) == 0) { + if (this.player.isSwimming() && this.player.level().getRandom().nextInt(6) == 0) { - this.player.addEffect(new MobEffectInstance(MobEffects.DOLPHINS_GRACE, 100), this.dolphin); + this.player.addEffect(new MobEffectInstance(MobEffects.DOLPHINS_GRACE, 100), this.dolphin, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.DOLPHIN); // CraftBukkit } } } -@@ -568,7 +_,7 @@ - 0.3F * Mth.cos(Dolphin.this.getYRot() * (float) (Math.PI / 180.0)) * Mth.cos(Dolphin.this.getXRot() * (float) (Math.PI / 180.0)) - + Mth.sin(f1) * f2 +@@ -573,7 +_,7 @@ + 0.3F * Mth.sin(Dolphin.this.getXRot() * Mth.DEG_TO_RAD) * 1.5F, + 0.3F * Mth.cos(Dolphin.this.getYRot() * Mth.DEG_TO_RAD) * Mth.cos(Dolphin.this.getXRot() * Mth.DEG_TO_RAD) + Mth.sin(dir) * pow2 ); -- Dolphin.this.level().addFreshEntity(itemEntity); -+ Dolphin.this.spawnAtLocation(getServerLevel(Dolphin.this), itemEntity); // Paper - Call EntityDropItemEvent +- Dolphin.this.level().addFreshEntity(thrownItem); ++ Dolphin.this.spawnAtLocation(getServerLevel(Dolphin.this), thrownItem); // Paper - Call EntityDropItemEvent } } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/AbstractChestedHorse.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/AbstractChestedHorse.java.patch index f0d60ff3e63d..5e1eb25224f2 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/AbstractChestedHorse.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/AbstractChestedHorse.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/equine/AbstractChestedHorse.java +++ b/net/minecraft/world/entity/animal/equine/AbstractChestedHorse.java -@@ -74,6 +_,12 @@ +@@ -75,6 +_,12 @@ super.dropEquipment(level); if (this.hasChest()) { this.spawnAtLocation(level, Blocks.CHEST); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/AbstractHorse.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/AbstractHorse.java.patch index 52883f8e6a3b..73571e79cb8a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/AbstractHorse.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/AbstractHorse.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/animal/equine/AbstractHorse.java +++ b/net/minecraft/world/entity/animal/equine/AbstractHorse.java -@@ -124,6 +_,7 @@ +@@ -125,6 +_,7 @@ protected boolean canGallop = true; protected int gallopSoundCounter; public @Nullable EntityReference owner; + public int maxDomestication = 100; // CraftBukkit - store max domestication value - protected AbstractHorse(EntityType type, Level level) { + protected AbstractHorse(final EntityType type, final Level level) { super(type, level); -@@ -252,7 +_,7 @@ +@@ -253,7 +_,7 @@ } @Override @@ -17,16 +17,16 @@ return !this.isVehicle(); } -@@ -299,7 +_,7 @@ +@@ -300,7 +_,7 @@ public void createInventory() { - SimpleContainer simpleContainer = this.inventory; + SimpleContainer old = this.inventory; - this.inventory = new SimpleContainer(this.getInventorySize()); + this.inventory = new SimpleContainer(this.getInventorySize(), (org.bukkit.entity.AbstractHorse) this.getBukkitEntity()); // CraftBukkit - if (simpleContainer != null) { - int min = Math.min(simpleContainer.getContainerSize(), this.inventory.getContainerSize()); + if (old != null) { + int max = Math.min(old.getContainerSize(), this.inventory.getContainerSize()); -@@ -391,7 +_,7 @@ +@@ -392,7 +_,7 @@ } public int getMaxTemper() { @@ -35,40 +35,40 @@ } @Override -@@ -454,7 +_,7 @@ - i1 = 5; +@@ -455,7 +_,7 @@ + temper = 5; if (!this.level().isClientSide() && this.isTamed() && this.getAge() == 0 && !this.isInLove()) { - flag = true; + itemUsed = true; - this.setInLove(player); -+ this.setInLove(player, stack.copy()); // Paper - Fix EntityBreedEvent copying ++ this.setInLove(player, itemStack.copy()); // Paper - Fix EntityBreedEvent copying } - } else if (stack.is(Items.GOLDEN_APPLE) || stack.is(Items.ENCHANTED_GOLDEN_APPLE)) { - f = 10.0F; -@@ -462,12 +_,12 @@ - i1 = 10; + } else if (itemStack.is(Items.GOLDEN_APPLE) || itemStack.is(Items.ENCHANTED_GOLDEN_APPLE)) { + heal = 10.0F; +@@ -463,12 +_,12 @@ + temper = 10; if (!this.level().isClientSide() && this.isTamed() && this.getAge() == 0 && !this.isInLove()) { - flag = true; + itemUsed = true; - this.setInLove(player); -+ this.setInLove(player, stack.copy()); // Paper - Fix EntityBreedEvent copying ++ this.setInLove(player, itemStack.copy()); // Paper - Fix EntityBreedEvent copying } } - if (this.getHealth() < this.getMaxHealth() && f > 0.0F) { -- this.heal(f); -+ this.heal(f, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // CraftBukkit - flag = true; + if (this.getHealth() < this.getMaxHealth() && heal > 0.0F) { +- this.heal(heal); ++ this.heal(heal, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // CraftBukkit + itemUsed = true; } -@@ -538,7 +_,7 @@ +@@ -539,7 +_,7 @@ super.aiStep(); - if (this.level() instanceof ServerLevel serverLevel && this.isAlive()) { + if (this.level() instanceof ServerLevel level && this.isAlive()) { if (this.random.nextInt(900) == 0 && this.deathTime == 0) { - this.heal(1.0F); + this.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit } if (this.canEatGrass()) { -@@ -640,6 +_,16 @@ +@@ -641,6 +_,16 @@ } } @@ -83,9 +83,9 @@ + // Paper end - Horse API + @Override - public InteractionResult mobInteract(Player player, InteractionHand hand) { + public InteractionResult mobInteract(final Player player, final InteractionHand hand) { if (this.isVehicle() || this.isBaby()) { -@@ -791,6 +_,7 @@ +@@ -792,6 +_,7 @@ output.putInt("Temper", this.getTemper()); output.putBoolean("Tame", this.isTamed()); EntityReference.store(this.owner, output, "Owner"); @@ -93,7 +93,7 @@ } @Override -@@ -801,6 +_,7 @@ +@@ -802,6 +_,7 @@ this.setTemper(input.getIntOr("Temper", 0)); this.setTamed(input.getBooleanOr("Tame", false)); this.owner = EntityReference.readWithOldOwnerConversion(input, "Owner", this.level()); @@ -101,16 +101,16 @@ } @Override -@@ -888,6 +_,17 @@ +@@ -895,6 +_,17 @@ @Override - public void handleStartJump(int jumpPower) { + public void handleStartJump(final int jumpScale) { + // CraftBukkit start + float power; -+ if (jumpPower >= 90) { ++ if (jumpScale >= 90) { + power = 1.0F; + } else { -+ power = 0.4F + 0.4F * (float) jumpPower / 90.0F; ++ power = 0.4F + 0.4F * (float) jumpScale / 90.0F; + } + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callHorseJumpEvent(this, power)) { + return; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/Llama.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/Llama.java.patch index 514774700575..5e819a923671 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/Llama.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/Llama.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/world/entity/animal/equine/Llama.java +++ b/net/minecraft/world/entity/animal/equine/Llama.java -@@ -75,18 +_,19 @@ +@@ -74,18 +_,19 @@ .scale(0.5F); - boolean didSpit; + private boolean didSpit; private @Nullable Llama caravanHead; - private @Nullable Llama caravanTail; + public @Nullable Llama caravanTail; // Paper - public - public Llama(EntityType type, Level level) { + public Llama(final EntityType type, final Level level) { super(type, level); this.getNavigation().setRequiredPathLength(40.0F); + this.maxDomestication = 30; // Paper - Missing entity API; configure max temper instead of a hardcoded value @@ -17,27 +17,27 @@ return false; } -- private void setStrength(int strength) { -+ public void setStrength(int strength) { // Paper - public +- private void setStrength(final int strength) { ++ public void setStrength(final int strength) { // Paper - public this.entityData.set(DATA_STRENGTH_ID, Math.max(1, Math.min(5, strength))); } -@@ -191,12 +_,12 @@ - f = 10.0F; +@@ -190,12 +_,12 @@ + heal = 10.0F; if (this.isTamed() && this.getAge() == 0 && this.canFallInLove()) { - flag = true; + itemUsed = true; - this.setInLove(player); -+ this.setInLove(player, stack.copy()); // Paper - Fix EntityBreedEvent copying ++ this.setInLove(player, itemStack.copy()); // Paper - Fix EntityBreedEvent copying } } - if (this.getHealth() < this.getMaxHealth() && f > 0.0F) { -- this.heal(f); -+ this.heal(f, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // Paper - Add missing regain reason - flag = true; + if (this.getHealth() < this.getMaxHealth() && heal > 0.0F) { +- this.heal(heal); ++ this.heal(heal, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // Paper - Add missing regain reason + itemUsed = true; } -@@ -308,7 +_,7 @@ +@@ -307,7 +_,7 @@ @Override public int getMaxTemper() { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/SkeletonHorse.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/SkeletonHorse.java.patch index 43ae42746bd0..528468435da6 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/SkeletonHorse.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/SkeletonHorse.java.patch @@ -3,7 +3,7 @@ @@ -126,7 +_,7 @@ public void aiStep() { super.aiStep(); - if (this.isTrap() && this.trapTime++ >= 18000) { + if (!this.isPersistenceRequired() && this.isTrap() && this.trapTime++ >= 18000) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/SkeletonTrapGoal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/SkeletonTrapGoal.java.patch index 0017eebc7c21..c908cb39e179 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/SkeletonTrapGoal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/SkeletonTrapGoal.java.patch @@ -6,7 +6,7 @@ private final SkeletonHorse horse; + private java.util.List eligiblePlayers; // Paper - public SkeletonTrapGoal(SkeletonHorse horse) { + public SkeletonTrapGoal(final SkeletonHorse horse) { this.horse = horse; @@ -25,12 +_,13 @@ @@ -18,31 +18,31 @@ @Override public void tick() { - ServerLevel serverLevel = (ServerLevel)this.horse.level(); + ServerLevel level = (ServerLevel)this.horse.level(); + if (!new com.destroystokyo.paper.event.entity.SkeletonHorseTrapEvent((org.bukkit.entity.SkeletonHorse) this.horse.getBukkitEntity(), this.eligiblePlayers).callEvent()) return; // Paper - DifficultyInstance currentDifficultyAt = serverLevel.getCurrentDifficultyAt(this.horse.blockPosition()); + DifficultyInstance difficulty = level.getCurrentDifficultyAt(this.horse.blockPosition()); this.horse.setTrap(false); this.horse.setTamed(true); @@ -39,11 +_,11 @@ - if (lightningBolt != null) { - lightningBolt.snapTo(this.horse.getX(), this.horse.getY(), this.horse.getZ()); - lightningBolt.setVisualOnly(true); -- serverLevel.addFreshEntity(lightningBolt); -+ serverLevel.strikeLightning(lightningBolt, org.bukkit.event.weather.LightningStrikeEvent.Cause.TRAP); // CraftBukkit - Skeleton skeleton = this.createSkeleton(currentDifficultyAt, this.horse); + if (bolt != null) { + bolt.snapTo(this.horse.getX(), this.horse.getY(), this.horse.getZ()); + bolt.setVisualOnly(true); +- level.addFreshEntity(bolt); ++ level.strikeLightning(bolt, org.bukkit.event.weather.LightningStrikeEvent.Cause.TRAP); // CraftBukkit + Skeleton skeleton = this.createSkeleton(difficulty, this.horse); if (skeleton != null) { skeleton.startRiding(this.horse); -- serverLevel.addFreshEntityWithPassengers(skeleton); -+ serverLevel.addFreshEntityWithPassengers(skeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.TRAP); // CraftBukkit +- level.addFreshEntityWithPassengers(skeleton); ++ level.addFreshEntityWithPassengers(skeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.TRAP); // CraftBukkit for (int i = 0; i < 3; i++) { - AbstractHorse abstractHorse = this.createHorse(currentDifficultyAt); + AbstractHorse otherHorse = this.createHorse(difficulty); @@ -52,7 +_,7 @@ - if (skeleton1 != null) { - skeleton1.startRiding(abstractHorse); - abstractHorse.push(this.horse.getRandom().triangle(0.0, 1.1485), 0.0, this.horse.getRandom().triangle(0.0, 1.1485)); -- serverLevel.addFreshEntityWithPassengers(abstractHorse); -+ serverLevel.addFreshEntityWithPassengers(abstractHorse, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.JOCKEY); // CraftBukkit + if (otherSkeleton != null) { + otherSkeleton.startRiding(otherHorse); + otherHorse.push(this.horse.getRandom().triangle(0.0, 1.1485), 0.0, this.horse.getRandom().triangle(0.0, 1.1485)); +- level.addFreshEntityWithPassengers(otherHorse); ++ level.addFreshEntityWithPassengers(otherHorse, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.JOCKEY); // CraftBukkit } } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/TraderLlama.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/TraderLlama.java.patch index 6fe1f4c67b78..911be680c2b7 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/TraderLlama.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/TraderLlama.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/equine/TraderLlama.java +++ b/net/minecraft/world/entity/animal/equine/TraderLlama.java -@@ -88,7 +_,7 @@ +@@ -92,7 +_,7 @@ this.despawnDelay = this.isLeashedToWanderingTrader() ? ((WanderingTrader)this.getLeashHolder()).getDespawnDelay() - 1 : this.despawnDelay - 1; if (this.despawnDelay <= 0) { this.removeLeash(); @@ -9,7 +9,7 @@ } } } -@@ -146,7 +_,7 @@ +@@ -154,7 +_,7 @@ @Override public void start() { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/feline/Cat.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/feline/Cat.java.patch index 4f297d72d1fb..b759422d79b9 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/feline/Cat.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/feline/Cat.java.patch @@ -1,49 +1,40 @@ --- a/net/minecraft/world/entity/animal/feline/Cat.java +++ b/net/minecraft/world/entity/animal/feline/Cat.java -@@ -372,6 +_,11 @@ - if (item instanceof DyeItem dyeItem) { - DyeColor dyeColor = dyeItem.getDyeColor(); - if (dyeColor != this.getCollarColor()) { +@@ -400,6 +_,11 @@ + if (itemStack.is(ItemTags.CAT_COLLAR_DYES)) { + DyeColor color = itemStack.get(DataComponents.DYE); + if (color != null && color != this.getCollarColor()) { + // Paper start - Add EntityDyeEvent and CollarColorable interface -+ final io.papermc.paper.event.entity.EntityDyeEvent event = new io.papermc.paper.event.entity.EntityDyeEvent(this.getBukkitEntity(), org.bukkit.DyeColor.getByWoolData((byte) dyeColor.getId()), ((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity()); ++ final io.papermc.paper.event.entity.EntityDyeEvent event = new io.papermc.paper.event.entity.EntityDyeEvent(this.getBukkitEntity(), org.bukkit.DyeColor.getByWoolData((byte) color.getId()), ((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity()); + if (!event.callEvent()) return InteractionResult.FAIL; -+ dyeColor = DyeColor.byId(event.getColor().getWoolData()); ++ color = DyeColor.byId(event.getColor().getWoolData()); + // Paper end - Add EntityDyeEvent and CollarColorable interface if (!this.level().isClientSide()) { - this.setCollarColor(dyeColor); - itemInHand.consume(1, player); -@@ -384,7 +_,7 @@ - if (!this.level().isClientSide()) { - this.usePlayerItem(player, hand, itemInHand); - FoodProperties foodProperties = itemInHand.get(DataComponents.FOOD); -- this.heal(foodProperties != null ? foodProperties.nutrition() : 1.0F); -+ this.heal(foodProperties != null ? foodProperties.nutrition() : 1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // Paper - Add missing regain reason - this.playEatingSound(); - } - -@@ -446,7 +_,7 @@ + this.setCollarColor(color); + itemStack.consume(1, player); +@@ -471,7 +_,7 @@ } - private void tryToTame(Player player) { + private void tryToTame(final Player player) { - if (this.random.nextInt(3) == 0) { + if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit this.tame(player); this.setOrderedToSit(true); this.level().broadcastEntityEvent(this, EntityEvent.TAMING_SUCCEEDED); -@@ -579,15 +_,20 @@ +@@ -601,15 +_,20 @@ .dropFromGiftLootTable( getServerLevel(this.cat), BuiltInLootTables.CAT_MORNING_GIFT, -- (level, stack) -> level.addFreshEntity( +- (level, itemStack) -> level.addFreshEntity( - new ItemEntity( + // CraftBukkit start -+ (level, stack) -> { ++ (level, itemStack) -> { + final ItemEntity item = new ItemEntity( level, - (double)mutableBlockPos.getX() - Mth.sin(this.cat.yBodyRot * (float) (Math.PI / 180.0)), - mutableBlockPos.getY(), - (double)mutableBlockPos.getZ() + Mth.cos(this.cat.yBodyRot * (float) (Math.PI / 180.0)), - stack + (double)catPos.getX() - Mth.sin(this.cat.yBodyRot * Mth.DEG_TO_RAD), + catPos.getY(), + (double)catPos.getZ() + Mth.cos(this.cat.yBodyRot * Mth.DEG_TO_RAD), + itemStack - ) - ) + ); @@ -55,12 +46,12 @@ ); } -@@ -613,7 +_,7 @@ +@@ -635,7 +_,7 @@ } - static class CatTemptGoal extends TemptGoal { + private static class CatTemptGoal extends TemptGoal { - private @Nullable Player selectedPlayer; + private @Nullable LivingEntity selectedPlayer; // CraftBukkit private final Cat cat; - public CatTemptGoal(Cat cat, double speedModifier, Predicate items, boolean canScare) { + public CatTemptGoal(final Cat mob, final double speedModifier, final Predicate items, final boolean canScare) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/feline/Ocelot.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/feline/Ocelot.java.patch index 0a20cf90d180..1b4bd7317427 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/feline/Ocelot.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/feline/Ocelot.java.patch @@ -3,15 +3,15 @@ @@ -129,7 +_,7 @@ @Override - public boolean removeWhenFarAway(double distanceToClosestPlayer) { + public boolean removeWhenFarAway(final double distSqr) { - return !this.isTrusting() && this.tickCount > 2400; + return !this.isTrusting() && this.tickCount > 2400 && !this.hasCustomName() && !this.isLeashed(); // Paper - honor name and leash } public static AttributeSupplier.Builder createAttributes() { @@ -162,7 +_,7 @@ - if ((this.temptGoal == null || this.temptGoal.isRunning()) && !this.isTrusting() && this.isFood(itemInHand) && player.distanceToSqr(this) < 9.0) { - this.usePlayerItem(player, hand, itemInHand); + if ((this.temptGoal == null || this.temptGoal.isRunning()) && !this.isTrusting() && this.isFood(itemStack) && player.distanceToSqr(this) < 9.0) { + this.usePlayerItem(player, hand, itemStack); if (!this.level().isClientSide()) { - if (this.random.nextInt(3) == 0) { + if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit - added event call and isCancelled check diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/fish/Pufferfish.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/fish/Pufferfish.java.patch index db4ed72b0b8f..2bb54fec69ac 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/fish/Pufferfish.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/fish/Pufferfish.java.patch @@ -4,41 +4,41 @@ public void tick() { if (!this.level().isClientSide() && this.isAlive() && this.isEffectiveAi()) { if (this.inflateCounter > 0) { -+ boolean increase = true; // Paper - Add PufferFishStateChangeEvent ++ boolean inflate = true; // Paper - Add PufferFishStateChangeEvent if (this.getPuffState() == 0) { + if (new io.papermc.paper.event.entity.PufferFishStateChangeEvent((org.bukkit.entity.PufferFish) getBukkitEntity(), STATE_MID).callEvent()) { // Paper - Add PufferFishStateChangeEvent this.makeSound(SoundEvents.PUFFER_FISH_BLOW_UP); this.setPuffState(STATE_MID); -+ } else { increase = false; } // Paper - Add PufferFishStateChangeEvent - } else if (this.inflateCounter > 40 && this.getPuffState() == 1) { ++ } else { inflate = false; } // Paper - Add PufferFishStateChangeEvent + } else if (this.inflateCounter > 40 && this.getPuffState() == STATE_MID) { + if (new io.papermc.paper.event.entity.PufferFishStateChangeEvent((org.bukkit.entity.PufferFish) getBukkitEntity(), STATE_FULL).callEvent()) { // Paper - Add PufferFishStateChangeEvent this.makeSound(SoundEvents.PUFFER_FISH_BLOW_UP); this.setPuffState(STATE_FULL); -+ } else { increase = false; } // Paper - Add PufferFishStateChangeEvent ++ } else { inflate = false; } // Paper - Add PufferFishStateChangeEvent } -+ if (increase) // Paper - Add PufferFishStateChangeEvent ++ if (inflate) // Paper - Add PufferFishStateChangeEvent this.inflateCounter++; } else if (this.getPuffState() != 0) { -+ boolean increase = true; // Paper - Add PufferFishStateChangeEvent - if (this.deflateTimer > 60 && this.getPuffState() == 2) { ++ boolean deflate = true; // Paper - Add PufferFishStateChangeEvent + if (this.deflateTimer > 60 && this.getPuffState() == STATE_FULL) { + if (new io.papermc.paper.event.entity.PufferFishStateChangeEvent((org.bukkit.entity.PufferFish) getBukkitEntity(), STATE_MID).callEvent()) { // Paper - Add PufferFishStateChangeEvent this.makeSound(SoundEvents.PUFFER_FISH_BLOW_OUT); this.setPuffState(STATE_MID); -+ } else { increase = false; } // Paper - Add PufferFishStateChangeEvent - } else if (this.deflateTimer > 100 && this.getPuffState() == 1) { ++ } else { deflate = false; } // Paper - Add PufferFishStateChangeEvent + } else if (this.deflateTimer > 100 && this.getPuffState() == STATE_MID) { + if (new io.papermc.paper.event.entity.PufferFishStateChangeEvent((org.bukkit.entity.PufferFish) getBukkitEntity(), STATE_SMALL).callEvent()) { // Paper - Add PufferFishStateChangeEvent this.makeSound(SoundEvents.PUFFER_FISH_BLOW_OUT); this.setPuffState(STATE_SMALL); -+ } else { increase = false; } // Paper - Add PufferFishStateChangeEvent ++ } else { deflate = false; } // Paper - Add PufferFishStateChangeEvent } -+ if (increase) // Paper - Add PufferFishStateChangeEvent ++ if (deflate) // Paper - Add PufferFishStateChangeEvent this.deflateTimer++; } } @@ -138,7 +_,7 @@ - private void touch(ServerLevel level, Mob mob) { + private void touch(final ServerLevel level, final Mob mob) { int puffState = this.getPuffState(); if (mob.hurtServer(level, this.damageSources().mobAttack(this), 1 + puffState)) { - mob.addEffect(new MobEffectInstance(MobEffects.POISON, 60 * puffState, 0), this); @@ -50,8 +50,8 @@ serverPlayer.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.PUFFER_FISH_STING, 0.0F)); } -- entity.addEffect(new MobEffectInstance(MobEffects.POISON, 60 * puffState, 0), this); -+ entity.addEffect(new MobEffectInstance(MobEffects.POISON, 60 * puffState, 0), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit +- player.addEffect(new MobEffectInstance(MobEffects.POISON, 60 * puffState, 0), this); ++ player.addEffect(new MobEffectInstance(MobEffects.POISON, 60 * puffState, 0), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/fish/WaterAnimal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/fish/WaterAnimal.java.patch index a55197acb9e7..e4f46df3d56f 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/fish/WaterAnimal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/fish/WaterAnimal.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/world/entity/animal/fish/WaterAnimal.java +++ b/net/minecraft/world/entity/animal/fish/WaterAnimal.java -@@ -72,6 +_,10 @@ +@@ -76,6 +_,10 @@ ) { int seaLevel = level.getSeaLevel(); - int i = seaLevel - 13; + int minSpawnLevel = seaLevel - 13; + // Paper start - Make water animal spawn height configurable + seaLevel = level.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.maximum.or(seaLevel); -+ i = level.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.minimum.or(i); ++ minSpawnLevel = level.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.minimum.or(minSpawnLevel); + // Paper end - Make water animal spawn height configurable - return pos.getY() >= i + return pos.getY() >= minSpawnLevel && pos.getY() <= seaLevel && level.getFluidState(pos.below()).is(FluidTags.WATER) diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/fox/Fox.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/fox/Fox.java.patch index e9b25ee2248e..19a985077464 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/fox/Fox.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/fox/Fox.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/fox/Fox.java +++ b/net/minecraft/world/entity/animal/fox/Fox.java -@@ -440,7 +_,7 @@ +@@ -447,7 +_,7 @@ input.read("Trusted", TRUSTED_LIST_CODEC).orElse(List.of()).forEach(this::addTrustedEntity); this.setSleeping(input.getBooleanOr("Sleeping", false)); this.setVariant(input.read("Type", Fox.Variant.CODEC).orElse(Fox.Variant.DEFAULT)); @@ -9,58 +9,58 @@ this.setIsCrouching(input.getBooleanOr("Crouching", false)); if (this.level() instanceof ServerLevel) { this.setTargetGoals(); -@@ -457,6 +_,12 @@ +@@ -464,6 +_,12 @@ } - public void setSitting(boolean sitting) { + public void setSitting(final boolean value) { + // Paper start - Add EntityToggleSitEvent -+ this.setSitting(sitting, true); ++ this.setSitting(value, true); + } -+ public void setSitting(boolean sitting, boolean fireEvent) { -+ if (fireEvent && !new io.papermc.paper.event.entity.EntityToggleSitEvent(this.getBukkitEntity(), sitting).callEvent()) return; ++ public void setSitting(final boolean value, final boolean fireEvent) { ++ if (fireEvent && !new io.papermc.paper.event.entity.EntityToggleSitEvent(this.getBukkitEntity(), value).callEvent()) return; + // Paper end - Add EntityToggleSitEvent - this.setFlag(FLAG_SITTING, sitting); + this.setFlag(FLAG_SITTING, value); } -@@ -516,19 +_,20 @@ - itemEntity.setPickUpDelay(40); - itemEntity.setThrower(this); +@@ -523,19 +_,20 @@ + thrownItem.setPickUpDelay(40); + thrownItem.setThrower(this); this.playSound(SoundEvents.FOX_SPIT, 1.0F, 1.0F); -- this.level().addFreshEntity(itemEntity); -+ this.spawnAtLocation((net.minecraft.server.level.ServerLevel) this.level(), itemEntity); // Paper - Call EntityDropItemEvent +- this.level().addFreshEntity(thrownItem); ++ this.spawnAtLocation((net.minecraft.server.level.ServerLevel) this.level(), thrownItem); // Paper - Call EntityDropItemEvent } } - private void dropItemStack(ItemStack stack) { - ItemEntity itemEntity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), stack); + private void dropItemStack(final ItemStack itemStack) { + ItemEntity itemEntity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemStack); - this.level().addFreshEntity(itemEntity); + this.spawnAtLocation((net.minecraft.server.level.ServerLevel) this.level(), itemEntity); // Paper - Call EntityDropItemEvent } @Override - protected void pickUpItem(ServerLevel level, ItemEntity entity) { - ItemStack item = entity.getItem(); -- if (this.canHoldItem(item)) { -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(this, entity, item.getCount() - 1, !this.canHoldItem(item)).isCancelled()) { // CraftBukkit - call EntityPickupItemEvent -+ item = entity.getItem(); // CraftBukkit - update item after event - int count = item.getCount(); + protected void pickUpItem(final ServerLevel level, final ItemEntity entity) { + ItemStack itemStack = entity.getItem(); +- if (this.canHoldItem(itemStack)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(this, entity, itemStack.getCount() - 1, !this.canHoldItem(itemStack)).isCancelled()) { // CraftBukkit - call EntityPickupItemEvent ++ itemStack = entity.getItem(); // CraftBukkit - update item after event + int count = itemStack.getCount(); if (count > 1) { - this.dropItemStack(item.split(count - 1)); -@@ -539,7 +_,7 @@ - this.setItemSlot(EquipmentSlot.MAINHAND, item.split(1)); + this.dropItemStack(itemStack.split(count - 1)); +@@ -546,7 +_,7 @@ + this.setItemSlot(EquipmentSlot.MAINHAND, itemStack.split(1)); this.setGuaranteedDrop(EquipmentSlot.MAINHAND); - this.take(entity, item.getCount()); + this.take(entity, itemStack.getCount()); - entity.discard(); + entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause this.ticksSinceEaten = 0; } } -@@ -630,12 +_,12 @@ +@@ -637,12 +_,12 @@ } @Override -- public void setTarget(@Nullable LivingEntity target) { -+ public boolean setTarget(@Nullable LivingEntity target, org.bukkit.event.entity.EntityTargetEvent.@Nullable TargetReason reason) { // CraftBukkit +- public void setTarget(final @Nullable LivingEntity target) { ++ public boolean setTarget(final @Nullable LivingEntity target, org.bukkit.event.entity.EntityTargetEvent.@Nullable TargetReason reason) { // CraftBukkit if (this.isDefending() && target == null) { this.setDefending(false); } @@ -69,13 +69,13 @@ + return super.setTarget(target, reason); // CraftBukkit } - void wakeUp() { -@@ -696,15 +_,33 @@ - return this.getTrustedEntities().anyMatch(entityReference -> entityReference.matches(entity)); + private void wakeUp() { +@@ -703,15 +_,33 @@ + return this.getTrustedEntities().anyMatch(trusted -> trusted.matches(entity)); } - @Override -- protected void dropAllDeathLoot(ServerLevel level, DamageSource damageSource) { +- protected void dropAllDeathLoot(final ServerLevel level, final DamageSource source) { + // Paper start - handle the bitten item separately like vanilla + @Override + protected boolean shouldSkipLoot(EquipmentSlot slot) { @@ -85,16 +85,16 @@ + + @Override + // Paper start - Cancellable death event -+ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(ServerLevel level, DamageSource damageSource) { - ItemStack itemBySlot = this.getItemBySlot(EquipmentSlot.MAINHAND); -- if (!itemBySlot.isEmpty()) { ++ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(final ServerLevel level, final DamageSource source) { + ItemStack itemStack = this.getItemBySlot(EquipmentSlot.MAINHAND); +- if (!itemStack.isEmpty()) { + boolean releaseMouth = false; -+ if (!itemBySlot.isEmpty() && level.getGameRules().get(GameRules.MOB_DROPS)) { // Fix MC-153010 - this.spawnAtLocation(level, itemBySlot); ++ if (!itemStack.isEmpty() && level.getGameRules().get(GameRules.MOB_DROPS)) { // Fix MC-153010 + this.spawnAtLocation(level, itemStack); + releaseMouth = true; + } + -+ org.bukkit.event.entity.EntityDeathEvent deathEvent = super.dropAllDeathLoot(level, damageSource); ++ org.bukkit.event.entity.EntityDeathEvent deathEvent = super.dropAllDeathLoot(level, source); + // Below is code to drop + if (deathEvent == null || deathEvent.isCancelled()) return deathEvent; + @@ -103,20 +103,20 @@ this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY); } -- super.dropAllDeathLoot(level, damageSource); +- super.dropAllDeathLoot(level, source); + return deathEvent; // Paper - Cancellable death event } - public static boolean isPathClear(Fox fox, LivingEntity livingEntity) { -@@ -877,6 +_,19 @@ - fox.addTrustedEntity(loveCause1); + public static boolean isPathClear(final Fox fox, final LivingEntity target) { +@@ -896,6 +_,19 @@ + offspring.addTrustedEntity(partnerLoveCause); } + // CraftBukkit start - call EntityBreedEvent -+ fox.setAge(-24000); -+ fox.snapTo(this.animal.getX(), this.animal.getY(), this.animal.getZ(), 0.0F, 0.0F); ++ offspring.setAge(-24000); ++ offspring.snapTo(this.animal.getX(), this.animal.getY(), this.animal.getZ(), 0.0F, 0.0F); + int experience = this.animal.getRandom().nextInt(7) + 1; -+ org.bukkit.event.entity.EntityBreedEvent entityBreedEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(fox, this.animal, this.partner, loveCause, this.animal.breedItem, experience); ++ org.bukkit.event.entity.EntityBreedEvent entityBreedEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(offspring, this.animal, this.partner, loveCause, this.animal.breedItem, experience); + if (entityBreedEvent.isCancelled()) { + this.animal.resetLove(); + this.partner.resetLove(); @@ -125,32 +125,32 @@ + experience = entityBreedEvent.getExperience(); + // CraftBukkit end - call EntityBreedEvent + - if (serverPlayer != null) { - serverPlayer.awardStat(Stats.ANIMALS_BRED); - CriteriaTriggers.BRED_ANIMALS.trigger(serverPlayer, this.animal, this.partner, fox); -@@ -886,14 +_,12 @@ + if (loveCause != null) { + loveCause.awardStat(Stats.ANIMALS_BRED); + CriteriaTriggers.BRED_ANIMALS.trigger(loveCause, this.animal, this.partner, offspring); +@@ -905,14 +_,12 @@ this.partner.setAge(6000); this.animal.resetLove(); this.partner.resetLove(); -- fox.setAge(-24000); -- fox.snapTo(this.animal.getX(), this.animal.getY(), this.animal.getZ(), 0.0F, 0.0F); -- this.level.addFreshEntityWithPassengers(fox); -+ this.level.addFreshEntityWithPassengers(fox, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason +- offspring.setAge(-24000); +- offspring.snapTo(this.animal.getX(), this.animal.getY(), this.animal.getZ(), 0.0F, 0.0F); +- this.level.addFreshEntityWithPassengers(offspring); ++ this.level.addFreshEntityWithPassengers(offspring, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason this.level.broadcastEntityEvent(this.animal, EntityEvent.IN_LOVE_HEARTS); - if (this.level.getGameRules().get(GameRules.MOB_DROPS)) { + if (experience > 0 && this.level.getGameRules().get(GameRules.MOB_DROPS)) { // Paper - call EntityBreedEvent this.level .addFreshEntity( - new ExperienceOrb(this.level, this.animal.getX(), this.animal.getY(), this.animal.getZ(), this.animal.getRandom().nextInt(7) + 1) -+ new ExperienceOrb(this.level, this.animal.position(), net.minecraft.world.phys.Vec3.ZERO, experience, org.bukkit.entity.ExperienceOrb.SpawnReason.BREED, loveCause, fox) // Paper - call EntityBreedEvent, add spawn context ++ new ExperienceOrb(this.level, this.animal.position(), net.minecraft.world.phys.Vec3.ZERO, experience, org.bukkit.entity.ExperienceOrb.SpawnReason.BREED, loveCause, offspring) // Paper - call EntityBreedEvent, add spawn context ); } } -@@ -957,6 +_,7 @@ - private void pickSweetBerries(BlockState state) { - int ageValue = state.getValue(SweetBerryBushBlock.AGE); +@@ -977,6 +_,7 @@ + private void pickSweetBerries(final BlockState state) { + int age = state.getValue(SweetBerryBushBlock.AGE); state.setValue(SweetBerryBushBlock.AGE, 1); + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(Fox.this, this.blockPos, state.setValue(SweetBerryBushBlock.AGE, 1))) return; // CraftBukkit - call EntityChangeBlockEvent - int i = 1 + Fox.this.level().random.nextInt(2) + (ageValue == 3 ? 1 : 0); - ItemStack itemBySlot = Fox.this.getItemBySlot(EquipmentSlot.MAINHAND); - if (itemBySlot.isEmpty()) { + int count = 1 + Fox.this.level().getRandom().nextInt(2) + (age == 3 ? 1 : 0); + ItemStack heldItem = Fox.this.getItemBySlot(EquipmentSlot.MAINHAND); + if (heldItem.isEmpty()) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/Frog.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/Frog.java.patch index ae9192ae64c2..432dea63b573 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/Frog.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/Frog.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/entity/animal/frog/Frog.java +++ b/net/minecraft/world/entity/animal/frog/Frog.java -@@ -285,7 +_,12 @@ +@@ -257,7 +_,12 @@ @Override - public void spawnChildFromBreeding(ServerLevel level, Animal partner) { + public void spawnChildFromBreeding(final ServerLevel level, final Animal partner) { - this.finalizeSpawnChildFromBreeding(level, partner, null); + // Paper start - Add EntityFertilizeEggEvent event + final io.papermc.paper.event.entity.EntityFertilizeEggEvent result = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityFertilizeEggEvent(this, partner); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/ShootTongue.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/ShootTongue.java.patch index 3b1480d7f079..6ea45dab5671 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/ShootTongue.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/ShootTongue.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/animal/frog/ShootTongue.java +++ b/net/minecraft/world/entity/animal/frog/ShootTongue.java @@ -94,7 +_,7 @@ - if (entity.isAlive()) { - frog.doHurtTarget(level, entity); - if (!entity.isAlive()) { -- entity.remove(Entity.RemovalReason.KILLED); -+ entity.remove(Entity.RemovalReason.KILLED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause + if (target.isAlive()) { + body.doHurtTarget(level, target); + if (!target.isAlive()) { +- target.remove(Entity.RemovalReason.KILLED); ++ target.remove(Entity.RemovalReason.KILLED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause } } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/Tadpole.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/Tadpole.java.patch index 2d3ea622dc8d..b7a18eef6cea 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/Tadpole.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/frog/Tadpole.java.patch @@ -1,75 +1,23 @@ --- a/net/minecraft/world/entity/animal/frog/Tadpole.java +++ b/net/minecraft/world/entity/animal/frog/Tadpole.java -@@ -64,6 +_,7 @@ - MemoryModuleType.BREED_TARGET, - MemoryModuleType.IS_PANICKING - ); -+ public boolean ageLocked; // Paper - - public Tadpole(EntityType type, Level level) { - super(type, level); -@@ -115,7 +_,7 @@ - @Override - public void aiStep() { - super.aiStep(); -- if (!this.level().isClientSide()) { -+ if (!this.level().isClientSide() && !this.ageLocked) { // Paper - this.setAge(this.age + 1); - } - } -@@ -124,12 +_,14 @@ - protected void addAdditionalSaveData(ValueOutput output) { - super.addAdditionalSaveData(output); - output.putInt("Age", this.age); -+ output.putBoolean("AgeLocked", this.ageLocked); // Paper - } - - @Override - protected void readAdditionalSaveData(ValueInput input) { - super.readAdditionalSaveData(input); - this.setAge(input.getIntOr("Age", 0)); -+ this.ageLocked = input.getBooleanOr("AgeLocked", false); // Paper - } - - @Override -@@ -170,13 +_,19 @@ - @Override - public void saveToBucketTag(ItemStack stack) { - Bucketable.saveDefaultDataToBucketTag(this, stack); -- CustomData.update(DataComponents.BUCKET_ENTITY_DATA, stack, compoundTag -> compoundTag.putInt("Age", this.getAge())); -+ // Paper start - Save tadpole age -+ CustomData.update(DataComponents.BUCKET_ENTITY_DATA, stack, compoundTag -> { -+ compoundTag.putInt("Age", this.getAge()); -+ compoundTag.putBoolean("AgeLocked", this.ageLocked); -+ }); -+ // Paper end - Save tadpole age - } - - @Override - public void loadFromBucketTag(CompoundTag tag) { - Bucketable.loadDefaultDataFromBucketTag(this, tag); - tag.getInt("Age").ifPresent(this::setAge); -+ this.ageLocked = tag.getBooleanOr("AgeLocked", false); // Paper - } - - @Override -@@ -208,6 +_,7 @@ +@@ -127,7 +_,7 @@ + entityData.define(AGE_LOCKED, false); } - private void ageUp(int offset) { -+ if (this.ageLocked) return; // Paper - this.setAge(this.age + offset * 20); +- protected void setAgeLocked(final boolean locked) { ++ public void setAgeLocked(final boolean locked) { // Paper - public + this.entityData.set(AGE_LOCKED, locked); } -@@ -220,12 +_,17 @@ +@@ -236,12 +_,17 @@ private void ageUp() { if (this.level() instanceof ServerLevel serverLevel) { -- this.convertTo(EntityType.FROG, ConversionParams.single(this, false, false), mob -> { -+ Frog converted = this.convertTo(EntityType.FROG, ConversionParams.single(this, false, false), mob -> { // CraftBukkit - mob.finalizeSpawn(serverLevel, serverLevel.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.CONVERSION, null); - mob.setPersistenceRequired(); - mob.fudgePositionAfterSizeChange(this.getDimensions(this.getPose())); +- this.convertTo(EntityType.FROG, ConversionParams.single(this, false, false), frog -> { ++ Frog converted = this.convertTo(EntityType.FROG, ConversionParams.single(this, false, false), frog -> { // CraftBukkit + frog.finalizeSpawn(serverLevel, serverLevel.getCurrentDifficultyAt(frog.blockPosition()), EntitySpawnReason.CONVERSION, null); + frog.setPersistenceRequired(); + frog.fudgePositionAfterSizeChange(this.getDimensions(this.getPose())); this.playSound(SoundEvents.TADPOLE_GROW_UP, 0.15F, 1.0F); - }); + // CraftBukkit start diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/goat/Goat.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/goat/Goat.java.patch index a05540f3cdf0..6d7aa8763011 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/goat/Goat.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/goat/Goat.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/animal/goat/Goat.java +++ b/net/minecraft/world/entity/animal/goat/Goat.java -@@ -234,13 +_,22 @@ - public InteractionResult mobInteract(Player player, InteractionHand hand) { - ItemStack itemInHand = player.getItemInHand(hand); - if (itemInHand.is(Items.BUCKET) && !this.isBaby()) { +@@ -213,13 +_,22 @@ + public InteractionResult mobInteract(final Player player, final InteractionHand hand) { + ItemStack heldItem = player.getItemInHand(hand); + if (heldItem.is(Items.BUCKET) && !this.isBaby()) { + // CraftBukkit start - Got milk? -+ org.bukkit.event.player.PlayerBucketFillEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(player.level(), player, this.blockPosition(), this.blockPosition(), null, itemInHand, Items.MILK_BUCKET, hand); ++ org.bukkit.event.player.PlayerBucketFillEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(player.level(), player, this.blockPosition(), this.blockPosition(), null, heldItem, Items.MILK_BUCKET, hand); + + if (event.isCancelled()) { + player.containerMenu.sendAllDataToRemote(); // Paper - Fix inventory desync @@ -13,29 +13,29 @@ + } + // CraftBukkit end player.playSound(this.getMilkingSound(), 1.0F, 1.0F); -- ItemStack itemStack = ItemUtils.createFilledResult(itemInHand, player, Items.MILK_BUCKET.getDefaultInstance()); -+ ItemStack itemStack = ItemUtils.createFilledResult(itemInHand, player, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack())); // CraftBukkit - player.setItemInHand(hand, itemStack); +- ItemStack bucketOrMilkBucket = ItemUtils.createFilledResult(heldItem, player, Items.MILK_BUCKET.getDefaultInstance()); ++ ItemStack bucketOrMilkBucket = ItemUtils.createFilledResult(heldItem, player, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack())); // CraftBukkit + player.setItemInHand(hand, bucketOrMilkBucket); return InteractionResult.SUCCESS; } else { -+ boolean isFood = this.isFood(itemInHand); // Paper - track before stack is possibly decreased to 0 (Fixes MC-244739) ++ boolean isFood = this.isFood(heldItem); // Paper - track before stack is possibly decreased to 0 (Fixes MC-244739) InteractionResult interactionResult = super.mobInteract(player, hand); -- if (interactionResult.consumesAction() && this.isFood(itemInHand)) { +- if (interactionResult.consumesAction() && this.isFood(heldItem)) { + if (interactionResult.consumesAction() && isFood) { // Paper this.playEatingSound(); } -@@ -346,8 +_,7 @@ - double d1 = Mth.randomBetween(this.random, 0.3F, 0.7F); - double d2 = Mth.randomBetween(this.random, -0.2F, 0.2F); - ItemEntity itemEntity = new ItemEntity(this.level(), vec3.x(), vec3.y(), vec3.z(), itemStack, d, d1, d2); -- this.level().addFreshEntity(itemEntity); -- return true; -+ return this.spawnAtLocation((net.minecraft.server.level.ServerLevel) this.level(), itemEntity) != null; // Paper - Call EntityDropItemEvent +@@ -328,8 +_,7 @@ + double deltaY = Mth.randomBetween(this.random, 0.3F, 0.7F); + double deltaZ = Mth.randomBetween(this.random, -0.2F, 0.2F); + ItemEntity itemEntity = new ItemEntity(this.level(), bodyPosition.x(), bodyPosition.y(), bodyPosition.z(), item, deltaX, deltaY, deltaZ); +- this.level().addFreshEntity(itemEntity); +- return true; ++ return this.spawnAtLocation((net.minecraft.server.level.ServerLevel) this.level(), itemEntity) != null; // Paper - Call EntityDropItemEvent + } } } - -@@ -378,4 +_,15 @@ +@@ -352,4 +_,15 @@ ) { return level.getBlockState(pos.below()).is(BlockTags.GOATS_SPAWNABLE_ON) && isBrightEnoughToSpawn(level, pos); } @@ -43,10 +43,10 @@ + // Paper start - Goat ram API + public void ram(net.minecraft.world.entity.LivingEntity entity) { + Brain brain = this.getBrain(); -+ brain.setMemory(MemoryModuleType.RAM_TARGET, entity.position()); -+ brain.eraseMemory(MemoryModuleType.RAM_COOLDOWN_TICKS); -+ brain.eraseMemory(MemoryModuleType.BREED_TARGET); -+ brain.eraseMemory(MemoryModuleType.TEMPTING_PLAYER); ++ brain.setMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.RAM_TARGET, entity.position()); ++ brain.eraseMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.RAM_COOLDOWN_TICKS); ++ brain.eraseMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.BREED_TARGET); ++ brain.eraseMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.TEMPTING_PLAYER); + brain.setActiveActivityIfPossible(net.minecraft.world.entity.schedule.Activity.RAM); + } + // Paper end - Goat ram API diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/CopperGolem.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/CopperGolem.java.patch index 3257d8bb7bbd..e5842b62dbdd 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/CopperGolem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/CopperGolem.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/golem/CopperGolem.java +++ b/net/minecraft/world/entity/animal/golem/CopperGolem.java -@@ -57,8 +_,8 @@ +@@ -58,8 +_,8 @@ import org.jspecify.annotations.Nullable; public class CopperGolem extends AbstractGolem implements ContainerUser, Shearable { @@ -11,7 +11,7 @@ private static final int WEATHERING_TICK_FROM = 504000; private static final int WEATHERING_TICK_TO = 552000; private static final int SPIN_ANIMATION_MIN_COOLDOWN = 200; -@@ -75,7 +_,7 @@ +@@ -79,7 +_,7 @@ ); private @Nullable BlockPos openedChestPos; private @Nullable UUID lastLightningBoltUUID; @@ -20,32 +20,32 @@ private int idleAnimationStartTick = 0; private final AnimationState idleAnimationState = new AnimationState(); private final AnimationState interactionGetItemAnimationState = new AnimationState(); -@@ -219,7 +_,15 @@ +@@ -218,7 +_,15 @@ Level level = this.level(); - if (itemInHand.is(Items.SHEARS) && this.readyForShearing()) { + if (itemStack.is(Items.SHEARS) && this.readyForShearing()) { if (level instanceof ServerLevel serverLevel) { -- this.shear(serverLevel, SoundSource.PLAYERS, itemInHand); +- this.shear(serverLevel, SoundSource.PLAYERS, itemStack); + // Paper start -+ java.util.List drops = this.generateDefaultDrops(serverLevel, itemInHand); -+ org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemInHand, hand, drops); ++ java.util.List drops = this.generateDefaultDrops(serverLevel, itemStack); ++ org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemStack, hand, drops); + if (event != null) { + if (event.isCancelled()) return InteractionResult.PASS; + drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops()); + } -+ this.shear(serverLevel, SoundSource.PLAYERS, itemInHand, drops); ++ this.shear(serverLevel, SoundSource.PLAYERS, itemStack, drops); + // Paper end this.gameEvent(GameEvent.SHEAR, player); - itemInHand.hurtAndBreak(1, player, hand); + itemStack.hurtAndBreak(1, player, hand); } -@@ -282,20 +_,27 @@ +@@ -281,20 +_,27 @@ - private void turnToStatue(ServerLevel level) { - BlockPos blockPos = this.blockPosition(); + private void turnToStatue(final ServerLevel level) { + BlockPos pos = this.blockPosition(); - level.setBlock( -- blockPos, +- pos, + // Paper start - call EntityChangeBlockEvent + //level.setBlock( -+ // blockPos, ++ // pos, + BlockState newState = Blocks.OXIDIZED_COPPER_GOLEM_STATUE .defaultBlockState() @@ -58,12 +58,12 @@ + .setValue(CopperGolemStatueBlock.FACING, Direction.fromYRot(this.getYRot())); + // Block.UPDATE_ALL + //); -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockPos, newState)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, pos, newState)) { + return; + } -+ level.setBlock(blockPos, newState, Block.UPDATE_ALL); ++ level.setBlock(pos, newState, Block.UPDATE_ALL); + // Paper end - call EntityChangeBlockEvent - if (level.getBlockEntity(blockPos) instanceof CopperGolemStatueBlockEntity copperGolemStatueBlockEntity) { + if (level.getBlockEntity(pos) instanceof CopperGolemStatueBlockEntity copperGolemStatueBlockEntity) { copperGolemStatueBlockEntity.createStatue(this); this.dropPreservedEquipment(level); - this.discard(); @@ -71,17 +71,17 @@ this.playSound(SoundEvents.COPPER_GOLEM_BECOME_STATUE); if (this.isLeashed()) { if (level.getGameRules().get(GameRules.ENTITY_DROPS)) { -@@ -425,12 +_,32 @@ +@@ -424,12 +_,32 @@ } @Override -- public void shear(ServerLevel level, SoundSource source, ItemStack shears) { +- public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool) { + // Paper start -+ public void shear(ServerLevel level, SoundSource source, ItemStack shears, java.util.List drops) { - level.playSound(null, this, SoundEvents.COPPER_GOLEM_SHEAR, source, 1.0F, 1.0F); -- ItemStack itemBySlot = this.getItemBySlot(EQUIPMENT_SLOT_ANTENNA); ++ public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool, final java.util.List drops) { + level.playSound(null, this, SoundEvents.COPPER_GOLEM_SHEAR, soundSource, 1.0F, 1.0F); +- ItemStack itemStack = this.getItemBySlot(EQUIPMENT_SLOT_ANTENNA); this.setItemSlot(EQUIPMENT_SLOT_ANTENNA, ItemStack.EMPTY); -- this.spawnAtLocation(level, itemBySlot, 1.5F); +- this.spawnAtLocation(level, itemStack, 1.5F); - } + for (ItemStack drop : drops) { + this.forceDrops = true; @@ -91,12 +91,12 @@ + } + + @Override -+ public void shear(ServerLevel level, SoundSource source, ItemStack shears) { -+ this.shear(level, source, shears, this.generateDefaultDrops(level, shears)); ++ public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool) { ++ this.shear(level, soundSource, tool, this.generateDefaultDrops(level, tool)); + } + + @Override -+ public java.util.List generateDefaultDrops(final net.minecraft.server.level.ServerLevel serverLevel, final net.minecraft.world.item.ItemStack shears) { ++ public java.util.List generateDefaultDrops(final ServerLevel level, final ItemStack tool) { + if (!this.readyForShearing()) { + return java.util.Collections.emptyList(); + } @@ -108,15 +108,15 @@ @Override public boolean readyForShearing() { -@@ -444,9 +_,13 @@ +@@ -443,9 +_,13 @@ } @Override -- protected void actuallyHurt(ServerLevel level, DamageSource damageSource, float amount) { -- super.actuallyHurt(level, damageSource, amount); +- protected void actuallyHurt(final ServerLevel level, final DamageSource source, final float dmg) { +- super.actuallyHurt(level, source, dmg); + // CraftBukkit start - void -> boolean -+ protected boolean actuallyHurt(ServerLevel level, DamageSource damageSource, float amount, org.bukkit.event.entity.EntityDamageEvent event) { -+ boolean damageResult = super.actuallyHurt(level, damageSource, amount, event); ++ protected boolean actuallyHurt(final ServerLevel level, final DamageSource source, final float dmg, final org.bukkit.event.entity.EntityDamageEvent event) { ++ boolean damageResult = super.actuallyHurt(level, source, dmg, event); + if (!damageResult) return false; + // CraftBukkit end this.setState(CopperGolemState.IDLE); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/IronGolem.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/IronGolem.java.patch index 9f6fe1934d89..afbb13319fe3 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/IronGolem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/IronGolem.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/entity/animal/golem/IronGolem.java @@ -106,7 +_,7 @@ @Override - protected void doPush(Entity entity) { + protected void doPush(final Entity entity) { if (entity instanceof Enemy && !(entity instanceof Creeper) && this.getRandom().nextInt(20) == 0) { - this.setTarget((LivingEntity)entity); + this.setTarget((LivingEntity)entity, org.bukkit.event.entity.EntityTargetLivingEntityEvent.TargetReason.COLLISION); // CraftBukkit - set reason @@ -10,11 +10,11 @@ super.doPush(entity); @@ -304,7 +_,7 @@ - BlockPos blockPos = this.blockPosition(); - BlockPos blockPos1 = blockPos.below(); - BlockState blockState = level.getBlockState(blockPos1); -- if (!blockState.entityCanStandOn(level, blockPos1, this)) { -+ if (!blockState.entityCanStandOn(level, blockPos1, this) && !this.level().paperConfig().entities.spawning.ironGolemsCanSpawnInAir) { // Paper - Add option to allow iron golems to spawn in air + BlockPos pos = this.blockPosition(); + BlockPos belowPos = pos.below(); + BlockState below = level.getBlockState(belowPos); +- if (!below.entityCanStandOn(level, belowPos, this)) { ++ if (!below.entityCanStandOn(level, belowPos, this) && !this.level().paperConfig().entities.spawning.ironGolemsCanSpawnInAir) { // Paper - Add option to allow iron golems to spawn in air return false; } else { for (int i = 1; i < 3; i++) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch index 730fcd93d943..00b0d31e243c 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch @@ -10,23 +10,23 @@ if (!serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) { @@ -107,7 +_,7 @@ - int floor2 = Mth.floor(this.getZ() + (i / 2 % 2 * 2 - 1) * 0.25F); - BlockPos blockPos = new BlockPos(floor, floor1, floor2); - if (this.level().getBlockState(blockPos).isAir() && blockState.canSurvive(this.level(), blockPos)) { -- this.level().setBlockAndUpdate(blockPos, blockState); -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this.level(), blockPos, blockState, net.minecraft.world.level.block.Block.UPDATE_ALL, this)) continue; // CraftBukkit - this.level().gameEvent(GameEvent.BLOCK_PLACE, blockPos, GameEvent.Context.of(this, blockState)); + int zz = Mth.floor(this.getZ() + (i / 2 % 2 * 2 - 1) * 0.25F); + BlockPos snowPos = new BlockPos(xx, yy, zz); + if (this.level().getBlockState(snowPos).isAir() && snow.canSurvive(this.level(), snowPos)) { +- this.level().setBlockAndUpdate(snowPos, snow); ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this.level(), snowPos, snow, net.minecraft.world.level.block.Block.UPDATE_ALL, this)) continue; // CraftBukkit + this.level().gameEvent(GameEvent.BLOCK_PLACE, snowPos, GameEvent.Context.of(this, snow)); } } -@@ -135,7 +_,19 @@ - ItemStack itemInHand = player.getItemInHand(hand); - if (itemInHand.is(Items.SHEARS) && this.readyForShearing()) { - if (this.level() instanceof ServerLevel serverLevel) { -- this.shear(serverLevel, SoundSource.PLAYERS, itemInHand); +@@ -138,7 +_,19 @@ + ItemStack itemStack = player.getItemInHand(hand); + if (itemStack.is(Items.SHEARS) && this.readyForShearing()) { + if (this.level() instanceof ServerLevel level) { +- this.shear(level, SoundSource.PLAYERS, itemStack); + // CraftBukkit start + // Paper start - custom shear drops -+ java.util.List drops = this.generateDefaultDrops(serverLevel, itemInHand); -+ org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemInHand, hand, drops); ++ java.util.List drops = this.generateDefaultDrops(level, itemStack); ++ org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemStack, hand, drops); + if (event != null) { + if (event.isCancelled()) { + return InteractionResult.PASS; @@ -35,40 +35,40 @@ + // Paper end - custom shear drops + } + // CraftBukkit end -+ this.shear(serverLevel, SoundSource.PLAYERS, itemInHand, drops); // Paper - custom shear drops ++ this.shear(level, SoundSource.PLAYERS, itemStack, drops); // Paper - custom shear drops this.gameEvent(GameEvent.SHEAR, player); - itemInHand.hurtAndBreak(1, player, hand.asEquipmentSlot()); + itemStack.hurtAndBreak(1, player, hand.asEquipmentSlot()); } -@@ -148,11 +_,29 @@ +@@ -151,9 +_,31 @@ @Override - public void shear(ServerLevel level, SoundSource source, ItemStack shears) { + public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool) { + // Paper start - custom shear drops -+ this.shear(level, source, shears, this.generateDefaultDrops(level, shears)); ++ this.shear(level, soundSource, tool, this.generateDefaultDrops(level, tool)); + } + + @Override -+ public java.util.List generateDefaultDrops(final ServerLevel level, final ItemStack shears) { ++ public java.util.List generateDefaultDrops(final ServerLevel level, final ItemStack tool) { + final java.util.List drops = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); -+ this.dropFromShearingLootTable(level, BuiltInLootTables.SHEAR_SNOW_GOLEM, shears, (ignored, stack) -> { ++ this.dropFromShearingLootTable(level, BuiltInLootTables.SHEAR_SNOW_GOLEM, tool, (ignored, stack) -> { + drops.add(stack); + }); + return drops; + } + + @Override -+ public void shear(ServerLevel level, SoundSource source, ItemStack shears, java.util.List drops) { ++ public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool, final java.util.List drops) { + // Paper end - custom shear drops - level.playSound(null, this, SoundEvents.SNOW_GOLEM_SHEAR, source, 1.0F, 1.0F); + level.playSound(null, this, SoundEvents.SNOW_GOLEM_SHEAR, soundSource, 1.0F, 1.0F); this.setPumpkin(false); -- this.dropFromShearingLootTable( -- level, BuiltInLootTables.SHEAR_SNOW_GOLEM, shears, (serverLevel, itemStack) -> this.spawnAtLocation(serverLevel, itemStack, this.getEyeHeight()) -- ); -+ drops.forEach(itemStack -> { // Paper - custom shear drops +- this.dropFromShearingLootTable(level, BuiltInLootTables.SHEAR_SNOW_GOLEM, tool, (l, drop) -> this.spawnAtLocation(l, drop, this.getEyeHeight())); ++ // Paper start - custom shear drops ++ drops.forEach(drop -> { + this.forceDrops = true; // CraftBukkit -+ this.spawnAtLocation(level, itemStack, this.getEyeHeight()); ++ this.spawnAtLocation(level, drop, this.getEyeHeight()); + this.forceDrops = false; // CraftBukkit + }); ++ // Paper end - custom shear drops } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/happyghast/HappyGhast.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/happyghast/HappyGhast.java.patch index 860f951bc384..f1e78e4bb704 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/happyghast/HappyGhast.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/happyghast/HappyGhast.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/world/entity/animal/happyghast/HappyGhast.java +++ b/net/minecraft/world/entity/animal/happyghast/HappyGhast.java -@@ -306,8 +_,12 @@ +@@ -314,8 +_,12 @@ } @Override -- protected void removePassenger(Entity passenger) { +- protected void removePassenger(final Entity passenger) { - super.removePassenger(passenger); + // Paper start - cancellable passengers -+ protected boolean removePassenger(Entity passenger, boolean suppressCancellation) { ++ protected boolean removePassenger(final Entity passenger, final boolean suppressCancellation) { + if (!super.removePassenger(passenger, suppressCancellation)) { + return false; + } @@ -15,7 +15,7 @@ if (!this.level().isClientSide()) { this.setServerStillTimeout(10); } -@@ -316,6 +_,7 @@ +@@ -324,6 +_,7 @@ this.clearHome(); this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.HARNESS_GOGGLES_UP, this.getSoundSource(), 1.0F, 1.0F); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/nautilus/AbstractNautilus.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/nautilus/AbstractNautilus.java.patch index 8d376e3a0e5a..3d324243e54b 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/nautilus/AbstractNautilus.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/nautilus/AbstractNautilus.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/animal/nautilus/AbstractNautilus.java +++ b/net/minecraft/world/entity/animal/nautilus/AbstractNautilus.java -@@ -139,8 +_,12 @@ +@@ -141,8 +_,12 @@ ) { int seaLevel = level.getSeaLevel(); - int i = seaLevel - 25; + int minSpawnLevel = seaLevel - 25; + // Paper start - Make water animal spawn height configurable + seaLevel = level.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.maximum.or(seaLevel - 5); -+ i = level.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.minimum.or(i); ++ minSpawnLevel = level.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.minimum.or(minSpawnLevel); + // Paper end - Make water animal spawn height configurable - return pos.getY() >= i + return pos.getY() >= minSpawnLevel - && pos.getY() <= seaLevel - 5 + && pos.getY() <= seaLevel // Paper - Make water animal spawn height configurable && level.getFluidState(pos.below()).is(FluidTags.WATER) @@ -16,37 +16,28 @@ } @@ -263,7 +_,7 @@ boolean hasEffect = player.hasEffect(MobEffects.BREATH_OF_THE_NAUTILUS); - boolean flag = level.getGameTime() % 40L == 0L; - if (!hasEffect || flag) { + boolean shouldRefresh = level.getGameTime() % 40L == 0L; + if (!hasEffect || shouldRefresh) { - player.addEffect(new MobEffectInstance(MobEffects.BREATH_OF_THE_NAUTILUS, 60, 0, true, true, true)); + player.addEffect(new MobEffectInstance(MobEffects.BREATH_OF_THE_NAUTILUS, 60, 0, true, true, true), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.NAUTILUS); // Paper - Add cause } } } -@@ -405,7 +_,7 @@ - - if (this.isFood(itemInHand) && this.getHealth() < this.getMaxHealth()) { - FoodProperties foodProperties = itemInHand.get(DataComponents.FOOD); -- this.heal(foodProperties != null ? 2 * foodProperties.nutrition() : 1.0F); -+ this.heal(foodProperties != null ? 2 * foodProperties.nutrition() : 1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // Paper - Add regain reason - this.usePlayerItem(player, hand, itemInHand); - this.playEatingSound(); - return InteractionResult.SUCCESS; -@@ -427,7 +_,7 @@ +@@ -431,7 +_,7 @@ } - private void tryToTame(Player player) { + private void tryToTame(final Player player) { - if (this.random.nextInt(3) == 0) { + if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit - added event call this.tame(player); this.navigation.stop(); this.level().broadcastEntityEvent(this, EntityEvent.TAMING_SUCCEEDED); -@@ -482,7 +_,7 @@ +@@ -486,7 +_,7 @@ protected void createInventory() { - SimpleContainer simpleContainer = this.inventory; + SimpleContainer old = this.inventory; - this.inventory = new SimpleContainer(this.getInventorySize()); + this.inventory = new SimpleContainer(this.getInventorySize(), (org.bukkit.entity.AbstractNautilus) this.getBukkitEntity()); // Paper - add owner - if (simpleContainer != null) { - int min = Math.min(simpleContainer.getContainerSize(), this.inventory.getContainerSize()); + if (old != null) { + int max = Math.min(old.getContainerSize(), this.inventory.getContainerSize()); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/nautilus/Nautilus.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/nautilus/Nautilus.java.patch index cc0fc86f7522..a17b2fd11b2b 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/nautilus/Nautilus.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/nautilus/Nautilus.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/nautilus/Nautilus.java +++ b/net/minecraft/world/entity/animal/nautilus/Nautilus.java -@@ -109,8 +_,14 @@ +@@ -111,8 +_,14 @@ @Override public int getMaxAirSupply() { @@ -15,9 +15,9 @@ + } + // CraftBukkit end - protected void handleAirSupply(ServerLevel level, int airSupply) { + protected void handleAirSupply(final ServerLevel level, final int preTickAirSupply) { if (this.isAlive() && !this.isInWater()) { -@@ -120,7 +_,7 @@ +@@ -122,7 +_,7 @@ this.hurtServer(level, this.damageSources().dryOut(), 2.0F); } } else { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/panda/Panda.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/panda/Panda.java.patch index 8df23114de3b..422c65d8fdf1 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/panda/Panda.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/panda/Panda.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/animal/panda/Panda.java +++ b/net/minecraft/world/entity/animal/panda/Panda.java -@@ -130,6 +_,7 @@ +@@ -133,6 +_,7 @@ } - public void sit(boolean sitting) { -+ if (!new io.papermc.paper.event.entity.EntityToggleSitEvent(this.getBukkitEntity(), sitting).callEvent()) return; // Paper - Add EntityToggleSitEvent - this.setFlag(FLAG_SIT, sitting); + public void sit(final boolean value) { ++ if (!new io.papermc.paper.event.entity.EntityToggleSitEvent(this.getBukkitEntity(), value).callEvent()) return; // Paper - Add EntityToggleSitEvent + this.setFlag(FLAG_SIT, value); } -@@ -518,24 +_,28 @@ +@@ -517,24 +_,28 @@ for (Panda panda : level.getEntitiesOfClass(Panda.class, this.getBoundingBox().inflate(10.0))) { if (!panda.isBaby() && panda.onGround() && !panda.isInWater() && panda.canPerformAction()) { @@ -26,56 +26,56 @@ } @Override - protected void pickUpItem(ServerLevel level, ItemEntity entity) { + protected void pickUpItem(final ServerLevel level, final ItemEntity entity) { - if (this.getItemBySlot(EquipmentSlot.MAINHAND).isEmpty() && canPickUpAndEat(entity)) { + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(this, entity, 0, !(this.getItemBySlot(EquipmentSlot.MAINHAND).isEmpty() && Panda.canPickUpAndEat(entity))).isCancelled()) { // CraftBukkit this.onItemPickup(entity); - ItemStack item = entity.getItem(); - this.setItemSlot(EquipmentSlot.MAINHAND, item); + ItemStack itemStack = entity.getItem(); + this.setItemSlot(EquipmentSlot.MAINHAND, itemStack); this.setGuaranteedDrop(EquipmentSlot.MAINHAND); - this.take(entity, item.getCount()); + this.take(entity, itemStack.getCount()); - entity.discard(); + entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause } } -@@ -625,8 +_,9 @@ - this.usePlayerItem(player, hand, itemInHand); - this.ageUp((int)(-this.getAge() / 20 * 0.1F), true); - } else if (!this.level().isClientSide() && this.getAge() == 0 && this.canFallInLove()) { -+ final ItemStack breedCopy = itemInHand.copy(); // Paper - Fix EntityBreedEvent copying - this.usePlayerItem(player, hand, itemInHand); -- this.setInLove(player); -+ this.setInLove(player, breedCopy); // Paper - Fix EntityBreedEvent copying - } else { - if (!(this.level() instanceof ServerLevel serverLevel) || this.isSitting() || this.isInWater()) { - return InteractionResult.PASS; -@@ -636,7 +_,9 @@ - this.eat(true); - ItemStack itemBySlot = this.getItemBySlot(EquipmentSlot.MAINHAND); - if (!itemBySlot.isEmpty() && !player.hasInfiniteMaterials()) { -+ this.forceDrops = true; // Paper - Add missing forceDrop toggles - this.spawnAtLocation(serverLevel, itemBySlot); -+ this.forceDrops = false; // Paper - Add missing forceDrop toggles +@@ -629,8 +_,9 @@ } - this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(itemInHand.getItem(), 1)); -@@ -855,7 +_,7 @@ + if (!this.level().isClientSide() && this.getAge() == 0 && this.canFallInLove()) { ++ final ItemStack breedCopy = interactionItemStack.copy(); // Paper - Fix EntityBreedEvent copying + this.usePlayerItem(player, hand, interactionItemStack); +- this.setInLove(player); ++ this.setInLove(player, breedCopy); // Paper - Fix EntityBreedEvent copying + } else { + if (!(this.level() instanceof ServerLevel level) || this.isSitting() || this.isInWater()) { + return InteractionResult.PASS; +@@ -640,7 +_,9 @@ + this.eat(true); + ItemStack pandasCurrentItem = this.getItemBySlot(EquipmentSlot.MAINHAND); + if (!pandasCurrentItem.isEmpty() && !player.hasInfiniteMaterials()) { ++ this.forceDrops = true; // Paper - Add missing forceDrop toggles + this.spawnAtLocation(level, pandasCurrentItem); ++ this.forceDrops = false; // Paper - Add missing forceDrop toggles + } + + this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(interactionItemStack.getItem(), 1)); +@@ -862,7 +_,7 @@ @Override - protected void alertOther(Mob mob, LivingEntity target) { - if (mob instanceof Panda && mob.isAggressive()) { -- mob.setTarget(target); -+ mob.setTarget(target, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_ENTITY); // CraftBukkit + protected void alertOther(final Mob other, final LivingEntity hurtByMob) { + if (other instanceof Panda && other.isAggressive()) { +- other.setTarget(hurtByMob); ++ other.setTarget(hurtByMob, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_ENTITY); // CraftBukkit } } } -@@ -1084,7 +_,9 @@ +@@ -1093,7 +_,9 @@ public void stop() { - ItemStack itemBySlot = Panda.this.getItemBySlot(EquipmentSlot.MAINHAND); - if (!itemBySlot.isEmpty()) { + ItemStack itemStack = Panda.this.getItemBySlot(EquipmentSlot.MAINHAND); + if (!itemStack.isEmpty()) { + Panda.this.forceDrops = true; // Paper - Add missing forceDrop toggles - Panda.this.spawnAtLocation(getServerLevel(Panda.this.level()), itemBySlot); + Panda.this.spawnAtLocation(getServerLevel(Panda.this.level()), itemStack); + Panda.this.forceDrops = false; // Paper - Add missing forceDrop toggles Panda.this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY); - int i = Panda.this.isLazy() ? Panda.this.random.nextInt(50) + 10 : Panda.this.random.nextInt(150) + 10; - this.cooldown = Panda.this.tickCount + i * 20; + int waitSeconds = Panda.this.isLazy() ? Panda.this.random.nextInt(50) + 10 : Panda.this.random.nextInt(150) + 10; + this.cooldown = Panda.this.tickCount + waitSeconds * 20; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/parrot/Parrot.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/parrot/Parrot.java.patch index 7db0b4f6b0e1..d5100bd9db08 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/parrot/Parrot.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/parrot/Parrot.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/parrot/Parrot.java +++ b/net/minecraft/world/entity/animal/parrot/Parrot.java -@@ -269,7 +_,7 @@ +@@ -270,7 +_,7 @@ } if (!this.level().isClientSide()) { @@ -9,16 +9,16 @@ this.tame(player); this.level().broadcastEntityEvent(this, EntityEvent.TAMING_SUCCEEDED); } else { -@@ -290,7 +_,7 @@ +@@ -291,7 +_,7 @@ } } else { - this.usePlayerItem(player, hand, itemInHand); + this.usePlayerItem(player, hand, itemStack); - this.addEffect(new MobEffectInstance(MobEffects.POISON, 900)); + this.addEffect(new MobEffectInstance(MobEffects.POISON, 900), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); // CraftBukkit if (player.isCreative() || !this.isInvulnerable()) { this.hurt(this.damageSources().playerAttack(player), Float.MAX_VALUE); } -@@ -383,8 +_,8 @@ +@@ -384,8 +_,8 @@ } @Override @@ -29,16 +29,16 @@ } @Override -@@ -399,8 +_,13 @@ - if (this.isInvulnerableTo(level, damageSource)) { +@@ -400,8 +_,13 @@ + if (this.isInvulnerableTo(level, source)) { return false; } else { + // CraftBukkit start -+ if (!super.hurtServer(level, damageSource, amount)) { ++ if (!super.hurtServer(level, source, damage)) { + return false; + } this.setOrderedToSit(false); -- return super.hurtServer(level, damageSource, amount); +- return super.hurtServer(level, source, damage); + return true; + // CraftBukkit } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/parrot/ShoulderRidingEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/parrot/ShoulderRidingEntity.java.patch index 74075655549d..beb7868e9c5d 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/parrot/ShoulderRidingEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/parrot/ShoulderRidingEntity.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/entity/animal/parrot/ShoulderRidingEntity.java +++ b/net/minecraft/world/entity/animal/parrot/ShoulderRidingEntity.java @@ -24,7 +_,7 @@ - this.saveWithoutId(tagValueOutput); - tagValueOutput.putString("id", this.getEncodeId()); - if (player.setEntityOnShoulder(tagValueOutput.buildResult())) { + this.saveWithoutId(output); + output.putString("id", this.getEncodeId()); + if (player.setEntityOnShoulder(output.buildResult())) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause return true; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/pig/Pig.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/pig/Pig.java.patch index 7f3ffd06c1f0..1876e579f8ef 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/pig/Pig.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/pig/Pig.java.patch @@ -1,18 +1,18 @@ --- a/net/minecraft/world/entity/animal/pig/Pig.java +++ b/net/minecraft/world/entity/animal/pig/Pig.java -@@ -178,7 +_,14 @@ - ZombifiedPiglin zombifiedPiglin = this.convertTo(EntityType.ZOMBIFIED_PIGLIN, ConversionParams.single(this, false, true), mob -> { - mob.populateDefaultEquipmentSlots(this.getRandom(), level.getCurrentDifficultyAt(this.blockPosition())); - mob.setPersistenceRequired(); +@@ -198,7 +_,14 @@ + ZombifiedPiglin zombifiedPiglin = this.convertTo(EntityType.ZOMBIFIED_PIGLIN, ConversionParams.single(this, false, true), zp -> { + zp.populateDefaultEquipmentSlots(this.getRandom(), level.getCurrentDifficultyAt(this.blockPosition())); + zp.setPersistenceRequired(); - }); + // CraftBukkit start + }, null, null); -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callPigZapEvent(this, lightning, zombifiedPiglin).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callPigZapEvent(this, lightningBolt, zombifiedPiglin).isCancelled()) { + return; + } + level.addFreshEntity(zombifiedPiglin, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.TRANSFORMATION); // CraftBukkit - add Bukkit remove cause + // CraftBukkit end if (zombifiedPiglin == null) { - super.thunderHit(level, lightning); + super.thunderHit(level, lightningBolt); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/rabbit/Rabbit.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/rabbit/Rabbit.java.patch index 6c26de099a9b..0deae6397e12 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/rabbit/Rabbit.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/rabbit/Rabbit.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/animal/rabbit/Rabbit.java +++ b/net/minecraft/world/entity/animal/rabbit/Rabbit.java -@@ -99,7 +_,7 @@ +@@ -113,7 +_,7 @@ super(type, level); this.jumpControl = new Rabbit.RabbitJumpControl(this); this.moveControl = new Rabbit.RabbitMoveControl(this); @@ -9,15 +9,15 @@ } @Override -@@ -589,9 +_,11 @@ +@@ -637,9 +_,11 @@ if (this.canRaid && block instanceof CarrotBlock) { - int ageValue = blockState.getValue(CarrotBlock.AGE); - if (ageValue == 0) { -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.rabbit, blockPos, blockState.getFluidState().createLegacyBlock())) return; // CraftBukkit // Paper - fix wrong block state - level.setBlock(blockPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS); - level.destroyBlock(blockPos, true, this.rabbit); + int carrotAge = blockState.getValue(CarrotBlock.AGE); + if (carrotAge == 0) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.rabbit, cropsPos, blockState.getFluidState().createLegacyBlock())) return; // CraftBukkit // Paper - fix wrong block state + level.setBlock(cropsPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS); + level.destroyBlock(cropsPos, true, this.rabbit); } else { -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.rabbit, blockPos, blockState.setValue(CarrotBlock.AGE, ageValue - 1))) return; // CraftBukkit // Paper - fix wrong block state - level.setBlock(blockPos, blockState.setValue(CarrotBlock.AGE, ageValue - 1), Block.UPDATE_CLIENTS); - level.gameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Context.of(this.rabbit)); - level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, blockPos, Block.getId(blockState)); ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.rabbit, cropsPos, blockState.setValue(CarrotBlock.AGE, carrotAge - 1))) return; // CraftBukkit // Paper - fix wrong block state + level.setBlock(cropsPos, blockState.setValue(CarrotBlock.AGE, carrotAge - 1), Block.UPDATE_CLIENTS); + level.gameEvent(GameEvent.BLOCK_CHANGE, cropsPos, GameEvent.Context.of(this.rabbit)); + level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, cropsPos, Block.getId(blockState)); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/sheep/Sheep.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/sheep/Sheep.java.patch index bb206b657c75..c9a21385ac4b 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/sheep/Sheep.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/sheep/Sheep.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/animal/sheep/Sheep.java +++ b/net/minecraft/world/entity/animal/sheep/Sheep.java @@ -140,7 +_,19 @@ - ItemStack itemInHand = player.getItemInHand(hand); - if (itemInHand.is(Items.SHEARS)) { - if (this.level() instanceof ServerLevel serverLevel && this.readyForShearing()) { -- this.shear(serverLevel, SoundSource.PLAYERS, itemInHand); + ItemStack itemStack = player.getItemInHand(hand); + if (itemStack.is(Items.SHEARS)) { + if (this.level() instanceof ServerLevel level && this.readyForShearing()) { +- this.shear(level, SoundSource.PLAYERS, itemStack); + // CraftBukkit start + // Paper start - custom shear drops -+ java.util.List drops = this.generateDefaultDrops(serverLevel, itemInHand); -+ org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemInHand, hand, drops); ++ java.util.List drops = this.generateDefaultDrops(level, itemStack); ++ org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemStack, hand, drops); + if (event != null) { + if (event.isCancelled()) { + return InteractionResult.PASS; @@ -17,46 +17,46 @@ + // Paper end - custom shear drops + } + // CraftBukkit end -+ this.shear(serverLevel, SoundSource.PLAYERS, itemInHand, drops); // Paper - custom shear drops ++ this.shear(level, SoundSource.PLAYERS, itemStack, drops); // Paper - custom shear drops this.gameEvent(GameEvent.SHEAR, player); - itemInHand.hurtAndBreak(1, player, hand.asEquipmentSlot()); + itemStack.hurtAndBreak(1, player, hand.asEquipmentSlot()); return InteractionResult.SUCCESS_SERVER; @@ -154,14 +_,28 @@ @Override - public void shear(ServerLevel level, SoundSource source, ItemStack shears) { + public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool) { + // Paper start - custom shear drops -+ this.shear(level, source, shears, this.generateDefaultDrops(level, shears)); ++ this.shear(level, soundSource, tool, this.generateDefaultDrops(level, tool)); + } + + @Override -+ public java.util.List generateDefaultDrops(final ServerLevel level, final ItemStack shears) { ++ public java.util.List generateDefaultDrops(final ServerLevel level, final ItemStack tool) { + final java.util.List drops = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); -+ this.dropFromShearingLootTable(level, BuiltInLootTables.SHEAR_SHEEP, shears, (ignored, stack) -> { -+ for (int i = 0; i < stack.getCount(); ++i) drops.add(stack.copyWithCount(1)); ++ this.dropFromShearingLootTable(level, BuiltInLootTables.SHEAR_SHEEP, tool, (ignored, stack) -> { ++ for (int i = 0; i < stack.getCount(); ++i) drops.add(stack.copyWithCount(1)); + }); + return drops; + } + + @Override -+ public void shear(ServerLevel level, SoundSource source, ItemStack shears, java.util.List drops) { ++ public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool, final java.util.List drops) { + // Paper end - custom shear drops - level.playSound(null, this, SoundEvents.SHEEP_SHEAR, source, 1.0F, 1.0F); + level.playSound(null, this, SoundEvents.SHEEP_SHEAR, soundSource, 1.0F, 1.0F); - this.dropFromShearingLootTable( - level, - BuiltInLootTables.SHEAR_SHEEP, -- shears, -- (serverLevel, itemStack) -> { -- for (int i = 0; i < itemStack.getCount(); i++) { -- ItemEntity itemEntity = this.spawnAtLocation(serverLevel, itemStack.copyWithCount(1), 1.0F); +- tool, +- (l, drop) -> { +- for (int i = 0; i < drop.getCount(); i++) { +- ItemEntity entity = this.spawnAtLocation(l, drop.copyWithCount(1), 1.0F); + drops.forEach(itemStack -> { // Paper - custom drops - loop in generated default drops + { // Paper - custom drops - loop in generated default drops + this.forceDrops = true; // CraftBukkit -+ ItemEntity itemEntity = this.spawnAtLocation(level, itemStack, 1.0F); // Paper - custom drops - copy already done above ++ ItemEntity entity = this.spawnAtLocation(level, itemStack, 1.0F); // Paper - custom drops - copy already done above + this.forceDrops = false; // CraftBukkit - if (itemEntity != null) { - itemEntity.setDeltaMovement( - itemEntity.getDeltaMovement() + if (entity != null) { + entity.setDeltaMovement( + entity.getDeltaMovement() @@ -279,6 +_,7 @@ @Override @@ -64,4 +64,4 @@ + if (!new org.bukkit.event.entity.SheepRegrowWoolEvent((org.bukkit.entity.Sheep) this.getBukkitEntity()).callEvent()) return; // CraftBukkit super.ate(); this.setSheared(false); - if (this.isBaby()) { + if (this.canAgeUp()) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/sniffer/Sniffer.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/sniffer/Sniffer.java.patch index 915d16ecb441..15676ec795c0 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/sniffer/Sniffer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/sniffer/Sniffer.java.patch @@ -1,35 +1,35 @@ --- a/net/minecraft/world/entity/animal/sniffer/Sniffer.java +++ b/net/minecraft/world/entity/animal/sniffer/Sniffer.java -@@ -278,6 +_,13 @@ - this.dropFromGiftLootTable(serverLevel, BuiltInLootTables.SNIFFER_DIGGING, (serverLevel1, itemStack) -> { - ItemEntity itemEntity = new ItemEntity(this.level(), headBlock.getX(), headBlock.getY(), headBlock.getZ(), itemStack); - itemEntity.setDefaultPickUpDelay(); +@@ -288,6 +_,13 @@ + this.dropFromGiftLootTable(level, BuiltInLootTables.SNIFFER_DIGGING, (l, itemStack) -> { + ItemEntity entity = new ItemEntity(this.level(), head.getX(), head.getY(), head.getZ(), itemStack); + entity.setDefaultPickUpDelay(); + // CraftBukkit start - handle EntityDropItemEvent -+ org.bukkit.event.entity.EntityDropItemEvent event = new org.bukkit.event.entity.EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity()); ++ org.bukkit.event.entity.EntityDropItemEvent event = new org.bukkit.event.entity.EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entity.getBukkitEntity()); + org.bukkit.Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + // CraftBukkit end - serverLevel1.addFreshEntity(itemEntity); + l.addFreshEntity(entity); }); this.playSound(SoundEvents.SNIFFER_DROP_SEED, 1.0F, 1.0F); -@@ -336,12 +_,17 @@ +@@ -346,12 +_,17 @@ @Override - public void spawnChildFromBreeding(ServerLevel level, Animal partner) { + public void spawnChildFromBreeding(final ServerLevel level, final Animal partner) { + // Paper start - Add EntityFertilizeEggEvent event + final io.papermc.paper.event.entity.EntityFertilizeEggEvent result = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityFertilizeEggEvent(this, partner); + if (result.isCancelled()) return; + // Paper end - Add EntityFertilizeEggEvent event ItemStack itemStack = new ItemStack(Items.SNIFFER_EGG); - ItemEntity itemEntity = new ItemEntity(level, this.position().x(), this.position().y(), this.position().z(), itemStack); - itemEntity.setDefaultPickUpDelay(); + ItemEntity entity = new ItemEntity(level, this.position().x(), this.position().y(), this.position().z(), itemStack); + entity.setDefaultPickUpDelay(); - this.finalizeSpawnChildFromBreeding(level, partner, null); + this.finalizeSpawnChildFromBreeding(level, partner, null, result.getExperience()); // Paper - Add EntityFertilizeEggEvent event -+ if (this.spawnAtLocation(level, itemEntity) != null) { // Paper - Call EntityDropItemEvent ++ if (this.spawnAtLocation(level, entity) != null) { // Paper - Call EntityDropItemEvent this.playSound(SoundEvents.SNIFFER_EGG_PLOP, 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 0.5F); -- level.addFreshEntity(itemEntity); +- level.addFreshEntity(entity); + } // Paper - Call EntityDropItemEvent } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/squid/Squid.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/squid/Squid.java.patch index b6389c8fc3e5..6cb1af19242e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/squid/Squid.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/squid/Squid.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/entity/animal/squid/Squid.java +++ b/net/minecraft/world/entity/animal/squid/Squid.java -@@ -48,7 +_,7 @@ +@@ -51,7 +_,7 @@ - public Squid(EntityType type, Level level) { + public Squid(final EntityType type, final Level level) { super(type, level); - this.random.setSeed(this.getId()); + // this.random.setSeed(this.getId()); // Paper - Share random for entities to make them more random diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/turtle/Turtle.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/turtle/Turtle.java.patch index f65385287f30..607e65c3a8fa 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/turtle/Turtle.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/turtle/Turtle.java.patch @@ -3,9 +3,9 @@ @@ -256,7 +_,9 @@ protected void ageBoundaryReached() { super.ageBoundaryReached(); - if (!this.isBaby() && this.level() instanceof ServerLevel serverLevel && serverLevel.getGameRules().get(GameRules.MOB_DROPS)) { + if (!this.isBaby() && this.level() instanceof ServerLevel level && level.getGameRules().get(GameRules.MOB_DROPS)) { + this.forceDrops = true; // CraftBukkit - this.dropFromGiftLootTable(serverLevel, BuiltInLootTables.TURTLE_GROW, this::spawnAtLocation); + this.dropFromGiftLootTable(level, BuiltInLootTables.TURTLE_GROW, this::spawnAtLocation); + this.forceDrops = false; // CraftBukkit } } @@ -13,9 +13,9 @@ @@ -277,7 +_,7 @@ @Override - public void thunderHit(ServerLevel level, LightningBolt lightning) { + public void thunderHit(final ServerLevel level, final LightningBolt lightningBolt) { - this.hurtServer(level, this.damageSources().lightningBolt(), Float.MAX_VALUE); -+ this.hurtServer(level, this.damageSources().lightningBolt().eventEntityDamager(lightning), Float.MAX_VALUE); // CraftBukkit // Paper - fix DamageSource API ++ this.hurtServer(level, this.damageSources().lightningBolt().eventEntityDamager(lightningBolt), Float.MAX_VALUE); // CraftBukkit // Paper - fix DamageSource API } @Override @@ -49,7 +49,7 @@ @Override @@ -448,14 +_,20 @@ - BlockPos blockPos = this.turtle.blockPosition(); + BlockPos turtlePos = this.turtle.blockPosition(); if (!this.turtle.isInWater() && this.isReachedTarget()) { if (this.turtle.layEggCounter < 1) { - this.turtle.setLayingEgg(true); @@ -61,12 +61,12 @@ + if (layEggEvent.callEvent() && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.turtle, this.blockPos.above(), Blocks.TURTLE_EGG.defaultBlockState().setValue(TurtleEggBlock.EGGS, layEggEvent.getEggCount()))) { + // Paper end - Turtle API Level level = this.turtle.level(); - level.playSound(null, blockPos, SoundEvents.TURTLE_LAY_EGG, SoundSource.BLOCKS, 0.3F, 0.9F + level.random.nextFloat() * 0.2F); - BlockPos blockPos1 = this.blockPos.above(); -- BlockState blockState = Blocks.TURTLE_EGG.defaultBlockState().setValue(TurtleEggBlock.EGGS, this.turtle.random.nextInt(4) + 1); -+ BlockState blockState = Blocks.TURTLE_EGG.defaultBlockState().setValue(TurtleEggBlock.EGGS, layEggEvent.getEggCount()); // Paper - level.setBlock(blockPos1, blockState, Block.UPDATE_ALL); - level.gameEvent(GameEvent.BLOCK_PLACE, blockPos1, GameEvent.Context.of(this.turtle, blockState)); + level.playSound(null, turtlePos, SoundEvents.TURTLE_LAY_EGG, SoundSource.BLOCKS, 0.3F, 0.9F + level.getRandom().nextFloat() * 0.2F); + BlockPos eggPos = this.blockPos.above(); +- BlockState eggState = Blocks.TURTLE_EGG.defaultBlockState().setValue(TurtleEggBlock.EGGS, this.turtle.random.nextInt(4) + 1); ++ BlockState eggState = Blocks.TURTLE_EGG.defaultBlockState().setValue(TurtleEggBlock.EGGS, layEggEvent.getEggCount()); // Paper + level.setBlock(eggPos, eggState, Block.UPDATE_ALL); + level.gameEvent(GameEvent.BLOCK_PLACE, eggPos, GameEvent.Context.of(this.turtle, eggState)); + } // CraftBukkit this.turtle.setHasEgg(false); this.turtle.setLayingEgg(false); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/wolf/Wolf.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/wolf/Wolf.java.patch index 73a3cd945133..601db61365af 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/wolf/Wolf.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/wolf/Wolf.java.patch @@ -1,36 +1,36 @@ --- a/net/minecraft/world/entity/animal/wolf/Wolf.java +++ b/net/minecraft/world/entity/animal/wolf/Wolf.java -@@ -397,16 +_,18 @@ - if (this.isInvulnerableTo(level, damageSource)) { +@@ -396,16 +_,18 @@ + if (this.isInvulnerableTo(level, source)) { return false; } else { -+ if (!super.hurtServer(level, damageSource, amount)) return false; // CraftBukkit ++ if (!super.hurtServer(level, source, damage)) return false; // CraftBukkit this.setOrderedToSit(false); -- return super.hurtServer(level, damageSource, amount); +- return super.hurtServer(level, source, damage); + return true; // CraftBukkit } } @Override -- protected void actuallyHurt(ServerLevel level, DamageSource damageSource, float amount) { -+ public boolean actuallyHurt(ServerLevel level, DamageSource damageSource, float amount, org.bukkit.event.entity.EntityDamageEvent event) { // CraftBukkit - void -> boolean - if (!this.canArmorAbsorb(damageSource)) { -- super.actuallyHurt(level, damageSource, amount); -+ return super.actuallyHurt(level, damageSource, amount, event); // CraftBukkit +- protected void actuallyHurt(final ServerLevel level, final DamageSource source, final float damage) { ++ public boolean actuallyHurt(final ServerLevel level, final DamageSource source, final float damage, final org.bukkit.event.entity.EntityDamageEvent event) { // CraftBukkit - void -> boolean + if (!this.canArmorAbsorb(source)) { +- super.actuallyHurt(level, source, damage); ++ return super.actuallyHurt(level, source, damage, event); // CraftBukkit } else { + if (event.isCancelled()) return false; // CraftBukkit - SPIGOT-7815: if the damage was cancelled, no need to run the wolf armor behaviour - ItemStack bodyArmorItem = this.getBodyArmorItem(); - int damageValue = bodyArmorItem.getDamageValue(); - int maxDamage = bodyArmorItem.getMaxDamage(); -@@ -426,6 +_,7 @@ + ItemStack armorBefore = this.getBodyArmorItem(); + int damageBefore = armorBefore.getDamageValue(); + int maxDamage = armorBefore.getMaxDamage(); +@@ -417,6 +_,7 @@ ); } } + return true; // CraftBukkit // Paper - return false ONLY if event was cancelled } - private boolean canArmorAbsorb(DamageSource damageSource) { -@@ -436,7 +_,7 @@ + private boolean canArmorAbsorb(final DamageSource source) { +@@ -427,7 +_,7 @@ protected void applyTamingSideEffects() { if (this.isTame()) { this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(40.0); @@ -39,16 +39,7 @@ } else { this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(8.0); } -@@ -461,7 +_,7 @@ - this.usePlayerItem(player, hand, itemInHand); - FoodProperties foodProperties = itemInHand.get(DataComponents.FOOD); - float f = foodProperties != null ? foodProperties.nutrition() : 1.0F; -- this.heal(2.0F * f); -+ this.heal(2.0F * f, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // CraftBukkit - return InteractionResult.SUCCESS; - } - -@@ -490,7 +_,7 @@ +@@ -477,7 +_,7 @@ this.setOrderedToSit(!this.isOrderedToSit()); this.jumping = false; this.navigation.stop(); @@ -57,24 +48,36 @@ return InteractionResult.SUCCESS.withoutItem(); } -@@ -499,6 +_,13 @@ +@@ -486,6 +_,25 @@ - DyeColor dyeColor = dyeItem.getDyeColor(); - if (dyeColor != this.getCollarColor()) { + DyeColor color = itemStack.get(DataComponents.DYE); + if (color != null && color != this.getCollarColor()) { + // Paper start - Add EntityDyeEvent and CollarColorable interface -+ final io.papermc.paper.event.entity.EntityDyeEvent event = new io.papermc.paper.event.entity.EntityDyeEvent(this.getBukkitEntity(), org.bukkit.DyeColor.getByWoolData((byte) dyeColor.getId()), (org.bukkit.entity.Player) player.getBukkitEntity()); ++ final DyeColor originalColor = color; ++ final io.papermc.paper.event.entity.EntityDyeEvent event = new io.papermc.paper.event.entity.EntityDyeEvent(this.getBukkitEntity(), org.bukkit.DyeColor.getByWoolData((byte) color.getId()), (org.bukkit.entity.Player) player.getBukkitEntity()); + if (!event.callEvent()) { ++ if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { ++ this.resendPossiblyDesyncedDataValues(java.util.List.of(DATA_COLLAR_COLOR), serverPlayer); ++ } ++ if (!player.hasInfiniteMaterials()) { ++ player.containerMenu.forceHeldSlot(hand); ++ } + return InteractionResult.FAIL; + } -+ dyeColor = DyeColor.byId(event.getColor().getWoolData()); ++ color = DyeColor.byId(event.getColor().getWoolData()); ++ if (originalColor != color) { ++ if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { ++ this.resendPossiblyDesyncedDataValues(java.util.List.of(DATA_COLLAR_COLOR), serverPlayer); ++ } ++ } + // Paper end - Add EntityDyeEvent and CollarColorable interface - this.setCollarColor(dyeColor); - itemInHand.consume(1, player); + this.setCollarColor(color); + itemStack.consume(1, player); return InteractionResult.SUCCESS; -@@ -513,7 +_,7 @@ +@@ -500,7 +_,7 @@ } - private void tryToTame(Player player) { + private void tryToTame(final Player player) { - if (this.random.nextInt(3) == 0) { + if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, player).isCancelled()) { // CraftBukkit - added event call this.tame(player); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java.patch index 89b1a90fe0b4..7900660e9112 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java.patch @@ -6,14 +6,14 @@ public int time; + public boolean generatedByDragonFight = false; // Paper - Fix invulnerable end crystals - public EndCrystal(EntityType type, Level level) { + public EndCrystal(final EntityType type, final Level level) { super(type, level); @@ -57,21 +_,37 @@ if (this.level() instanceof ServerLevel) { - BlockPos blockPos = this.blockPosition(); - if (((ServerLevel)this.level()).getDragonFight() != null && this.level().getBlockState(blockPos).isAir()) { -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level(), blockPos, this).isCancelled()) { // Paper - this.level().setBlockAndUpdate(blockPos, BaseFireBlock.getState(this.level(), blockPos)); + BlockPos pos = this.blockPosition(); + if (((ServerLevel)this.level()).getDragonFight() != null && this.level().getBlockState(pos).isAir()) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level(), pos, this).isCancelled()) { // Paper + this.level().setBlockAndUpdate(pos, BaseFireBlock.getState(this.level(), pos)); - } - } + } // Paper @@ -25,7 +25,7 @@ + if (!java.util.Objects.equals(((ServerLevel) this.level()).uuid, this.originWorld) + || ((ServerLevel) this.level()).getDragonFight() == null + || ((ServerLevel) this.level()).getDragonFight().respawnStage == null -+ || ((ServerLevel) this.level()).getDragonFight().respawnStage.ordinal() > net.minecraft.world.level.dimension.end.DragonRespawnAnimation.SUMMONING_DRAGON.ordinal()) { ++ || ((ServerLevel) this.level()).getDragonFight().respawnStage.ordinal() > net.minecraft.world.level.dimension.end.DragonRespawnStage.SUMMONING_DRAGON.ordinal()) { + this.setInvulnerable(false); + this.setBeamTarget(null); + } @@ -34,14 +34,14 @@ } @Override - protected void addAdditionalSaveData(ValueOutput output) { + protected void addAdditionalSaveData(final ValueOutput output) { output.storeNullable("beam_target", BlockPos.CODEC, this.getBeamTarget()); output.putBoolean("ShowBottom", this.showsBottom()); + output.putBoolean("Paper.GeneratedByDragonFight", this.generatedByDragonFight); // Paper - Fix invulnerable end crystals } @Override - protected void readAdditionalSaveData(ValueInput input) { + protected void readAdditionalSaveData(final ValueInput input) { this.setBeamTarget(input.read("beam_target", BlockPos.CODEC).orElse(null)); this.setShowBottom(input.getBooleanOr("ShowBottom", true)); + this.generatedByDragonFight = input.getBooleanOr("Paper.GeneratedByDragonFight", false); // Paper - Fix invulnerable end crystals @@ -54,13 +54,13 @@ if (!this.isRemoved()) { - this.remove(Entity.RemovalReason.KILLED); + // CraftBukkit start - All non-living entities need this -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damageSource, amount, false)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, damage, false)) { + return false; + } + // CraftBukkit end - if (!damageSource.is(DamageTypeTags.IS_EXPLOSION)) { - DamageSource damageSource1 = damageSource.getEntity() != null ? this.damageSources().explosion(this, damageSource.getEntity()) : null; -- level.explode(this, damageSource1, null, this.getX(), this.getY(), this.getZ(), 6.0F, false, Level.ExplosionInteraction.BLOCK); + if (!source.is(DamageTypeTags.IS_EXPLOSION)) { + DamageSource damageSource = source.getEntity() != null ? this.damageSources().explosion(this, source.getEntity()) : null; +- level.explode(this, damageSource, null, this.getX(), this.getY(), this.getZ(), 6.0F, false, Level.ExplosionInteraction.BLOCK); + // CraftBukkit start + org.bukkit.event.entity.ExplosionPrimeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExplosionPrimeEvent(this, 6.0F, false); + if (event.isCancelled()) { @@ -68,10 +68,10 @@ + } + + this.remove(Entity.RemovalReason.KILLED, org.bukkit.event.entity.EntityRemoveEvent.Cause.EXPLODE); // Paper - add Bukkit remove cause -+ level.explode(this, damageSource1, null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.BLOCK); ++ level.explode(this, damageSource, null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.BLOCK); + } else { + this.remove(Entity.RemovalReason.KILLED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DEATH); // Paper - add Bukkit remove cause + // CraftBukkit end } - this.onDestroyedBy(level, damageSource); + this.onDestroyedBy(level, source); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java.patch index 0ccd70609a01..291d4a74687c 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java.patch @@ -6,10 +6,10 @@ private final BinaryHeap openSet = new BinaryHeap(); + // Paper start + private final net.minecraft.world.level.Explosion explosionSource; // Paper - reusable source for CraftTNTPrimed.getSource() -+ @Nullable private BlockPos podium; ++ private @Nullable BlockPos podium; + // Paper end - public EnderDragon(EntityType type, Level level) { + public EnderDragon(final EntityType type, final Level level) { super(EntityType.ENDER_DRAGON, level); @@ -101,6 +_,7 @@ this.setHealth(this.getMaxHealth()); @@ -18,7 +18,7 @@ + this.explosionSource = new net.minecraft.world.level.ServerExplosion(level.getMinecraftWorld(), this, null, null, new Vec3(Double.NaN, Double.NaN, Double.NaN), Float.NaN, true, net.minecraft.world.level.Explosion.BlockInteraction.DESTROY); // Paper } - public void setDragonFight(EndDragonFight dragonFight) { + public void setDragonFight(final EnderDragonFight fight) { @@ -119,6 +_,19 @@ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 200.0).add(Attributes.CAMERA_DISTANCE, 16.0); } @@ -38,17 +38,17 @@ + @Override public boolean isFlapping() { - float cos = Mth.cos(this.flapTime * (float) (Math.PI * 2)); + float flap = Mth.cos(this.flapTime * (Mth.PI * 2.0F)); @@ -210,7 +_,7 @@ } - Vec3 flyTargetLocation = currentPhase.getFlyTargetLocation(); -- if (flyTargetLocation != null) { -+ if (flyTargetLocation != null && currentPhase.getPhase() != EnderDragonPhase.HOVERING) { // CraftBukkit - Don't move when hovering - double d = flyTargetLocation.x - this.getX(); - double d1 = flyTargetLocation.y - this.getY(); - double d2 = flyTargetLocation.z - this.getZ(); -@@ -365,7 +_,12 @@ + Vec3 targetLocation = currentPhase.getFlyTargetLocation(); +- if (targetLocation != null) { ++ if (targetLocation != null && currentPhase.getPhase() != EnderDragonPhase.HOVERING) { // CraftBukkit - Don't move when hovering + double xdd = targetLocation.x - this.getX(); + double ydd = targetLocation.y - this.getY(); + double zdd = targetLocation.z - this.getZ(); +@@ -359,7 +_,12 @@ if (this.nearestCrystal.isRemoved()) { this.nearestCrystal = null; } else if (this.tickCount % 10 == 0 && this.getHealth() < this.getMaxHealth()) { @@ -62,99 +62,100 @@ } } -@@ -395,7 +_,7 @@ - double d2 = entity.getX() - d; - double d3 = entity.getZ() - d1; - double max = Math.max(d2 * d2 + d3 * d3, 0.1); -- entity.push(d2 / max * 4.0, 0.2F, d3 / max * 4.0); -+ entity.push(d2 / max * 4.0, 0.2F, d3 / max * 4.0, this); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent - if (!this.phaseManager.getCurrentPhase().isSitting() && livingEntity.getLastHurtByMobTimestamp() < entity.tickCount - 2) { +@@ -389,7 +_,7 @@ + double xd = entity.getX() - xm; + double zd = entity.getZ() - zm; + double dd = Math.max(xd * xd + zd * zd, 0.1); +- entity.push(xd / dd * 4.0, 0.2F, zd / dd * 4.0); ++ entity.push(xd / dd * 4.0, 0.2F, zd / dd * 4.0, this); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent + if (!this.phaseManager.getCurrentPhase().isSitting() && livingTarget.getLastHurtByMobTimestamp() < entity.tickCount - 2) { DamageSource damageSource = this.damageSources().mobAttack(this); - entity.hurtServer(level, damageSource, 5.0F); -@@ -428,6 +_,7 @@ - int floor5 = Mth.floor(box.maxZ); - boolean flag = false; - boolean flag1 = false; + entity.hurtServer(serverLevel, damageSource, 5.0F); +@@ -422,6 +_,7 @@ + int z1 = Mth.floor(bb.maxZ); + boolean hitWall = false; + boolean destroyedBlock = false; + List destroyedBlocks = new java.util.ArrayList<>(); // Paper - Create a list to hold all the destroyed blocks - for (int i = floor; i <= floor3; i++) { - for (int i1 = floor1; i1 <= floor4; i1++) { -@@ -436,7 +_,11 @@ - BlockState blockState = level.getBlockState(blockPos); - if (!blockState.isAir() && !blockState.is(BlockTags.DRAGON_TRANSPARENT)) { - if (level.getGameRules().get(GameRules.MOB_GRIEFING) && !blockState.is(BlockTags.DRAGON_IMMUNE)) { -- flag1 = level.removeBlock(blockPos, false) || flag1; + for (int x = x0; x <= x1; x++) { + for (int y = y0; y <= y1; y++) { +@@ -430,7 +_,11 @@ + BlockState state = level.getBlockState(blockPos); + if (!state.isAir() && !state.is(BlockTags.DRAGON_TRANSPARENT)) { + if (level.getGameRules().get(GameRules.MOB_GRIEFING) && !state.is(BlockTags.DRAGON_IMMUNE)) { +- destroyedBlock = level.removeBlock(blockPos, false) || destroyedBlock; + // CraftBukkit start - Add blocks to list rather than destroying them + //flag1 = level.removeBlock(blockPos, false) || flag1; -+ flag1 = true; ++ destroyedBlock = true; + destroyedBlocks.add(org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos)); + // CraftBukkit end } else { - flag = true; + hitWall = true; } -@@ -445,6 +_,58 @@ +@@ -439,6 +_,59 @@ } } + // CraftBukkit start - Set off an EntityExplodeEvent for the dragon exploding all these blocks + // SPIGOT-4882: don't fire event if nothing hit -+ if (!flag1) { -+ return flag; ++ if (!destroyedBlock) { ++ return hitWall; + } + + org.bukkit.event.entity.EntityExplodeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityExplodeEvent(this, destroyedBlocks, 0F, this.explosionSource.getBlockInteraction()); + if (event.isCancelled()) { + // This flag literally means 'Dragon hit something hard' (Obsidian, White Stone or Bedrock) and will cause the dragon to slow down. + // We should consider adding an event extension for it, or perhaps returning true if the event is cancelled. -+ return flag; ++ return hitWall; + } else if (event.getYield() == 0F) { + // Yield zero ==> no drops + for (org.bukkit.block.Block block : event.blockList()) { + this.level().removeBlock(new BlockPos(block.getX(), block.getY(), block.getZ()), false); + } + } else { -+ for (org.bukkit.block.Block block : event.blockList()) { -+ org.bukkit.Material blockType = block.getType(); ++ for (org.bukkit.block.Block b : event.blockList()) { ++ org.bukkit.Material blockType = b.getType(); + if (blockType.isAir()) { + continue; + } + -+ org.bukkit.craftbukkit.block.CraftBlock craftBlock = ((org.bukkit.craftbukkit.block.CraftBlock) block); ++ org.bukkit.craftbukkit.block.CraftBlock craftBlock = ((org.bukkit.craftbukkit.block.CraftBlock) b); + BlockPos pos = craftBlock.getPosition(); ++ net.minecraft.world.level.block.state.BlockState state = craftBlock.getBlockState(); ++ net.minecraft.world.level.block.Block block = state.getBlock(); + -+ net.minecraft.world.level.block.Block nmsBlock = craftBlock.getNMS().getBlock(); -+ if (nmsBlock.dropFromExplosion(this.explosionSource)) { -+ net.minecraft.world.level.block.entity.BlockEntity blockEntity = craftBlock.getNMS().hasBlockEntity() ? this.level().getBlockEntity(pos) : null; ++ if (block.dropFromExplosion(this.explosionSource)) { ++ net.minecraft.world.level.block.entity.BlockEntity blockEntity = state.hasBlockEntity() ? this.level().getBlockEntity(pos) : null; + net.minecraft.world.level.storage.loot.LootParams.Builder builder = new net.minecraft.world.level.storage.loot.LootParams.Builder((ServerLevel) this.level()) + .withParameter(net.minecraft.world.level.storage.loot.parameters.LootContextParams.ORIGIN, Vec3.atCenterOf(pos)) + .withParameter(net.minecraft.world.level.storage.loot.parameters.LootContextParams.TOOL, net.minecraft.world.item.ItemStack.EMPTY) + .withParameter(net.minecraft.world.level.storage.loot.parameters.LootContextParams.EXPLOSION_RADIUS, 1.0F / event.getYield()) + .withOptionalParameter(net.minecraft.world.level.storage.loot.parameters.LootContextParams.BLOCK_ENTITY, blockEntity); + -+ craftBlock.getNMS().getDrops(builder).forEach((stack) -> { -+ net.minecraft.world.level.block.Block.popResource(this.level(), pos, stack); ++ state.getDrops(builder).forEach((item) -> { ++ net.minecraft.world.level.block.Block.popResource(this.level(), pos, item); + }); -+ craftBlock.getNMS().spawnAfterBreak((ServerLevel) this.level(), pos, net.minecraft.world.item.ItemStack.EMPTY, false); ++ state.spawnAfterBreak((ServerLevel) this.level(), pos, net.minecraft.world.item.ItemStack.EMPTY, false); + } + // Paper start - TNTPrimeEvent + org.bukkit.block.Block tntBlock = org.bukkit.craftbukkit.block.CraftBlock.at(this.level(), pos); + if (!new com.destroystokyo.paper.event.block.TNTPrimeEvent(tntBlock, com.destroystokyo.paper.event.block.TNTPrimeEvent.PrimeReason.EXPLOSION, explosionSource.getIndirectSourceEntity().getBukkitEntity()).callEvent()) + continue; + // Paper end - TNTPrimeEvent -+ nmsBlock.wasExploded((ServerLevel) this.level(), pos, this.explosionSource); ++ block.wasExploded((ServerLevel) this.level(), pos, this.explosionSource); + + this.level().removeBlock(pos, false); + } + } + // CraftBukkit end + - if (flag1) { - BlockPos blockPos1 = new BlockPos( - floor + this.random.nextInt(floor3 - floor + 1), -@@ -502,7 +_,15 @@ + if (destroyedBlock) { + BlockPos randomPos = new BlockPos( + x0 + this.random.nextInt(x1 - x0 + 1), y0 + this.random.nextInt(y1 - y0 + 1), z0 + this.random.nextInt(z1 - z0 + 1) +@@ -501,7 +_,15 @@ @Override - public void kill(ServerLevel level) { + public void kill(final ServerLevel level) { - this.remove(Entity.RemovalReason.KILLED); + // Paper start - Fire entity death event + this.silentDeath = true; @@ -168,38 +169,38 @@ this.gameEvent(GameEvent.ENTITY_DIE); if (this.dragonFight != null) { this.dragonFight.updateDragon(this); -@@ -524,18 +_,41 @@ - this.level().addParticle(ParticleTypes.EXPLOSION_EMITTER, this.getX() + f, this.getY() + 2.0 + f1, this.getZ() + f2, 0.0, 0.0, 0.0); +@@ -523,18 +_,41 @@ + this.level().addParticle(ParticleTypes.EXPLOSION_EMITTER, this.getX() + xo, this.getY() + 2.0 + yo, this.getZ() + zo, 0.0, 0.0, 0.0); } + // CraftBukkit start - SPIGOT-2420: Moved up to #getExpReward method + /* - int i = 500; + int xpCount = 500; if (this.dragonFight != null && !this.dragonFight.hasPreviouslyKilledDragon()) { - i = 12000; + xpCount = 12000; } + */ -+ int i = this.expToDrop; ++ int xpCount = this.expToDrop; + // CraftBukkit end - if (this.level() instanceof ServerLevel serverLevel) { -- if (this.dragonDeathTime > 150 && this.dragonDeathTime % 5 == 0 && serverLevel.getGameRules().get(GameRules.MOB_DROPS)) { -- ExperienceOrb.award(serverLevel, this.position(), Mth.floor(i * 0.08F)); + if (this.level() instanceof ServerLevel level) { +- if (this.dragonDeathTime > 150 && this.dragonDeathTime % 5 == 0 && level.getGameRules().get(GameRules.MOB_DROPS)) { +- ExperienceOrb.award(level, this.position(), Mth.floor(xpCount * 0.08F)); + if (this.dragonDeathTime > 150 && this.dragonDeathTime % 5 == 0) { // CraftBukkit - SPIGOT-2420: Already checked for the game rule when calculating the xp -+ ExperienceOrb.awardWithDirection(serverLevel, this.position(), net.minecraft.world.phys.Vec3.ZERO, Mth.floor(i * 0.08F), org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, net.minecraft.world.entity.EntityReference.get(this.lastHurtByPlayer, this.level(), Player.class), this); // Paper ++ ExperienceOrb.awardWithDirection(level, this.position(), net.minecraft.world.phys.Vec3.ZERO, Mth.floor(xpCount * 0.08F), org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, net.minecraft.world.entity.EntityReference.get(this.lastHurtByPlayer, this.level(), Player.class), this); // Paper } if (this.dragonDeathTime == 1 && !this.isSilent()) { -- serverLevel.globalLevelEvent(LevelEvent.SOUND_DRAGON_DEATH, this.blockPosition(), 0); +- level.globalLevelEvent(LevelEvent.SOUND_DRAGON_DEATH, this.blockPosition(), 0); + // CraftBukkit start - Use relative location for far away sounds -+ // serverLevel.globalLevelEvent(LevelEvent.SOUND_DRAGON_DEATH, this.blockPosition(), 0); -+ int viewDistance = serverLevel.getCraftServer().getViewDistance() * 16; -+ for (net.minecraft.server.level.ServerPlayer player : serverLevel.getPlayersForGlobalSoundGamerule()) { // Paper - respect global sound events gamerule ++ // level.globalLevelEvent(LevelEvent.SOUND_DRAGON_DEATH, this.blockPosition(), 0); ++ int viewDistance = level.getCraftServer().getViewDistance() * 16; ++ for (net.minecraft.server.level.ServerPlayer player : level.getPlayersForGlobalSoundGamerule()) { // Paper - respect global sound events gamerule + double deltaX = this.getX() - player.getX(); + double deltaZ = this.getZ() - player.getZ(); + double distanceSquared = Mth.square(deltaX) + Mth.square(deltaZ); -+ final double soundRadiusSquared = serverLevel.getGlobalSoundRangeSquared(config -> config.dragonDeathSoundRadius); // Paper - respect global sound events gamerule -+ if (!serverLevel.getGameRules().get(GameRules.GLOBAL_SOUND_EVENTS) && distanceSquared > soundRadiusSquared) continue; // Paper - respect global sound events gamerule ++ final double soundRadiusSquared = level.getGlobalSoundRangeSquared(config -> config.dragonDeathSoundRadius); // Paper - respect global sound events gamerule ++ if (!level.getGameRules().get(GameRules.GLOBAL_SOUND_EVENTS) && distanceSquared > soundRadiusSquared) continue; // Paper - respect global sound events gamerule + if (distanceSquared > Mth.square(viewDistance)) { + double deltaLength = Math.sqrt(distanceSquared); + double relativeX = player.getX() + (deltaX / deltaLength) * viewDistance; @@ -213,14 +214,14 @@ } } -@@ -548,15 +_,15 @@ +@@ -547,15 +_,15 @@ } - if (this.dragonDeathTime == 200 && this.level() instanceof ServerLevel serverLevel1) { -- if (serverLevel1.getGameRules().get(GameRules.MOB_DROPS)) { -- ExperienceOrb.award(serverLevel1, this.position(), Mth.floor(i * 0.2F)); + if (this.dragonDeathTime >= 200 && this.level() instanceof ServerLevel level) { +- if (level.getGameRules().get(GameRules.MOB_DROPS)) { +- ExperienceOrb.award(level, this.position(), Mth.floor(xpCount * 0.2F)); + if (true) { // Paper - SPIGOT-2420: Already checked for the game rule when calculating the xp -+ ExperienceOrb.awardWithDirection(serverLevel1, this.position(), net.minecraft.world.phys.Vec3.ZERO, Mth.floor(i * 0.2F), org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, net.minecraft.world.entity.EntityReference.get(this.lastHurtByPlayer, this.level(), Player.class), this); // Paper ++ ExperienceOrb.awardWithDirection(level, this.position(), net.minecraft.world.phys.Vec3.ZERO, Mth.floor(xpCount * 0.2F), org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, net.minecraft.world.entity.EntityReference.get(this.lastHurtByPlayer, this.level(), Player.class), this); // Paper } if (this.dragonFight != null) { @@ -232,7 +233,7 @@ this.gameEvent(GameEvent.ENTITY_DIE); } } -@@ -737,6 +_,7 @@ +@@ -738,6 +_,7 @@ super.addAdditionalSaveData(output); output.putInt("DragonPhase", this.phaseManager.getCurrentPhase().getPhase().getId()); output.putInt("DragonDeathTime", this.dragonDeathTime); @@ -240,25 +241,25 @@ } @Override -@@ -744,6 +_,7 @@ +@@ -745,6 +_,7 @@ super.readAdditionalSaveData(input); - input.getInt("DragonPhase").ifPresent(integer -> this.phaseManager.setPhase(EnderDragonPhase.getById(integer))); + input.getInt("DragonPhase").ifPresent(phaseId -> this.phaseManager.setPhase(EnderDragonPhase.getById(phaseId))); this.dragonDeathTime = input.getIntOr("DragonDeathTime", 0); + this.expToDrop = input.getIntOr("Bukkit.expToDrop", 0); // CraftBukkit - SPIGOT-2420: The ender dragon drops xp over time which can also happen between server starts } @Override -@@ -784,7 +_,7 @@ - EnderDragonPhase phase = currentPhase.getPhase(); - Vec3 viewVector; +@@ -785,7 +_,7 @@ + EnderDragonPhase phase = phaseInstance.getPhase(); + Vec3 result; if (phase == EnderDragonPhase.LANDING || phase == EnderDragonPhase.TAKEOFF) { -- BlockPos heightmapPos = this.level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.fightOrigin)); -+ BlockPos heightmapPos = this.level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.getPodium()); // Paper - Allow changing the EnderDragon podium - float max = Math.max((float)Math.sqrt(heightmapPos.distToCenterSqr(this.position())) / 4.0F, 1.0F); - float f = 6.0F / max; - float xRot = this.getXRot(); -@@ -871,4 +_,19 @@ - protected float sanitizeScale(float scale) { +- BlockPos egg = this.level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.fightOrigin)); ++ BlockPos egg = this.level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.getPodium()); // Paper - Allow changing the EnderDragon podium + float dist = Math.max((float)Math.sqrt(egg.distToCenterSqr(this.position())) / 4.0F, 1.0F); + float yOffset = 6.0F / dist; + float xRotOld = this.getXRot(); +@@ -872,4 +_,19 @@ + protected float sanitizeScale(final float scale) { return 1.0F; } + @@ -266,14 +267,14 @@ + @Override + public int getExpReward(ServerLevel level, Entity entity) { + // CraftBukkit - Moved from #tickDeath method -+ boolean flag = level.getGameRules().get(GameRules.MOB_DROPS); -+ int i = 500; ++ boolean shouldDrop = level.getGameRules().get(GameRules.MOB_DROPS); ++ int xpCount = 500; + + if (this.dragonFight != null && !this.dragonFight.hasPreviouslyKilledDragon()) { -+ i = 12000; ++ xpCount = 12000; + } + -+ return flag ? i : 0; ++ return shouldDrop ? xpCount : 0; + } + // CraftBukkit end } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonDeathPhase.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonDeathPhase.java.patch index 50c8b1f6f662..72f7f6dcf6e2 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonDeathPhase.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonDeathPhase.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/boss/enderdragon/phases/DragonDeathPhase.java +++ b/net/minecraft/world/entity/boss/enderdragon/phases/DragonDeathPhase.java @@ -33,7 +_,7 @@ - public void doServerTick(ServerLevel level) { + public void doServerTick(final ServerLevel level) { this.time++; if (this.targetLocation == null) { -- BlockPos heightmapPos = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); -+ BlockPos heightmapPos = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium - this.targetLocation = Vec3.atBottomCenterOf(heightmapPos); +- BlockPos egg = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); ++ BlockPos egg = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium + this.targetLocation = Vec3.atBottomCenterOf(egg); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonHoldingPatternPhase.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonHoldingPatternPhase.java.patch index 0ae3e07ca1ce..83f37436200a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonHoldingPatternPhase.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonHoldingPatternPhase.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/entity/boss/enderdragon/phases/DragonHoldingPatternPhase.java @@ -50,7 +_,7 @@ - private void findNewTarget(ServerLevel level) { + private void findNewTarget(final ServerLevel level) { if (this.currentPath != null && this.currentPath.isDone()) { -- BlockPos heightmapPos = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); -+ BlockPos heightmapPos = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium - int i = this.dragon.getDragonFight() == null ? 0 : this.dragon.getDragonFight().getCrystalsAlive(); - if (this.dragon.getRandom().nextInt(i + 3) == 0) { +- BlockPos egg = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); ++ BlockPos egg = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium + int crystals = this.dragon.getDragonFight() == null ? 0 : this.dragon.getDragonFight().aliveCrystals(); + if (this.dragon.getRandom().nextInt(crystals + 3) == 0) { this.dragon.getPhaseManager().setPhase(EnderDragonPhase.LANDING_APPROACH); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingApproachPhase.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingApproachPhase.java.patch index 0abf0d51e715..4821a085de51 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingApproachPhase.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingApproachPhase.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingApproachPhase.java +++ b/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingApproachPhase.java @@ -49,7 +_,7 @@ - private void findNewTarget(ServerLevel level) { + private void findNewTarget(final ServerLevel level) { if (this.currentPath == null || this.currentPath.isDone()) { - int i = this.dragon.findClosestNode(); -- BlockPos heightmapPos = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); -+ BlockPos heightmapPos = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium - Player nearestPlayer = level.getNearestPlayer(NEAR_EGG_TARGETING, this.dragon, heightmapPos.getX(), heightmapPos.getY(), heightmapPos.getZ()); - int i1; - if (nearestPlayer != null) { + int currentNodeIndex = this.dragon.findClosestNode(); +- BlockPos egg = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); ++ BlockPos egg = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium + Player playerNearestToEgg = level.getNearestPlayer(NEAR_EGG_TARGETING, this.dragon, egg.getX(), egg.getY(), egg.getZ()); + int targetNodeIndex; + if (playerNearestToEgg != null) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingPhase.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingPhase.java.patch index dcadba51268e..12f5d4e4c426 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingPhase.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingPhase.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingPhase.java +++ b/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingPhase.java @@ -50,7 +_,7 @@ - public void doServerTick(ServerLevel level) { + public void doServerTick(final ServerLevel level) { if (this.targetLocation == null) { this.targetLocation = Vec3.atBottomCenterOf( - level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())) diff --git a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonSittingFlamingPhase.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonSittingFlamingPhase.java.patch index 42a7a0356203..2c15d57ca37d 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonSittingFlamingPhase.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonSittingFlamingPhase.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/boss/enderdragon/phases/DragonSittingFlamingPhase.java +++ b/net/minecraft/world/entity/boss/enderdragon/phases/DragonSittingFlamingPhase.java -@@ -93,7 +_,13 @@ +@@ -87,7 +_,13 @@ this.flame.setCustomParticle(PowerParticleOption.create(ParticleTypes.DRAGON_BREATH, 1.0F)); this.flame.setPotionDurationScale(0.25F); this.flame.addEffect(new MobEffectInstance(MobEffects.INSTANT_DAMAGE)); @@ -14,7 +14,7 @@ } } -@@ -106,7 +_,7 @@ +@@ -100,7 +_,7 @@ @Override public void end() { if (this.flame != null) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonStrafePlayerPhase.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonStrafePlayerPhase.java.patch index 8a080ca5df82..ec1b61486491 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonStrafePlayerPhase.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonStrafePlayerPhase.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/world/entity/boss/enderdragon/phases/DragonStrafePlayerPhase.java +++ b/net/minecraft/world/entity/boss/enderdragon/phases/DragonStrafePlayerPhase.java -@@ -70,13 +_,16 @@ - double d6 = this.attackTarget.getY(0.5) - d3; - double d7 = this.attackTarget.getZ() - d4; - Vec3 vec32 = new Vec3(d5, d6, d7); +@@ -69,13 +_,16 @@ + double ydd = this.attackTarget.getY(0.5) - startingY; + double zdd = this.attackTarget.getZ() - startingZ; + Vec3 direction = new Vec3(xdd, ydd, zdd); - if (!this.dragon.isSilent()) { + if (false && !this.dragon.isSilent()) { // Paper - EnderDragon Events; Fire after shoot fireball event level.levelEvent(null, LevelEvent.SOUND_DRAGON_FIREBALL, this.dragon.blockPosition(), 0); } - DragonFireball dragonFireball = new DragonFireball(level, this.dragon, vec32.normalize()); - dragonFireball.snapTo(d2, d3, d4, 0.0F, 0.0F); -+ if (new com.destroystokyo.paper.event.entity.EnderDragonShootFireballEvent((org.bukkit.entity.EnderDragon) this.dragon.getBukkitEntity(), (org.bukkit.entity.DragonFireball) dragonFireball.getBukkitEntity()).callEvent()) { // Paper - EnderDragon Events + DragonFireball entity = new DragonFireball(level, this.dragon, direction.normalize()); + entity.snapTo(startingX, startingY, startingZ, 0.0F, 0.0F); ++ if (new com.destroystokyo.paper.event.entity.EnderDragonShootFireballEvent((org.bukkit.entity.EnderDragon) this.dragon.getBukkitEntity(), (org.bukkit.entity.DragonFireball) entity.getBukkitEntity()).callEvent()) { // Paper - EnderDragon Events + if (!this.dragon.isSilent()) level.levelEvent(null, LevelEvent.SOUND_DRAGON_FIREBALL, this.dragon.blockPosition(), 0); // Paper - EnderDragon Events; Fire after shoot fireball event - level.addFreshEntity(dragonFireball); -+ } else dragonFireball.discard(null); // Paper - EnderDragon Events + level.addFreshEntity(entity); ++ } else entity.discard(null); // Paper - EnderDragon Events this.fireballCharge = 0; if (this.currentPath != null) { while (!this.currentPath.isDone()) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonTakeoffPhase.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonTakeoffPhase.java.patch index f58b95dd48cd..909c55e73f90 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonTakeoffPhase.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/DragonTakeoffPhase.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/entity/boss/enderdragon/phases/DragonTakeoffPhase.java @@ -22,7 +_,7 @@ @Override - public void doServerTick(ServerLevel level) { + public void doServerTick(final ServerLevel level) { if (!this.firstTick && this.currentPath != null) { -- BlockPos heightmapPos = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); -+ BlockPos heightmapPos = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium - if (!heightmapPos.closerToCenterThan(this.dragon.position(), 10.0)) { +- BlockPos egg = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); ++ BlockPos egg = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium + if (!egg.closerToCenterThan(this.dragon.position(), 10.0)) { this.dragon.getPhaseManager().setPhase(EnderDragonPhase.HOLDING_PATTERN); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/EnderDragonPhaseManager.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/EnderDragonPhaseManager.java.patch index 41396fd6331f..7024e32b5783 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/EnderDragonPhaseManager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/boss/enderdragon/phases/EnderDragonPhaseManager.java.patch @@ -1,21 +1,27 @@ --- a/net/minecraft/world/entity/boss/enderdragon/phases/EnderDragonPhaseManager.java +++ b/net/minecraft/world/entity/boss/enderdragon/phases/EnderDragonPhaseManager.java -@@ -23,6 +_,18 @@ +@@ -17,11 +_,23 @@ + this.setPhase(EnderDragonPhase.HOVERING); + } + +- public void setPhase(final EnderDragonPhase target) { ++ public void setPhase(EnderDragonPhase target) { // CraftBukkit - Call EnderDragonChangePhaseEvent + if (this.currentPhase == null || target != this.currentPhase.getPhase()) { + if (this.currentPhase != null) { this.currentPhase.end(); } - ++ + // CraftBukkit start - Call EnderDragonChangePhaseEvent + org.bukkit.event.entity.EnderDragonChangePhaseEvent event = new org.bukkit.event.entity.EnderDragonChangePhaseEvent( + (org.bukkit.craftbukkit.entity.CraftEnderDragon) this.dragon.getBukkitEntity(), + (this.currentPhase == null) ? null : org.bukkit.craftbukkit.entity.CraftEnderDragon.getBukkitPhase(this.currentPhase.getPhase()), -+ org.bukkit.craftbukkit.entity.CraftEnderDragon.getBukkitPhase(phase) ++ org.bukkit.craftbukkit.entity.CraftEnderDragon.getBukkitPhase(target) + ); + if (!event.callEvent()) { + return; + } -+ phase = org.bukkit.craftbukkit.entity.CraftEnderDragon.getMinecraftPhase(event.getNewPhase()); ++ target = org.bukkit.craftbukkit.entity.CraftEnderDragon.getMinecraftPhase(event.getNewPhase()); + // CraftBukkit end -+ - this.currentPhase = this.getPhase((EnderDragonPhase)phase); + + this.currentPhase = this.getPhase((EnderDragonPhase)target); if (!this.dragon.level().isClientSide()) { - this.dragon.getEntityData().set(EnderDragon.DATA_PHASE, phase.getId()); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch index fd3e6fe6529c..ecb892c4f6a5 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch @@ -1,17 +1,17 @@ --- a/net/minecraft/world/entity/boss/wither/WitherBoss.java +++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java -@@ -72,6 +_,7 @@ +@@ -74,6 +_,7 @@ private final int[] nextHeadUpdate = new int[2]; private final int[] idleHeadUpdates = new int[2]; private int destroyBlocksTick; + private boolean canPortal = false; // Paper - public final ServerBossEvent bossEvent = (ServerBossEvent)new ServerBossEvent( - this.getDisplayName(), BossEvent.BossBarColor.PURPLE, BossEvent.BossBarOverlay.PROGRESS - ) -@@ -263,15 +_,40 @@ - int i = this.getInvulnerableTicks() - 1; - this.bossEvent.setProgress(1.0F - i / 220.0F); - if (i <= 0) { + public final ServerBossEvent bossEvent = Util.make( + new ServerBossEvent(Mth.createInsecureUUID(this.random), this.getDisplayName(), BossEvent.BossBarColor.PURPLE, BossEvent.BossBarOverlay.PROGRESS), + e -> e.setDarkenScreen(true) +@@ -265,15 +_,40 @@ + int newCount = this.getInvulnerableTicks() - 1; + this.bossEvent.setProgress(1.0F - newCount / 220.0F); + if (newCount <= 0) { - level.explode(this, this.getX(), this.getEyeY(), this.getZ(), 7.0F, false, Level.ExplosionInteraction.MOB); + // CraftBukkit start + org.bukkit.event.entity.ExplosionPrimeEvent event = new org.bukkit.event.entity.ExplosionPrimeEvent(this.getBukkitEntity(), 7.0F, false); @@ -45,34 +45,34 @@ } } - this.setInvulnerableTicks(i); + this.setInvulnerableTicks(newCount); if (this.tickCount % 10 == 0) { - this.heal(10.0F); + this.heal(10.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit } } else { super.customServerAiStep(level); -@@ -308,6 +_,7 @@ +@@ -307,6 +_,7 @@ ); - if (!nearbyEntities.isEmpty()) { - LivingEntity livingEntity1 = nearbyEntities.get(this.random.nextInt(nearbyEntities.size())); -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(this, livingEntity1, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY).isCancelled()) continue; // CraftBukkit - this.setAlternativeTarget(ix, livingEntity1.getId()); + if (!entities.isEmpty()) { + LivingEntity selected = entities.get(this.random.nextInt(entities.size())); ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(this, selected, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY).isCancelled()) continue; // CraftBukkit + this.setAlternativeTarget(i, selected.getId()); } } -@@ -337,6 +_,11 @@ +@@ -336,6 +_,11 @@ )) { - BlockState blockState = level.getBlockState(blockPos); - if (canDestroy(blockState)) { + BlockState state = level.getBlockState(blockPos); + if (canDestroy(state)) { + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockPos, blockState.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockPos, state.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state + continue; + } + // CraftBukkit end - flag = level.destroyBlock(blockPos, true, this) || flag; + destroyed = level.destroyBlock(blockPos, true, this) || destroyed; } } -@@ -348,7 +_,7 @@ +@@ -347,7 +_,7 @@ } if (this.tickCount % 20 == 0) { @@ -81,15 +81,15 @@ } this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth()); -@@ -486,16 +_,16 @@ +@@ -485,16 +_,16 @@ @Override - protected void dropCustomDeathLoot(ServerLevel level, DamageSource damageSource, boolean recentlyHit) { - super.dropCustomDeathLoot(level, damageSource, recentlyHit); -- ItemEntity itemEntity = this.spawnAtLocation(level, Items.NETHER_STAR); -+ ItemEntity itemEntity = this.spawnAtLocation(level, new net.minecraft.world.item.ItemStack(Items.NETHER_STAR), net.minecraft.world.phys.Vec3.ZERO, ItemEntity::setExtendedLifetime); // Paper - Restore vanilla drops behavior; spawnAtLocation returns null so modify the item entity with a consumer - if (itemEntity != null) { -- itemEntity.setExtendedLifetime(); -+ itemEntity.setExtendedLifetime(); // Paper - diff on change + protected void dropCustomDeathLoot(final ServerLevel level, final DamageSource source, final boolean killedByPlayer) { + super.dropCustomDeathLoot(level, source, killedByPlayer); +- ItemEntity netherStar = this.spawnAtLocation(level, Items.NETHER_STAR); ++ ItemEntity netherStar = this.spawnAtLocation(level, new net.minecraft.world.item.ItemStack(Items.NETHER_STAR), net.minecraft.world.phys.Vec3.ZERO, ItemEntity::setExtendedLifetime); // Paper - Restore vanilla drops behavior; spawnAtLocation returns null so modify the item entity with a consumer + if (netherStar != null) { +- netherStar.setExtendedLifetime(); ++ netherStar.setExtendedLifetime(); // Paper - diff on change } } @@ -102,10 +102,10 @@ } else { this.noActionTime = 0; } -@@ -550,12 +_,18 @@ +@@ -549,12 +_,18 @@ @Override - public boolean canUsePortal(boolean allowPassengers) { + public boolean canUsePortal(final boolean ignorePassenger) { - return false; - } + return this.canPortal; // Paper @@ -118,9 +118,9 @@ + // Paper end @Override - public boolean canBeAffected(MobEffectInstance effectInstance) { -- return !effectInstance.is(MobEffects.WITHER) && super.canBeAffected(effectInstance); -+ return (!effectInstance.is(MobEffects.WITHER) || !this.level().paperConfig().entities.mobEffects.immuneToWitherEffect.wither) && super.canBeAffected(effectInstance); + public boolean canBeAffected(final MobEffectInstance newEffect) { +- return !newEffect.is(MobEffects.WITHER) && super.canBeAffected(newEffect); ++ return (!newEffect.is(MobEffects.WITHER) || !this.level().paperConfig().entities.mobEffects.immuneToWitherEffect.wither) && super.canBeAffected(newEffect); } - class WitherDoNothingGoal extends Goal { + private class WitherDoNothingGoal extends Goal { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/decoration/ArmorStand.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/decoration/ArmorStand.java.patch index 8e6296c6ae5b..8a895d311c68 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/decoration/ArmorStand.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/decoration/ArmorStand.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/decoration/ArmorStand.java +++ b/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -86,9 +_,16 @@ +@@ -88,9 +_,16 @@ private boolean invisible = false; public long lastHit; public int disabledSlots = 0; @@ -11,13 +11,13 @@ + private boolean noTickEquipmentDirty = false; + // Paper end - Allow ArmorStands not to tick - public ArmorStand(EntityType type, Level level) { + public ArmorStand(final EntityType type, final Level level) { super(type, level); + if (level != null) this.canTick = level.paperConfig().entities.armorStands.tick; // Paper - Allow ArmorStands not to tick } - public ArmorStand(Level level, double x, double y, double z) { -@@ -100,6 +_,13 @@ + public ArmorStand(final Level level, final double x, final double y, final double z) { +@@ -102,6 +_,13 @@ return createLivingAttributes().add(Attributes.STEP_HEIGHT, 0.0); } @@ -30,23 +30,23 @@ + @Override public void refreshDimensions() { - double x = this.getX(); -@@ -135,6 +_,14 @@ + double oldX = this.getX(); +@@ -137,6 +_,14 @@ return slot != EquipmentSlot.BODY && slot != EquipmentSlot.SADDLE && !this.isDisabled(slot); } -+ // Paper - Allow ArmorStands not to tick; Still update equipment ++ // Paper start - Allow ArmorStands not to tick; Still update equipment + @Override -+ public void setItemSlot(net.minecraft.world.entity.EquipmentSlot slot, ItemStack stack, boolean silent) { ++ public void setItemSlot(EquipmentSlot slot, ItemStack stack, boolean silent) { + super.setItemSlot(slot, stack, silent); + this.noTickEquipmentDirty = true; + } -+ // Paper - Allow ArmorStands not to tick; Still update equipment ++ // Paper end - Allow ArmorStands not to tick; Still update equipment + @Override - protected void addAdditionalSaveData(ValueOutput output) { + protected void addAdditionalSaveData(final ValueOutput output) { super.addAdditionalSaveData(output); -@@ -148,6 +_,7 @@ +@@ -150,6 +_,7 @@ } output.store("Pose", ArmorStand.ArmorStandPose.CODEC, this.getArmorStandPose()); @@ -54,7 +54,7 @@ } @Override -@@ -161,10 +_,16 @@ +@@ -163,10 +_,16 @@ this.setMarker(input.getBooleanOr("Marker", false)); this.noPhysics = !this.hasPhysics(); input.read("Pose", ArmorStand.ArmorStandPose.CODEC).ifPresent(this::setArmorStandPose); @@ -72,7 +72,7 @@ return false; } -@@ -174,6 +_,7 @@ +@@ -176,6 +_,7 @@ @Override protected void pushEntities() { @@ -80,155 +80,147 @@ for (Entity entity : this.level().getEntities(this, this.getBoundingBox(), RIDABLE_MINECARTS)) { if (this.distanceToSqr(entity) <= 0.2) { entity.push(this); -@@ -246,7 +_,25 @@ +@@ -248,7 +_,25 @@ return false; - } else if (itemBySlot.isEmpty() && (this.disabledSlots & 1 << slot.getFilterBit(16)) != 0) { + } else if (itemStack.isEmpty() && (this.disabledSlots & 1 << slot.getFilterBit(16)) != 0) { return false; -- } else if (player.hasInfiniteMaterials() && itemBySlot.isEmpty() && !stack.isEmpty()) { -+ // CraftBukkit start -+ } else { -+ org.bukkit.inventory.ItemStack armorStandItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemBySlot); -+ org.bukkit.inventory.ItemStack playerHeldItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack); +- } else if (player.hasInfiniteMaterials() && itemStack.isEmpty() && !playerItemStack.isEmpty()) { ++ } + -+ org.bukkit.entity.Player player1 = (org.bukkit.entity.Player) player.getBukkitEntity(); -+ org.bukkit.entity.ArmorStand self = (org.bukkit.entity.ArmorStand) this.getBukkitEntity(); ++ // CraftBukkit start ++ org.bukkit.inventory.ItemStack armorStandItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); ++ org.bukkit.inventory.ItemStack playerHeldItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(playerItemStack); + -+ org.bukkit.inventory.EquipmentSlot slot1 = org.bukkit.craftbukkit.CraftEquipmentSlot.getSlot(slot); -+ org.bukkit.inventory.EquipmentSlot hand1 = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand); -+ org.bukkit.event.player.PlayerArmorStandManipulateEvent armorStandManipulateEvent = new org.bukkit.event.player.PlayerArmorStandManipulateEvent(player1, self, playerHeldItem, armorStandItem, slot1, hand1); -+ this.level().getCraftServer().getPluginManager().callEvent(armorStandManipulateEvent); ++ org.bukkit.entity.Player player1 = (org.bukkit.entity.Player) player.getBukkitEntity(); ++ org.bukkit.entity.ArmorStand self = (org.bukkit.entity.ArmorStand) this.getBukkitEntity(); + -+ if (armorStandManipulateEvent.isCancelled()) { -+ return true; -+ } ++ org.bukkit.inventory.EquipmentSlot slot1 = org.bukkit.craftbukkit.CraftEquipmentSlot.getSlot(slot); ++ org.bukkit.inventory.EquipmentSlot hand1 = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand); ++ org.bukkit.event.player.PlayerArmorStandManipulateEvent armorStandManipulateEvent = new org.bukkit.event.player.PlayerArmorStandManipulateEvent(player1, self, playerHeldItem, armorStandItem, slot1, hand1); ++ this.level().getCraftServer().getPluginManager().callEvent(armorStandManipulateEvent); + -+ if (player.hasInfiniteMaterials() && itemBySlot.isEmpty() && !stack.isEmpty()) { -+ // CraftBukkit end - this.setItemSlot(slot, stack.copyWithCount(1)); - return true; - } else if (stack.isEmpty() || stack.getCount() <= 1) { -@@ -259,6 +_,7 @@ - this.setItemSlot(slot, stack.split(1)); ++ if (armorStandManipulateEvent.isCancelled()) { ++ return true; ++ } ++ // CraftBukkit end ++ if (player.hasInfiniteMaterials() && itemStack.isEmpty() && !playerItemStack.isEmpty()) { + this.setItemSlot(slot, playerItemStack.copyWithCount(1)); return true; - } -+ } // CraftBukkit - } - - @Override -@@ -268,15 +_,32 @@ - } else if (!level.getGameRules().get(GameRules.MOB_GRIEFING) && damageSource.getEntity() instanceof Mob) { + } else if (playerItemStack.isEmpty() || playerItemStack.getCount() <= 1) { +@@ -270,15 +_,32 @@ + } else if (!level.getGameRules().get(GameRules.MOB_GRIEFING) && source.getEntity() instanceof Mob) { return false; - } else if (damageSource.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) { + } else if (source.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) { - this.kill(level); + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damageSource, amount)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, damage)) { + return false; + } -+ this.kill(level, damageSource); // CraftBukkit ++ this.kill(level, source); // CraftBukkit + // CraftBukkit end return false; -- } else if (this.isInvulnerableTo(level, damageSource) || this.invisible || this.isMarker()) { -+ } else if (this.isInvulnerableTo(level, damageSource) /*|| this.invisible*/ || this.isMarker()) { // CraftBukkit +- } else if (this.isInvulnerableTo(level, source) || this.invisible || this.isMarker()) { ++ } else if (this.isInvulnerableTo(level, source) /*|| this.invisible*/ || this.isMarker()) { // CraftBukkit return false; - } else if (damageSource.is(DamageTypeTags.IS_EXPLOSION)) { -- this.brokenByAnything(level, damageSource); + } else if (source.is(DamageTypeTags.IS_EXPLOSION)) { +- this.brokenByAnything(level, source); - this.kill(level); + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damageSource, amount, true, this.invisible)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, damage, true, this.invisible)) { + return false; + } + // CraftBukkit end + // Paper start - avoid duplicate event call -+ org.bukkit.event.entity.EntityDeathEvent event = this.brokenByAnything(level, damageSource); -+ if (!event.isCancelled()) this.kill(level, damageSource, false); // CraftBukkit ++ org.bukkit.event.entity.EntityDeathEvent event = this.brokenByAnything(level, source); ++ if (!event.isCancelled()) this.kill(level, source, false); // CraftBukkit + // Paper end return false; - } else if (damageSource.is(DamageTypeTags.IGNITES_ARMOR_STANDS)) { + } else if (source.is(DamageTypeTags.IGNITES_ARMOR_STANDS)) { + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damageSource, amount, true, this.invisible)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, damage, true, this.invisible)) { + return false; + } + // CraftBukkit end if (this.isOnFire()) { - this.causeDamage(level, damageSource, 0.15F); + this.causeDamage(level, source, 0.15F); } else { -@@ -285,9 +_,19 @@ +@@ -287,9 +_,19 @@ return false; - } else if (damageSource.is(DamageTypeTags.BURNS_ARMOR_STANDS) && this.getHealth() > 0.5F) { + } else if (source.is(DamageTypeTags.BURNS_ARMOR_STANDS) && this.getHealth() > 0.5F) { + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damageSource, amount, true, this.invisible)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, damage, true, this.invisible)) { + return false; + } + // CraftBukkit end - this.causeDamage(level, damageSource, 4.0F); + this.causeDamage(level, source, 4.0F); return false; } else { + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damageSource, amount, true, this.invisible)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, damage, true, this.invisible)) { + return false; + } + // CraftBukkit end - boolean isCanBreakArmorStand = damageSource.is(DamageTypeTags.CAN_BREAK_ARMOR_STAND); - boolean isAlwaysKillsArmorStands = damageSource.is(DamageTypeTags.ALWAYS_KILLS_ARMOR_STANDS); - if (!isCanBreakArmorStand && !isAlwaysKillsArmorStands) { -@@ -297,7 +_,7 @@ - } else if (damageSource.isCreativePlayer()) { + boolean allowIncrementalBreaking = source.is(DamageTypeTags.CAN_BREAK_ARMOR_STAND); + boolean shouldKill = source.is(DamageTypeTags.ALWAYS_KILLS_ARMOR_STANDS); + if (!allowIncrementalBreaking && !shouldKill) { +@@ -299,7 +_,7 @@ + } else if (source.isCreativePlayer()) { this.playBrokenSound(); this.showBreakingParticles(); - this.kill(level); -+ this.kill(level, damageSource); // CraftBukkit ++ this.kill(level, source); // CraftBukkit return true; } else { - long gameTime = level.getGameTime(); -@@ -306,9 +_,9 @@ - this.gameEvent(GameEvent.ENTITY_DAMAGE, damageSource.getEntity()); - this.lastHit = gameTime; + long time = level.getGameTime(); +@@ -308,9 +_,9 @@ + this.gameEvent(GameEvent.ENTITY_DAMAGE, source.getEntity()); + this.lastHit = time; } else { -- this.brokenByPlayer(level, damageSource); -+ org.bukkit.event.entity.EntityDeathEvent event = this.brokenByPlayer(level, damageSource); // Paper +- this.brokenByPlayer(level, source); ++ org.bukkit.event.entity.EntityDeathEvent event = this.brokenByPlayer(level, source); // Paper this.showBreakingParticles(); - this.kill(level); -+ if (!event.isCancelled()) this.kill(level, damageSource, false); // Paper - we still need to kill to follow vanilla logic (emit the game event etc...) ++ if (!event.isCancelled()) this.kill(level, source, false); // Paper - we still need to kill to follow vanilla logic (emit the game event etc...) } return true; -@@ -360,31 +_,42 @@ +@@ -362,31 +_,42 @@ float health = this.getHealth(); - health -= damageAmount; + health -= dmg; if (health <= 0.5F) { -- this.brokenByAnything(level, damageSource); +- this.brokenByAnything(level, source); - this.kill(level); + // Paper start - avoid duplicate event call -+ org.bukkit.event.entity.EntityDeathEvent event = this.brokenByAnything(level, damageSource); -+ if (!event.isCancelled()) this.kill(level, damageSource, false); // CraftBukkit ++ org.bukkit.event.entity.EntityDeathEvent event = this.brokenByAnything(level, source); ++ if (!event.isCancelled()) this.kill(level, source, false); // CraftBukkit + // Paper end } else { this.setHealth(health); - this.gameEvent(GameEvent.ENTITY_DAMAGE, damageSource.getEntity()); + this.gameEvent(GameEvent.ENTITY_DAMAGE, source.getEntity()); } } -- private void brokenByPlayer(ServerLevel level, DamageSource damageSource) { -+ private org.bukkit.event.entity.EntityDeathEvent brokenByPlayer(ServerLevel level, DamageSource damageSource) { // Paper - ItemStack itemStack = new ItemStack(Items.ARMOR_STAND); - itemStack.set(DataComponents.CUSTOM_NAME, this.getCustomName()); -- Block.popResource(this.level(), this.blockPosition(), itemStack); -- this.brokenByAnything(level, damageSource); -+ this.drops.add(new DefaultDrop(itemStack, stack -> Block.popResource(this.level(), this.blockPosition(), stack))); // CraftBukkit - add to drops // Paper - Restore vanilla drops behavior -+ return this.brokenByAnything(level, damageSource); // Paper +- private void brokenByPlayer(final ServerLevel level, final DamageSource source) { ++ private org.bukkit.event.entity.EntityDeathEvent brokenByPlayer(final ServerLevel level, final DamageSource source) { // Paper + ItemStack result = new ItemStack(Items.ARMOR_STAND); + result.set(DataComponents.CUSTOM_NAME, this.getCustomName()); +- Block.popResource(this.level(), this.blockPosition(), result); +- this.brokenByAnything(level, source); ++ this.drops.add(new DefaultDrop(result, stack -> Block.popResource(this.level(), this.blockPosition(), stack))); // CraftBukkit - add to drops // Paper - Restore vanilla drops behavior ++ return this.brokenByAnything(level, source); // Paper } -- private void brokenByAnything(ServerLevel level, DamageSource damageSource) { -+ private org.bukkit.event.entity.EntityDeathEvent brokenByAnything(ServerLevel level, DamageSource damageSource) { // Paper +- private void brokenByAnything(final ServerLevel level, final DamageSource source) { ++ private org.bukkit.event.entity.EntityDeathEvent brokenByAnything(final ServerLevel level, final DamageSource source) { // Paper this.playBrokenSound(); -- this.dropAllDeathLoot(level, damageSource); -+ // this.dropAllDeathLoot(level, damageSource); // CraftBukkit - moved down +- this.dropAllDeathLoot(level, source); ++ // this.dropAllDeathLoot(level, source); // CraftBukkit - moved down - for (EquipmentSlot equipmentSlot : EquipmentSlot.VALUES) { -- ItemStack itemStack = this.equipment.set(equipmentSlot, ItemStack.EMPTY); -+ ItemStack itemStack = this.equipment.get(equipmentSlot); // Paper - move equipment removal past event call - if (!itemStack.isEmpty()) { + for (EquipmentSlot slot : EquipmentSlot.VALUES) { +- ItemStack itemStack = this.equipment.set(slot, ItemStack.EMPTY); ++ ItemStack itemStack = this.equipment.get(slot); // Paper - move equipment removal past event call + if (!itemStack.isEmpty() && !EnchantmentHelper.has(itemStack, EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP)) { - Block.popResource(this.level(), this.blockPosition().above(), itemStack); - } - } @@ -236,10 +228,10 @@ + } + } + // Paper start - move equipment removal past event call -+ org.bukkit.event.entity.EntityDeathEvent event = this.dropAllDeathLoot(level, damageSource); ++ org.bukkit.event.entity.EntityDeathEvent event = this.dropAllDeathLoot(level, source); + if (!event.isCancelled()) { -+ for (EquipmentSlot equipmentSlot : EquipmentSlot.VALUES) { -+ this.equipment.set(equipmentSlot, ItemStack.EMPTY); ++ for (EquipmentSlot slot : EquipmentSlot.VALUES) { ++ this.equipment.set(slot, ItemStack.EMPTY); + } + } + return event; @@ -247,7 +239,7 @@ } private void playBrokenSound() { -@@ -432,9 +_,40 @@ +@@ -434,9 +_,40 @@ return this.isSmall(); } @@ -267,20 +259,20 @@ + // Paper end - Allow ArmorStands not to tick + @Override - public void kill(ServerLevel level) { + public void kill(final ServerLevel level) { - this.remove(Entity.RemovalReason.KILLED); + // CraftBukkit start - pass DamageSource for kill + this.kill(level, null); + } + -+ public void kill(ServerLevel level, @Nullable DamageSource damageSource) { ++ public void kill(final ServerLevel level, @Nullable final DamageSource source) { + // Paper start - make cancellable -+ this.kill(level, damageSource, true); ++ this.kill(level, source, true); + } + -+ public void kill(ServerLevel level, @Nullable DamageSource damageSource, boolean callEvent) { ++ public void kill(final ServerLevel level, @Nullable final DamageSource source, final boolean callEvent) { + if (callEvent) { -+ org.bukkit.event.entity.EntityDeathEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDeathEvent(this, (damageSource == null ? this.damageSources().genericKill() : damageSource), this.drops); // CraftBukkit - call event ++ org.bukkit.event.entity.EntityDeathEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDeathEvent(this, (source == null ? this.damageSources().genericKill() : source), this.drops); // CraftBukkit - call event + if (event.isCancelled()) return; + } + // Paper end @@ -290,7 +282,7 @@ } @@ -683,4 +_,13 @@ - .apply(instance, ArmorStand.ArmorStandPose::new) + .apply(i, ArmorStand.ArmorStandPose::new) ); } + diff --git a/paper-server/patches/sources/net/minecraft/world/entity/decoration/BlockAttachedEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/decoration/BlockAttachedEntity.java.patch index 85ea8f71868c..c5fbc04fd63a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/decoration/BlockAttachedEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/decoration/BlockAttachedEntity.java.patch @@ -8,10 +8,10 @@ + private int checkInterval; { this.checkInterval = this.getId() % this.level().spigotConfig.hangingTickFrequency; } // Paper - Perf: offset item frame ticking protected BlockPos pos; - protected BlockAttachedEntity(EntityType type, Level level) { + protected BlockAttachedEntity(final EntityType type, final Level level) { @@ -39,10 +_,26 @@ public void tick() { - if (this.level() instanceof ServerLevel serverLevel) { + if (this.level() instanceof ServerLevel level) { this.checkBelowWorld(); - if (this.checkInterval++ == 100) { + if (this.checkInterval++ == this.level().spigotConfig.hangingTickFrequency) { // Spigot @@ -35,7 +35,7 @@ + } + // CraftBukkit end + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause - this.dropItem(serverLevel, null); + this.dropItem(level, null); } } @@ -75,6 +_,21 @@ @@ -43,12 +43,12 @@ } else { if (!this.isRemoved()) { + // CraftBukkit start - fire break events -+ Entity damager = (!damageSource.isDirect() && damageSource.getEntity() != null) ? damageSource.getEntity() : damageSource.getDirectEntity(); // Paper - fix DamageSource API ++ Entity damager = (!source.isDirect() && source.getEntity() != null) ? source.getEntity() : source.getDirectEntity(); // Paper - fix DamageSource API + org.bukkit.event.hanging.HangingBreakEvent event; + if (damager != null) { -+ event = new org.bukkit.event.hanging.HangingBreakByEntityEvent((org.bukkit.entity.Hanging) this.getBukkitEntity(), damager.getBukkitEntity(), damageSource.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION) ? org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.EXPLOSION : org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.ENTITY); ++ event = new org.bukkit.event.hanging.HangingBreakByEntityEvent((org.bukkit.entity.Hanging) this.getBukkitEntity(), damager.getBukkitEntity(), source.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION) ? org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.EXPLOSION : org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.ENTITY); + } else { -+ event = new org.bukkit.event.hanging.HangingBreakEvent((org.bukkit.entity.Hanging) this.getBukkitEntity(), damageSource.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION) ? org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.EXPLOSION : org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.DEFAULT); ++ event = new org.bukkit.event.hanging.HangingBreakEvent((org.bukkit.entity.Hanging) this.getBukkitEntity(), source.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION) ? org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.EXPLOSION : org.bukkit.event.hanging.HangingBreakEvent.RemoveCause.DEFAULT); + } + + this.level().getCraftServer().getPluginManager().callEvent(event); @@ -59,21 +59,21 @@ + // CraftBukkit end this.kill(level); this.markHurt(); - this.dropItem(level, damageSource.getEntity()); + this.dropItem(level, source.getEntity()); @@ -93,18 +_,36 @@ @Override - public void move(MoverType type, Vec3 movement) { - if (this.level() instanceof ServerLevel serverLevel && !this.isRemoved() && movement.lengthSqr() > 0.0) { -- this.kill(serverLevel); -- this.dropItem(serverLevel, null); + public void move(final MoverType moverType, final Vec3 delta) { + if (this.level() instanceof ServerLevel level && !this.isRemoved() && delta.lengthSqr() > 0.0) { +- this.kill(level); +- this.dropItem(level, null); - } - } - - @Override -- public void push(double x, double y, double z) { -- if (this.level() instanceof ServerLevel serverLevel && !this.isRemoved() && x * x + y * y + z * z > 0.0) { -- this.kill(serverLevel); -- this.dropItem(serverLevel, null); +- public void push(final double xa, final double ya, final double za) { +- if (this.level() instanceof ServerLevel level && !this.isRemoved() && xa * xa + ya * ya + za * za > 0.0) { +- this.kill(level); +- this.dropItem(level, null); - } - } + // CraftBukkit start - fire break events @@ -85,16 +85,16 @@ + return; + } + // CraftBukkit end -+ this.kill(serverLevel); -+ this.dropItem(serverLevel, null); ++ this.kill(level); ++ this.dropItem(level, null); + } + } + + @Override -+ public void push(double x, double y, double z, @Nullable Entity pushingEntity) { // Paper - override correct overload -+ if (false && this.level() instanceof ServerLevel serverLevel && !this.isRemoved() && x * x + y * y + z * z > 0.0) { // CraftBukkit - not needed -+ this.kill(serverLevel); -+ this.dropItem(serverLevel, null); ++ public void push(final double xa, final double ya, final double za, @Nullable final Entity pushingEntity) { // Paper - override correct overload ++ if (false && this.level() instanceof ServerLevel level && !this.isRemoved() && xa * xa + ya * ya + za * za > 0.0) { // CraftBukkit - not needed ++ this.kill(level); ++ this.dropItem(level, null); + } + } + @@ -108,4 +108,4 @@ + // CraftBukkit end @Override - protected void addAdditionalSaveData(ValueOutput output) { + protected void addAdditionalSaveData(final ValueOutput output) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/decoration/ItemFrame.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/decoration/ItemFrame.java.patch index dc67cbaad527..4af212abab10 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/decoration/ItemFrame.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/decoration/ItemFrame.java.patch @@ -6,52 +6,52 @@ public boolean fixed = false; + public @Nullable MapId cachedMapId; // Paper - Perf: Cache map ids on item frames - public ItemFrame(EntityType type, Level level) { + public ItemFrame(final EntityType type, final Level level) { super(type, level); @@ -111,6 +_,11 @@ } - private AABB createBoundingBox(BlockPos pos, Direction direction, boolean hasFramedMap) { + private AABB createBoundingBox(final BlockPos blockPos, final Direction direction, final boolean hasFramedMap) { + // Paper start - add static method -+ return createBoundingBoxStatic(pos, direction, hasFramedMap); ++ return createBoundingBoxStatic(blockPos, direction, hasFramedMap); + } -+ public static AABB createBoundingBoxStatic(BlockPos pos, Direction direction, boolean hasFramedMap) { ++ public static AABB createBoundingBoxStatic(BlockPos blockPos, Direction direction, boolean hasFramedMap) { + // Paper end - add static method - float f = 0.46875F; - Vec3 vec3 = Vec3.atCenterOf(pos).relative(direction, -0.46875); - float f1 = hasFramedMap ? 1.0F : 0.75F; + float shiftToBlockWall = 0.46875F; + Vec3 position = Vec3.atCenterOf(blockPos).relative(direction, -0.46875); + float width = hasFramedMap ? 1.0F : 0.75F; @@ -142,9 +_,9 @@ } @Override -- public void push(double x, double y, double z) { -+ public void push(double x, double y, double z, @Nullable Entity pushingEntity) { // Paper - add push source entity param +- public void push(final double xa, final double ya, final double za) { ++ public void push(final double xa, final double ya, final double za, @Nullable Entity pushingEntity) { // Paper - add push source entity param if (!this.fixed) { -- super.push(x, y, z); -+ super.push(x, y, z, pushingEntity); // Paper - add push source entity param +- super.push(xa, ya, za); ++ super.push(xa, ya, za, pushingEntity); // Paper - add push source entity param } } @@ -173,6 +_,18 @@ - if (this.isInvulnerableToBase(damageSource)) { + if (this.isInvulnerableToBase(source)) { return false; - } else if (this.shouldDamageDropItem(damageSource)) { + } else if (this.shouldDamageDropItem(source)) { + // CraftBukkit start - fire EntityDamageEvent -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damageSource, amount, false) || this.isRemoved()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, damage, false) || this.isRemoved()) { + return true; + } + // CraftBukkit end + // Paper start - Add PlayerItemFrameChangeEvent -+ if (damageSource.getEntity() instanceof Player player) { ++ if (source.getEntity() instanceof Player player) { + var event = new io.papermc.paper.event.player.PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), this.getItem().asBukkitCopy(), io.papermc.paper.event.player.PlayerItemFrameChangeEvent.ItemFrameChangeAction.REMOVE); + if (!event.callEvent()) return true; // return true here because you aren't cancelling the damage, just the change + this.setItem(ItemStack.fromBukkitCopy(event.getItemStack()), false); + } + // Paper end - Add PlayerItemFrameChangeEvent - this.dropItem(level, damageSource.getEntity(), false); - this.gameEvent(GameEvent.BLOCK_CHANGE, damageSource.getEntity()); + this.dropItem(level, source.getEntity(), false); + this.gameEvent(GameEvent.BLOCK_CHANGE, source.getEntity()); this.playSound(this.getRemoveItemSound(), 1.0F, 1.0F); -@@ -258,6 +_,13 @@ +@@ -256,6 +_,13 @@ return this.getEntityData().get(DATA_ITEM); } @@ -62,54 +62,54 @@ + } + // Paper end + - public @Nullable MapId getFramedMapId(ItemStack stack) { - return stack.get(DataComponents.MAP_ID); + public @Nullable MapId getFramedMapId(final ItemStack itemStack) { + return itemStack.get(DataComponents.MAP_ID); } -@@ -271,13 +_,19 @@ +@@ -269,13 +_,19 @@ } - public void setItem(ItemStack stack, boolean updateNeighbours) { + public void setItem(ItemStack itemStack, final boolean updateNeighbours) { + // CraftBukkit start -+ this.setItem(stack, updateNeighbours, true); ++ this.setItem(itemStack, updateNeighbours, true); + } + -+ public void setItem(ItemStack stack, boolean updateNeighbours, boolean playSound) { ++ public void setItem(ItemStack itemStack, final boolean updateNeighbours, boolean playSound) { + // CraftBukkit end - if (!stack.isEmpty()) { - stack = stack.copyWithCount(1); + if (!itemStack.isEmpty()) { + itemStack = itemStack.copyWithCount(1); } - this.onItemChanged(stack); - this.getEntityData().set(DATA_ITEM, stack); -- if (!stack.isEmpty()) { -+ if (!stack.isEmpty() && updateNeighbours && playSound) { // CraftBukkit // Paper - only play sound when update flag is set + this.onItemChanged(itemStack); + this.getEntityData().set(DATA_ITEM, itemStack); +- if (!itemStack.isEmpty()) { ++ if (!itemStack.isEmpty() && updateNeighbours && playSound) { // CraftBukkit // Paper - only play sound when update flag is set this.playSound(this.getAddItemSound(), 1.0F, 1.0F); } -@@ -304,6 +_,7 @@ +@@ -302,6 +_,7 @@ } - private void onItemChanged(ItemStack item) { + private void onItemChanged(final ItemStack item) { + this.cachedMapId = item.getComponents().get(net.minecraft.core.component.DataComponents.MAP_ID); // Paper - Perf: Cache map ids on item frames - if (!item.isEmpty() && item.getFrame() != this) { - item.setEntityRepresentation(this); - } -@@ -372,7 +_,13 @@ - if (savedData != null && savedData.isTrackedCountOverLimit(256)) { + this.recalculateBoundingBox(); + } + +@@ -366,7 +_,13 @@ + if (data != null && data.isTrackedCountOverLimit(256)) { return InteractionResult.FAIL; } else { -- this.setItem(itemInHand); +- this.setItem(itemStack); + // Paper start - Add PlayerItemFrameChangeEvent -+ io.papermc.paper.event.player.PlayerItemFrameChangeEvent event = new io.papermc.paper.event.player.PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), itemInHand.asBukkitCopy(), io.papermc.paper.event.player.PlayerItemFrameChangeEvent.ItemFrameChangeAction.PLACE); ++ io.papermc.paper.event.player.PlayerItemFrameChangeEvent event = new io.papermc.paper.event.player.PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), itemStack.asBukkitCopy(), io.papermc.paper.event.player.PlayerItemFrameChangeEvent.ItemFrameChangeAction.PLACE); + if (!event.callEvent()) { + return InteractionResult.FAIL; + } + this.setItem(ItemStack.fromBukkitCopy(event.getItemStack())); + // Paper end - Add PlayerItemFrameChangeEvent this.gameEvent(GameEvent.BLOCK_CHANGE, player); - itemInHand.consume(1, player); + itemStack.consume(1, player); return InteractionResult.SUCCESS; -@@ -381,6 +_,13 @@ +@@ -375,6 +_,13 @@ return InteractionResult.PASS; } } else { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java.patch index f89a67b2afb9..de163b618697 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java.patch @@ -1,51 +1,29 @@ --- a/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java +++ b/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java -@@ -82,7 +_,7 @@ - boolean flag = false; +@@ -85,7 +_,7 @@ + boolean attachedMob = false; for (Leashable leashable : Leashable.leashableLeashedTo(player)) { - if (leashable.canHaveALeashAttachedTo(this)) { + if (leashable.canHaveALeashAttachedTo(this) && org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerLeashEntityEvent(leashable, this, player, hand)) { // Paper - leash event leashable.setLeashedTo(this, true); - flag = true; + attachedMob = true; } -@@ -91,7 +_,7 @@ - boolean flag1 = false; - if (!flag && !player.isSecondaryUseActive()) { - for (Leashable leashable1 : Leashable.leashableLeashedTo(this)) { -- if (leashable1.canHaveALeashAttachedTo(player)) { -+ if (leashable1.canHaveALeashAttachedTo(player) && org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerLeashEntityEvent(leashable1, player, player, hand)) { // Paper - leash event - leashable1.setLeashedTo(player, true); - flag1 = true; +@@ -94,7 +_,7 @@ + boolean anyDropped = false; + if (!attachedMob && !player.isSecondaryUseActive()) { + for (Leashable mob : Leashable.leashableLeashedTo(this)) { +- if (mob.canHaveALeashAttachedTo(player)) { ++ if (mob.canHaveALeashAttachedTo(player) && org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerLeashEntityEvent(mob, player, player, hand)) { // Paper - leash event + mob.setLeashedTo(player, true); + anyDropped = true; } -@@ -111,7 +_,7 @@ +@@ -114,7 +_,7 @@ @Override - public void notifyLeasheeRemoved(Leashable leashHolder) { + public void notifyLeasheeRemoved(final Leashable entity) { if (Leashable.leashableLeashedTo(this).isEmpty()) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause } } -@@ -120,7 +_,12 @@ - return this.level().getBlockState(this.pos).is(BlockTags.FENCES); - } - -+ // Paper start - Track if a knot was created - public static LeashFenceKnotEntity getOrCreateKnot(Level level, BlockPos pos) { -+ return getOrCreateKnot(level, pos, null); -+ } -+ public static LeashFenceKnotEntity getOrCreateKnot(Level level, BlockPos pos, org.apache.commons.lang3.mutable.@Nullable MutableBoolean created) { -+ // Paper end - Track if a knot was created - int x = pos.getX(); - int y = pos.getY(); - int z = pos.getZ(); -@@ -134,7 +_,7 @@ - } - - LeashFenceKnotEntity leashFenceKnotEntity1 = new LeashFenceKnotEntity(level, pos); -- level.addFreshEntity(leashFenceKnotEntity1); -+ if (level.addFreshEntity(leashFenceKnotEntity1) && created != null) { created.setTrue(); } // Paper - Track if a knot was created - return leashFenceKnotEntity1; - } - diff --git a/paper-server/patches/sources/net/minecraft/world/entity/decoration/Mannequin.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/decoration/Mannequin.java.patch index 4fe03af5443c..8b0c9da412b1 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/decoration/Mannequin.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/decoration/Mannequin.java.patch @@ -3,7 +3,7 @@ @@ -33,7 +_,7 @@ Mannequin.class, EntityDataSerializers.OPTIONAL_COMPONENT ); - public static final byte ALL_LAYERS = (byte)Arrays.stream(PlayerModelPart.values()).mapToInt(PlayerModelPart::getMask).reduce(0, (i, i1) -> i | i1); + public static final byte ALL_LAYERS = (byte)Arrays.stream(PlayerModelPart.values()).mapToInt(PlayerModelPart::getMask).reduce(0, (a, b) -> a | b); - private static final Set VALID_POSES = Set.of(Pose.STANDING, Pose.CROUCHING, Pose.SWIMMING, Pose.FALL_FLYING, Pose.SLEEPING); + public static final Set VALID_POSES = Set.of(Pose.STANDING, Pose.CROUCHING, Pose.SWIMMING, Pose.FALL_FLYING, Pose.SLEEPING); // Paper - public public static final Codec POSE_CODEC = Pose.CODEC diff --git a/paper-server/patches/sources/net/minecraft/world/entity/decoration/painting/Painting.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/decoration/painting/Painting.java.patch index d7108d5cf4ec..07d6a2cb048e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/decoration/painting/Painting.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/decoration/painting/Painting.java.patch @@ -3,39 +3,39 @@ @@ -149,21 +_,31 @@ @Override - protected AABB calculateBoundingBox(BlockPos pos, Direction direction) { + protected AABB calculateBoundingBox(final BlockPos pos, final Direction direction) { + // CraftBukkit start + PaintingVariant variant = (PaintingVariant) this.getVariant().value(); + return Painting.calculateBoundingBoxStatic(pos, direction, variant.width(), variant.height()); + } + -+ public static AABB calculateBoundingBoxStatic(BlockPos pos, Direction direction, int width, int height) { ++ public static AABB calculateBoundingBoxStatic(final BlockPos pos, final Direction direction, final int width, final int height) { + // CraftBukkit end - float f = 0.46875F; - Vec3 vec3 = Vec3.atCenterOf(pos).relative(direction, -0.46875); -- PaintingVariant paintingVariant = this.getVariant().value(); -- double d = this.offsetForPaintingSize(paintingVariant.width()); -- double d1 = this.offsetForPaintingSize(paintingVariant.height()); + float shiftToBlockWall = 0.46875F; + Vec3 attachedToWall = Vec3.atCenterOf(pos).relative(direction, -0.46875); +- PaintingVariant variant = this.getVariant().value(); +- double horizontalOffset = this.offsetForPaintingSize(variant.width()); +- double verticalOffset = this.offsetForPaintingSize(variant.height()); + // CraftBukkit start -+ double d = Painting.offsetForPaintingSize(width); -+ double d1 = Painting.offsetForPaintingSize(height); ++ double horizontalOffset = Painting.offsetForPaintingSize(width); ++ double verticalOffset = Painting.offsetForPaintingSize(height); + // CraftBukkit end - Direction counterClockWise = direction.getCounterClockWise(); - Vec3 vec31 = vec3.relative(counterClockWise, d).relative(Direction.UP, d1); + Direction left = direction.getCounterClockWise(); + Vec3 position = attachedToWall.relative(left, horizontalOffset).relative(Direction.UP, verticalOffset); Direction.Axis axis = direction.getAxis(); -- double d2 = axis == Direction.Axis.X ? 0.0625 : paintingVariant.width(); -- double d3 = paintingVariant.height(); -- double d4 = axis == Direction.Axis.Z ? 0.0625 : paintingVariant.width(); +- double xSize = axis == Direction.Axis.X ? 0.0625 : variant.width(); +- double ySize = variant.height(); +- double zSize = axis == Direction.Axis.Z ? 0.0625 : variant.width(); + // CraftBukkit start -+ double d2 = axis == Direction.Axis.X ? 0.0625 : width; -+ double d3 = height; -+ double d4 = axis == Direction.Axis.Z ? 0.0625 : width; ++ double xSize = axis == Direction.Axis.X ? 0.0625 : width; ++ double ySize = height; ++ double zSize = axis == Direction.Axis.Z ? 0.0625 : width; + // CraftBukkit end - return AABB.ofSize(vec31, d2, d3, d4); + return AABB.ofSize(position, xSize, ySize, zSize); } -- private double offsetForPaintingSize(int size) { -+ private static double offsetForPaintingSize(int size) { // CraftBukkit - static +- private double offsetForPaintingSize(final int size) { ++ private static double offsetForPaintingSize(final int size) { // CraftBukkit - static return size % 2 == 0 ? 0.5 : 0.0; } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/item/FallingBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/item/FallingBlockEntity.java.patch index 8c6c4c685462..90e39c2f53d1 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/item/FallingBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/item/FallingBlockEntity.java.patch @@ -6,16 +6,16 @@ protected static final EntityDataAccessor DATA_START_POS = SynchedEntityData.defineId(FallingBlockEntity.class, EntityDataSerializers.BLOCK_POS); + public boolean autoExpire = true; // Paper - Expand FallingBlock API - public FallingBlockEntity(EntityType type, Level level) { + public FallingBlockEntity(final EntityType type, final Level level) { super(type, level); @@ -96,6 +_,7 @@ pos.getZ() + 0.5, state.hasProperty(BlockStateProperties.WATERLOGGED) ? state.setValue(BlockStateProperties.WATERLOGGED, false) : state ); -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(fallingBlockEntity, pos, state.getFluidState().createLegacyBlock())) return fallingBlockEntity; // CraftBukkit ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, state.getFluidState().createLegacyBlock())) return entity; // CraftBukkit level.setBlock(pos, state.getFluidState().createLegacyBlock(), Block.UPDATE_ALL); - level.addFreshEntity(fallingBlockEntity); - return fallingBlockEntity; + level.addFreshEntity(entity); + return entity; @@ -146,13 +_,22 @@ @Override public void tick() { @@ -39,13 +39,13 @@ + // Paper end - Configurable falling blocks height nerf this.handlePortal(); if (this.level() instanceof ServerLevel serverLevel && (this.isAlive() || this.forceTickAfterTeleportToDuplicate)) { - BlockPos blockPos = this.blockPosition(); + BlockPos pos = this.blockPosition(); @@ -173,12 +_,12 @@ } - if (!this.onGround() && !flag1) { -- if (this.time > 100 && (blockPos.getY() <= this.level().getMinY() || blockPos.getY() > this.level().getMaxY()) || this.time > 600) { -+ if ((this.time > 100 && autoExpire) && (blockPos.getY() <= this.level().getMinY() || blockPos.getY() > this.level().getMaxY()) || (this.time > 600 && autoExpire)) { // Paper - Expand FallingBlock API + if (!this.onGround() && !isStuckInWater) { +- if (this.time > 100 && (pos.getY() <= this.level().getMinY() || pos.getY() > this.level().getMaxY()) || this.time > 600) { ++ if ((this.time > 100 && this.autoExpire) && (pos.getY() <= this.level().getMinY() || pos.getY() > this.level().getMaxY()) || (this.time > 600 && this.autoExpire)) { // Paper - Expand FallingBlock API if (this.dropItem && serverLevel.getGameRules().get(GameRules.ENTITY_DROPS)) { this.spawnAtLocation(serverLevel, block); } @@ -54,50 +54,50 @@ + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause } } else { - BlockState blockState = this.level().getBlockState(blockPos); -@@ -196,11 +_,17 @@ + BlockState currentState = this.level().getBlockState(pos); +@@ -195,11 +_,17 @@ this.blockState = this.blockState.setValue(BlockStateProperties.WATERLOGGED, true); } + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockPos, this.blockState)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, pos, this.blockState)) { + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // SPIGOT-6586 called before the event in previous versions + return; + } + // CraftBukkit end - if (this.level().setBlock(blockPos, this.blockState, Block.UPDATE_ALL)) { + if (this.level().setBlock(pos, this.blockState, Block.UPDATE_ALL)) { serverLevel.getChunkSource() .chunkMap - .sendToTrackingPlayers(this, new ClientboundBlockUpdatePacket(blockPos, this.level().getBlockState(blockPos))); + .sendToTrackingPlayers(this, new ClientboundBlockUpdatePacket(pos, this.level().getBlockState(pos))); - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause if (block instanceof Fallable fallable) { - fallable.onLand(this.level(), blockPos, this.blockState, blockState, this); + fallable.onLand(this.level(), pos, this.blockState, currentState, this); } -@@ -225,19 +_,19 @@ +@@ -224,19 +_,19 @@ } } } else if (this.dropItem && serverLevel.getGameRules().get(GameRules.ENTITY_DROPS)) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause - this.callOnBrokenAfterFall(block, blockPos); + this.callOnBrokenAfterFall(block, pos); this.spawnAtLocation(serverLevel, block); } } else { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause if (this.dropItem && serverLevel.getGameRules().get(GameRules.ENTITY_DROPS)) { - this.callOnBrokenAfterFall(block, blockPos); + this.callOnBrokenAfterFall(block, pos); this.spawnAtLocation(serverLevel, block); } } } else { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause - this.callOnBrokenAfterFall(block, blockPos); + this.callOnBrokenAfterFall(block, pos); } } -@@ -297,6 +_,7 @@ +@@ -296,6 +_,7 @@ } output.putBoolean("CancelDrop", this.cancelDrop); @@ -105,7 +105,7 @@ } @Override -@@ -308,8 +_,9 @@ +@@ -307,8 +_,9 @@ this.fallDamagePerDistance = input.getFloatOr("FallHurtAmount", 0.0F); this.fallDamageMax = input.getIntOr("FallHurtMax", 40); this.dropItem = input.getBooleanOr("DropItem", true); @@ -115,13 +115,13 @@ + this.autoExpire = input.getBooleanOr("Paper.AutoExpire", true); // Paper - Expand FallingBlock API } - public void setHurtsEntities(float fallDamagePerDistance, int fallDamageMax) { -@@ -365,7 +_,7 @@ - ResourceKey resourceKey1 = this.level().dimension(); - boolean flag = (resourceKey1 == Level.END || resourceKey == Level.END) && resourceKey1 != resourceKey; - Entity entity = super.teleport(teleportTransition); -- this.forceTickAfterTeleportToDuplicate = entity != null && flag; -+ this.forceTickAfterTeleportToDuplicate = entity != null && flag && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowUnsafeEndPortalTeleportation; // Paper - return entity; + public void setHurtsEntities(final float damagePerDistance, final int damageMax) { +@@ -364,7 +_,7 @@ + ResourceKey oldDimension = this.level().dimension(); + boolean fromOrToEnd = (oldDimension == Level.END || newDimension == Level.END) && oldDimension != newDimension; + Entity newEntity = super.teleport(transition); +- this.forceTickAfterTeleportToDuplicate = newEntity != null && fromOrToEnd; ++ this.forceTickAfterTeleportToDuplicate = newEntity != null && fromOrToEnd && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowUnsafeEndPortalTeleportation; // Paper + return newEntity; } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/item/ItemEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/item/ItemEntity.java.patch index 4fda90eca2b9..0ef8416a98e3 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/item/ItemEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/item/ItemEntity.java.patch @@ -8,24 +8,10 @@ + private int despawnRate = -1; // Paper - Alternative item-despawn-rate + public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API - public ItemEntity(EntityType type, Level level) { + public ItemEntity(final EntityType type, final Level level) { super(type, level); -@@ -58,7 +_,12 @@ - } - - public ItemEntity(Level level, double posX, double posY, double posZ, ItemStack stack) { -- this(level, posX, posY, posZ, stack, level.random.nextDouble() * 0.2 - 0.1, 0.2, level.random.nextDouble() * 0.2 - 0.1); -+ // Paper start - Don't use level random in entity constructors (to make them thread-safe) -+ this(EntityType.ITEM, level); -+ this.setPos(posX, posY, posZ); -+ this.setDeltaMovement(this.random.nextDouble() * 0.2 - 0.1, 0.2, this.random.nextDouble() * 0.2 - 0.1); -+ this.setItem(stack); -+ // Paper end - Don't use level random in entity constructors - } - - public ItemEntity(Level level, double posX, double posY, double posZ, ItemStack stack, double deltaX, double deltaY, double deltaZ) { -@@ -68,6 +_,14 @@ - this.setItem(stack); +@@ -80,6 +_,14 @@ + this.setDeltaMovement(deltaX, deltaY, deltaZ); } + // Paper start - Require item entities to send their location precisely (Fixes MC-4) @@ -39,7 +25,7 @@ @Override public boolean dampensVibrations() { return this.getItem().is(ItemTags.DAMPENS_VIBRATIONS); -@@ -104,7 +_,7 @@ +@@ -116,7 +_,7 @@ @Override public void tick() { if (this.getItem().isEmpty()) { @@ -48,25 +34,27 @@ } else { super.tick(); if (this.pickupDelay > 0 && this.pickupDelay != 32767) { -@@ -132,11 +_,15 @@ +@@ -144,13 +_,17 @@ } } -- if (!this.onGround() || this.getDeltaMovement().horizontalDistanceSqr() > 1.0E-5F || (this.tickCount + this.getId()) % 4 == 0) { -+ if (!this.onGround() || this.getDeltaMovement().horizontalDistanceSqr() > 1.0E-5F || (this.tickCount + this.getId()) % 4 == 0) { // Paper - Diff on change; ActivationRange immunity +- if (this.onGround() && !(this.getDeltaMovement().horizontalDistanceSqr() > 1.0E-5F) && (this.tickCount + this.getId()) % 4 != 0) { ++ if (this.onGround() && !(this.getDeltaMovement().horizontalDistanceSqr() > 1.0E-5F) && (this.tickCount + this.getId()) % 4 != 0) { // Paper - Diff on change; ActivationRange immunity + this.applyEffectsFromBlocksForLastMovements(); + } else { this.move(MoverType.SELF, this.getDeltaMovement()); this.applyEffectsFromBlocks(); - float f = 0.98F; + float friction = 0.98F; - if (this.onGround()) { + // Paper start - Friction API + if (this.frictionState == net.kyori.adventure.util.TriState.FALSE) { -+ f = 1F; ++ friction = 1F; + } else if (this.onGround()) { + // Paper end - Friction API - f = this.level().getBlockState(this.getBlockPosBelowThatAffectsMyMovement()).getBlock().getFriction() * 0.98F; + friction = this.level().getBlockState(this.getBlockPosBelowThatAffectsMyMovement()).getBlock().getFriction() * 0.98F; } -@@ -169,8 +_,14 @@ +@@ -183,8 +_,14 @@ } } @@ -83,27 +71,27 @@ } } } -@@ -195,9 +_,18 @@ +@@ -209,9 +_,18 @@ private void mergeWithNeighbours() { if (this.isMergable()) { + double radius = this.level().spigotConfig.itemMerge; // Spigot - for (ItemEntity itemEntity : this.level() -- .getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.5, 0.0, 0.5), neighbour -> neighbour != this && neighbour.isMergable())) { -+ .getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, this.level().paperConfig().entities.behavior.onlyMergeItemsHorizontally ? 0 : radius - 0.5D, radius), neighbour -> neighbour != this && neighbour.isMergable())) { // Spigot // Paper - configuration to only merge items horizontally - if (itemEntity.isMergable()) { + for (ItemEntity entity : this.level() +- .getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.5, 0.0, 0.5), other -> other != this && other.isMergable())) { ++ .getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, this.level().paperConfig().entities.behavior.onlyMergeItemsHorizontally ? 0 : radius - 0.5D, radius), other -> other != this && other.isMergable())) { // Spigot // Paper - configuration to only merge items horizontally + if (entity.isMergable()) { + // Paper start - Fix items merging through walls + if (this.level().paperConfig().fixes.fixItemsMergingThroughWalls) { -+ if (this.level().clipDirect(this.position(), itemEntity.position(), ++ if (this.level().clipDirect(this.position(), entity.position(), + net.minecraft.world.phys.shapes.CollisionContext.of(this)) == net.minecraft.world.phys.HitResult.Type.BLOCK) { + continue; + } + } + // Paper end - Fix items merging through walls - this.tryToMerge(itemEntity); + this.tryToMerge(entity); if (this.isRemoved()) { break; -@@ -209,7 +_,7 @@ +@@ -223,7 +_,7 @@ private boolean isMergable() { ItemStack item = this.getItem(); @@ -111,37 +99,37 @@ + return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < this.despawnRate && item.getCount() < item.getMaxStackSize(); // Paper - Alternative item-despawn-rate } - private void tryToMerge(ItemEntity itemEntity) { -@@ -242,11 +_,16 @@ + private void tryToMerge(final ItemEntity other) { +@@ -256,11 +_,16 @@ } - private static void merge(ItemEntity destinationEntity, ItemStack destinationStack, ItemEntity originEntity, ItemStack originStack) { + private static void merge(final ItemEntity toItem, final ItemStack toStack, final ItemEntity fromItem, final ItemStack fromStack) { + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callItemMergeEvent(originEntity, destinationEntity)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callItemMergeEvent(fromItem, toItem)) { + return; + } + // CraftBukkit end - merge(destinationEntity, destinationStack, originStack); - destinationEntity.pickupDelay = Math.max(destinationEntity.pickupDelay, originEntity.pickupDelay); - destinationEntity.age = Math.min(destinationEntity.age, originEntity.age); - if (originStack.isEmpty()) { -- originEntity.discard(); -+ originEntity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.MERGE); // CraftBukkit - add Bukkit remove cause + merge(toItem, toStack, fromStack); + toItem.pickupDelay = Math.max(toItem.pickupDelay, fromItem.pickupDelay); + toItem.age = Math.min(toItem.age, fromItem.age); + if (fromStack.isEmpty()) { +- fromItem.discard(); ++ fromItem.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.MERGE); // CraftBukkit - add Bukkit remove cause } } -@@ -274,12 +_,17 @@ - } else if (!this.getItem().canBeHurtBy(damageSource)) { +@@ -288,12 +_,17 @@ + } else if (!this.getItem().canBeHurtBy(source)) { return false; } else { + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damageSource, amount)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, damage)) { + return false; + } + // CraftBukkit end this.markHurt(); - this.health = (int)(this.health - amount); - this.gameEvent(GameEvent.ENTITY_DAMAGE, damageSource.getEntity()); + this.health = (int)(this.health - damage); + this.gameEvent(GameEvent.ENTITY_DAMAGE, source.getEntity()); if (this.health <= 0) { this.getItem().onDestroyed(this); - this.discard(); @@ -149,7 +137,7 @@ } return true; -@@ -301,6 +_,11 @@ +@@ -315,6 +_,11 @@ if (!this.getItem().isEmpty()) { output.store("Item", ItemStack.CODEC, this.getItem()); } @@ -161,7 +149,7 @@ } @Override -@@ -311,8 +_,17 @@ +@@ -325,8 +_,17 @@ this.target = input.read("Owner", UUIDUtil.CODEC).orElse(null); this.thrower = EntityReference.read(input, "Thrower"); this.setItem(input.read("Item", ItemStack.CODEC).orElse(ItemStack.EMPTY)); @@ -180,24 +168,24 @@ } } -@@ -322,10 +_,73 @@ - ItemStack item = this.getItem(); - Item item1 = item.getItem(); - int count = item.getCount(); +@@ -336,10 +_,73 @@ + ItemStack itemStack = this.getItem(); + Item item = itemStack.getItem(); + int orgCount = itemStack.getCount(); + // CraftBukkit start - fire PlayerPickupItemEvent -+ int canHold = entity.getInventory().canHold(item); -+ int remaining = count - canHold; ++ int canHold = player.getInventory().canHold(itemStack); ++ int remaining = orgCount - canHold; + boolean flyAtPlayer = false; // Paper + + // Paper start - PlayerAttemptPickupItemEvent + if (this.pickupDelay <= 0) { -+ org.bukkit.event.player.PlayerAttemptPickupItemEvent attemptEvent = new org.bukkit.event.player.PlayerAttemptPickupItemEvent((org.bukkit.entity.Player) entity.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); ++ org.bukkit.event.player.PlayerAttemptPickupItemEvent attemptEvent = new org.bukkit.event.player.PlayerAttemptPickupItemEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); + this.level().getCraftServer().getPluginManager().callEvent(attemptEvent); + + flyAtPlayer = attemptEvent.getFlyAtPlayer(); + if (attemptEvent.isCancelled()) { + if (flyAtPlayer) { -+ entity.take(this, count); ++ player.take(this, orgCount); + } + + return; @@ -205,37 +193,37 @@ + } + + if (this.pickupDelay <= 0 && canHold > 0) { -+ item.setCount(canHold); ++ itemStack.setCount(canHold); + // Call legacy event -+ org.bukkit.event.player.PlayerPickupItemEvent playerEvent = new org.bukkit.event.player.PlayerPickupItemEvent((org.bukkit.entity.Player) entity.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); ++ org.bukkit.event.player.PlayerPickupItemEvent playerEvent = new org.bukkit.event.player.PlayerPickupItemEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); + playerEvent.setCancelled(!playerEvent.getPlayer().getCanPickupItems()); + this.level().getCraftServer().getPluginManager().callEvent(playerEvent); + flyAtPlayer = playerEvent.getFlyAtPlayer(); // Paper + if (playerEvent.isCancelled()) { -+ item.setCount(count); // SPIGOT-5294 - restore count ++ itemStack.setCount(orgCount); // SPIGOT-5294 - restore count + // Paper start + if (flyAtPlayer) { -+ entity.take(this, count); ++ player.take(this, orgCount); + } + // Paper end + return; + } + + // Call newer event afterwards -+ org.bukkit.event.entity.EntityPickupItemEvent entityEvent = new org.bukkit.event.entity.EntityPickupItemEvent(entity.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); ++ org.bukkit.event.entity.EntityPickupItemEvent entityEvent = new org.bukkit.event.entity.EntityPickupItemEvent(player.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); + entityEvent.setCancelled(!entityEvent.getEntity().getCanPickupItems()); + this.level().getCraftServer().getPluginManager().callEvent(entityEvent); + if (entityEvent.isCancelled()) { -+ item.setCount(count); // SPIGOT-5294 - restore count ++ itemStack.setCount(orgCount); // SPIGOT-5294 - restore count + return; + } + + // Update the ItemStack if it was changed in the event + ItemStack current = this.getItem(); -+ if (!item.equals(current)) { -+ item = current; ++ if (!itemStack.equals(current)) { ++ itemStack = current; + } else { -+ item.setCount(canHold + remaining); // = i ++ itemStack.setCount(canHold + remaining); // = i + } + + // Possibly < 0; fix here so we do not have to modify code below @@ -246,24 +234,24 @@ + } + // CraftBukkit end + // Paper end - PlayerAttemptPickupItemEvent - if (this.pickupDelay == 0 && (this.target == null || this.target.equals(entity.getUUID())) && entity.getInventory().add(item)) { + if (this.pickupDelay == 0 && (this.target == null || this.target.equals(player.getUUID())) && player.getInventory().add(itemStack)) { + if (flyAtPlayer) // Paper - PlayerPickupItemEvent - entity.take(this, count); - if (item.isEmpty()) { + player.take(this, orgCount); + if (itemStack.isEmpty()) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause - item.setCount(count); + itemStack.setCount(orgCount); } -@@ -362,6 +_,7 @@ +@@ -376,6 +_,7 @@ - public void setItem(ItemStack stack) { - this.getEntityData().set(DATA_ITEM, stack); -+ this.despawnRate = this.level().paperConfig().entities.spawning.altItemDespawnRate.enabled ? this.level().paperConfig().entities.spawning.altItemDespawnRate.items.getOrDefault(stack.getItem(), this.level().spigotConfig.itemDespawnRate) : this.level().spigotConfig.itemDespawnRate; // Paper - Alternative item-despawn-rate + public void setItem(final ItemStack itemStack) { + this.getEntityData().set(DATA_ITEM, itemStack); ++ this.despawnRate = this.level().paperConfig().entities.spawning.altItemDespawnRate.enabled ? this.level().paperConfig().entities.spawning.altItemDespawnRate.items.getOrDefault(itemStack.getItem(), this.level().spigotConfig.itemDespawnRate) : this.level().spigotConfig.itemDespawnRate; // Paper - Alternative item-despawn-rate } - @Override -@@ -414,7 +_,7 @@ + public void setTarget(final @Nullable UUID target) { +@@ -420,7 +_,7 @@ public void makeFakeItem() { this.setNeverPickUp(); @@ -271,4 +259,4 @@ + this.age = this.despawnRate - 1; // Spigot // Paper - Alternative item-despawn-rate } - public static float getSpin(float age, float bobOffset) { + public static float getSpin(final float ageInTicks, final float bobOffset) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/item/PrimedTnt.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/item/PrimedTnt.java.patch index 2c49055d3d3f..ed22259d6bcd 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/item/PrimedTnt.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/item/PrimedTnt.java.patch @@ -13,24 +13,24 @@ public class PrimedTnt extends Entity implements TraceableEntity { private static final EntityDataAccessor DATA_FUSE_ID = SynchedEntityData.defineId(PrimedTnt.class, EntityDataSerializers.INT); private static final EntityDataAccessor DATA_BLOCK_STATE_ID = SynchedEntityData.defineId(PrimedTnt.class, EntityDataSerializers.BLOCK_STATE); -@@ -51,6 +_,7 @@ +@@ -53,6 +_,7 @@ public @Nullable EntityReference owner; private boolean usedPortal; public float explosionPower = 4.0F; + public boolean isIncendiary = false; // CraftBukkit - public PrimedTnt(EntityType type, Level level) { + public PrimedTnt(final EntityType type, final Level level) { super(type, level); -@@ -60,7 +_,7 @@ - public PrimedTnt(Level level, double x, double y, double z, @Nullable LivingEntity owner) { +@@ -62,7 +_,7 @@ + public PrimedTnt(final Level level, final double x, final double y, final double z, final @Nullable LivingEntity owner) { this(EntityType.TNT, level); this.setPos(x, y, z); -- double d = level.random.nextDouble() * (float) (Math.PI * 2); -+ double d = this.random.nextDouble() * (float) (Math.PI * 2); // Paper - Don't use level random in entity constructors - this.setDeltaMovement(-Math.sin(d) * 0.02, 0.2F, -Math.cos(d) * 0.02); +- double rot = level.getRandom().nextDouble() * (float) (Math.PI * 2); ++ double rot = this.getRandom().nextDouble() * (float) (Math.PI * 2); // Paper - Don't use level random in entity constructors + this.setDeltaMovement(-Math.sin(rot) * 0.02, 0.2F, -Math.cos(rot) * 0.02); this.setFuse(80); this.xo = x; -@@ -92,10 +_,17 @@ +@@ -94,10 +_,17 @@ @Override public void tick() { @@ -48,10 +48,10 @@ this.setDeltaMovement(this.getDeltaMovement().scale(0.98)); if (this.onGround()) { this.setDeltaMovement(this.getDeltaMovement().multiply(0.7, -0.5, 0.7)); -@@ -104,20 +_,35 @@ - int i = this.getFuse() - 1; - this.setFuse(i); - if (i <= 0) { +@@ -106,20 +_,35 @@ + int fuse = this.getFuse() - 1; + this.setFuse(fuse); + if (fuse <= 0) { - this.discard(); + // CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event + //this.discard(); @@ -61,7 +61,7 @@ + this.discard(EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause + // CraftBukkit end } else { - this.updateInWaterStateAndDoFluidPushing(); + this.updateFluidInteraction(); if (this.level().isClientSide()) { this.level().addParticle(ParticleTypes.SMOKE, this.getX(), this.getY() + 0.5, this.getZ(), 0.0, 0.0, 0.0); } @@ -75,7 +75,7 @@ } private void explode() { - if (this.level() instanceof ServerLevel serverLevel && serverLevel.getGameRules().get(GameRules.TNT_EXPLODES)) { + if (this.level() instanceof ServerLevel level && level.getGameRules().get(GameRules.TNT_EXPLODES)) { + // CraftBukkit start + ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent((org.bukkit.entity.Explosive) this.getBukkitEntity()); + if (event.isCancelled()) { @@ -85,7 +85,7 @@ this.level() .explode( this, -@@ -126,8 +_,8 @@ +@@ -128,8 +_,8 @@ this.getX(), this.getY(0.0625), this.getZ(), @@ -96,8 +96,8 @@ Level.ExplosionInteraction.TNT ); } -@@ -199,4 +_,11 @@ - public final boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { +@@ -201,4 +_,11 @@ + public final boolean hurtServer(final ServerLevel level, final DamageSource source, final float damage) { return false; } + diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Creeper.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Creeper.java.patch index 65fc6a540624..7f4bb9c08612 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Creeper.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Creeper.java.patch @@ -6,7 +6,7 @@ public boolean droppedSkulls; + public @Nullable Entity entityIgniter; // CraftBukkit - public Creeper(EntityType type, Level level) { + public Creeper(final EntityType type, final Level level) { super(type, level); @@ -118,7 +_,7 @@ this.maxSwell = input.getShortOr("Fuse", (short)30); @@ -21,8 +21,8 @@ } @Override -- public void setTarget(@Nullable LivingEntity target) { -+ public boolean setTarget(@Nullable LivingEntity target, org.bukkit.event.entity.EntityTargetEvent.@Nullable TargetReason reason) { // CraftBukkit +- public void setTarget(final @Nullable LivingEntity target) { ++ public boolean setTarget(final @Nullable LivingEntity target, org.bukkit.event.entity.EntityTargetEvent.@Nullable TargetReason reason) { // CraftBukkit if (!(target instanceof Goat)) { - super.setTarget(target); + return super.setTarget(target, reason); // CraftBukkit @@ -33,10 +33,10 @@ @Override @@ -203,9 +_,20 @@ @Override - public void thunderHit(ServerLevel level, LightningBolt lightning) { - super.thunderHit(level, lightning); + public void thunderHit(final ServerLevel level, final LightningBolt lightningBolt) { + super.thunderHit(level, lightningBolt); + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callCreeperPowerEvent(this, lightning, org.bukkit.event.entity.CreeperPowerEvent.PowerCause.LIGHTNING).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callCreeperPowerEvent(this, lightningBolt, org.bukkit.event.entity.CreeperPowerEvent.PowerCause.LIGHTNING).isCancelled()) { + return; + } + // CraftBukkit end @@ -50,32 +50,32 @@ + // CraftBukkit end + @Override - protected InteractionResult mobInteract(Player player, InteractionHand hand) { - ItemStack itemInHand = player.getItemInHand(hand); + protected InteractionResult mobInteract(final Player player, final InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); @@ -214,8 +_,9 @@ this.level() .playSound(player, this.getX(), this.getY(), this.getZ(), soundEvent, this.getSoundSource(), 1.0F, this.random.nextFloat() * 0.4F + 0.8F); if (!this.level().isClientSide()) { + this.entityIgniter = player; // CraftBukkit this.ignite(); -- if (!itemInHand.isDamageableItem()) { -+ if (itemInHand.getMaxDamage() == 0) { // CraftBukkit - fix MC-264285: unbreakable flint and steels are completely consumed when igniting a creeper - itemInHand.shrink(1); +- if (!itemStack.isDamageableItem()) { ++ if (itemStack.getMaxDamage() == 0) { // CraftBukkit - fix MC-264285: unbreakable flint and steels are completely consumed when igniting a creeper + itemStack.shrink(1); } else { - itemInHand.hurtAndBreak(1, player, hand.asEquipmentSlot()); + itemStack.hurtAndBreak(1, player, hand.asEquipmentSlot()); @@ -231,18 +_,29 @@ public void explodeCreeper() { - if (this.level() instanceof ServerLevel serverLevel) { - float f = this.isPowered() ? 2.0F : 1.0F; + if (this.level() instanceof ServerLevel level) { + float explosionMultiplier = this.isPowered() ? 2.0F : 1.0F; + // CraftBukkit start -+ org.bukkit.event.entity.ExplosionPrimeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExplosionPrimeEvent(this, this.explosionRadius * f, false); ++ org.bukkit.event.entity.ExplosionPrimeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExplosionPrimeEvent(this, this.explosionRadius * explosionMultiplier, false); + if (!event.isCancelled()) { + // CraftBukkit end this.dead = true; -- serverLevel.explode(this, this.getX(), this.getY(), this.getZ(), this.explosionRadius * f, Level.ExplosionInteraction.MOB); -+ serverLevel.explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.MOB); // CraftBukkit // Paper - fix DamageSource API (revert to vanilla, no, just no, don't change this) +- level.explode(this, this.getX(), this.getY(), this.getZ(), this.explosionRadius * explosionMultiplier, Level.ExplosionInteraction.MOB); ++ level.explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.MOB); // CraftBukkit // Paper - fix DamageSource API (revert to vanilla, no, just no, don't change this) this.spawnLingeringCloud(); - this.triggerOnDeathMobEffects(serverLevel, Entity.RemovalReason.KILLED); + this.triggerOnDeathMobEffects(level, Entity.RemovalReason.KILLED); - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause + // CraftBukkit start @@ -91,19 +91,19 @@ Collection activeEffects = this.getActiveEffects(); - if (!activeEffects.isEmpty()) { + if (!activeEffects.isEmpty() && !this.level().paperConfig().entities.behavior.disableCreeperLingeringEffect) { // Paper - Option to disable creeper lingering effect - AreaEffectCloud areaEffectCloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ()); -+ areaEffectCloud.setOwner(this); // CraftBukkit - areaEffectCloud.setRadius(2.5F); - areaEffectCloud.setRadiusOnUse(-0.5F); - areaEffectCloud.setWaitTime(10); + AreaEffectCloud cloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ()); ++ cloud.setOwner(this); // CraftBukkit + cloud.setRadius(2.5F); + cloud.setRadiusOnUse(-0.5F); + cloud.setWaitTime(10); @@ -254,15 +_,26 @@ - areaEffectCloud.addEffect(new MobEffectInstance(mobEffectInstance)); + cloud.addEffect(new MobEffectInstance(mobEffect)); } -- this.level().addFreshEntity(areaEffectCloud); +- this.level().addFreshEntity(cloud); - } - } -+ this.level().addFreshEntity(areaEffectCloud, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EXPLOSION); // CraftBukkit ++ this.level().addFreshEntity(cloud, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EXPLOSION); // CraftBukkit + } + } + diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/ElderGuardian.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/ElderGuardian.java.patch index c3d2fb4778d2..08823a50a2b6 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/ElderGuardian.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/ElderGuardian.java.patch @@ -3,9 +3,9 @@ @@ -65,7 +_,7 @@ super.customServerAiStep(level); if ((this.tickCount + this.getId()) % 1200 == 0) { - MobEffectInstance mobEffectInstance = new MobEffectInstance(MobEffects.MINING_FATIGUE, 6000, 2); -- List list = MobEffectUtil.addEffectToPlayersAround(level, this, this.position(), 50.0, mobEffectInstance, 1200); -+ List list = MobEffectUtil.addEffectToPlayersAround(level, this, this.position(), 50.0, mobEffectInstance, 1200, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK, (player) -> new io.papermc.paper.event.entity.ElderGuardianAppearanceEvent((org.bukkit.entity.ElderGuardian) this.getBukkitEntity(), player.getBukkitEntity()).callEvent()); // CraftBukkit // Paper - Add ElderGuardianAppearanceEvent - list.forEach( - serverPlayer -> serverPlayer.connection + MobEffectInstance miningFatigue = new MobEffectInstance(MobEffects.MINING_FATIGUE, 6000, 2); +- List affectedPlayers = MobEffectUtil.addEffectToPlayersAround(level, this, this.position(), 50.0, miningFatigue, 1200); ++ List affectedPlayers = MobEffectUtil.addEffectToPlayersAround(level, this, this.position(), 50.0, miningFatigue, 1200, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK, player -> new io.papermc.paper.event.entity.ElderGuardianAppearanceEvent((org.bukkit.entity.ElderGuardian)this.getBukkitEntity(), player.getBukkitEntity()).callEvent()); // CraftBukkit // Paper - Add ElderGuardianAppearanceEvent + affectedPlayers.forEach( + player -> player.connection .send(new ClientboundGameEventPacket(ClientboundGameEventPacket.GUARDIAN_ELDER_EFFECT, this.isSilent() ? 0.0F : 1.0F)) diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/EnderMan.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/EnderMan.java.patch index a7435e6abc0d..2e6fd539b7c9 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/EnderMan.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/EnderMan.java.patch @@ -11,7 +11,7 @@ + // Paper end - EndermanEscapeEvent + @Override -- public void setTarget(@Nullable LivingEntity target) { +- public void setTarget(final @Nullable LivingEntity target) { - super.setTarget(target); + // CraftBukkit start - fire event + public boolean setTarget(@Nullable LivingEntity target, org.bukkit.event.entity.EntityTargetEvent.@Nullable TargetReason reason) { @@ -20,11 +20,11 @@ + } + target = this.getTarget(); + // CraftBukkit end - AttributeInstance attribute = this.getAttribute(Attributes.MOVEMENT_SPEED); + AttributeInstance movementSpeed = this.getAttribute(Attributes.MOVEMENT_SPEED); if (target == null) { this.targetChangeTime = 0; @@ -135,6 +_,7 @@ - attribute.addTransientModifier(SPEED_MODIFIER_ATTACKING); + movementSpeed.addTransientModifier(SPEED_MODIFIER_ATTACKING); } } + return true; // CraftBukkit @@ -34,7 +34,7 @@ @@ -207,6 +_,15 @@ } - boolean isBeingStaredBy(Player player) { + private boolean isBeingStaredBy(final Player player) { + // Paper start - EndermanAttackPlayerEvent + final boolean shouldAttack = this.isBeingStaredBy0(player); + final com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event = new com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent((org.bukkit.entity.Enderman) getBukkitEntity(), (org.bukkit.entity.Player) player.getBukkitEntity()); @@ -47,31 +47,31 @@ return LivingEntity.PLAYER_NOT_WEARING_DISGUISE_ITEM.test(player) && this.isLookingAtMe(player, 0.025, true, false, this.getEyeY()); } -@@ -246,7 +_,7 @@ - float lightLevelDependentMagicValue = this.getLightLevelDependentMagicValue(); - if (lightLevelDependentMagicValue > 0.5F - && level.canSeeSky(this.blockPosition()) -- && this.random.nextFloat() * 30.0F < (lightLevelDependentMagicValue - 0.4F) * 2.0F) { -+ && this.random.nextFloat() * 30.0F < (lightLevelDependentMagicValue - 0.4F) * 2.0F && this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper - EndermanEscapeEvent +@@ -244,7 +_,7 @@ + protected void customServerAiStep(final ServerLevel level) { + if (level.isBrightOutside() && this.tickCount >= this.targetChangeTime + 600) { + float br = this.getLightLevelDependentMagicValue(); +- if (br > 0.5F && level.canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30.0F < (br - 0.4F) * 2.0F) { ++ if (br > 0.5F && level.canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30.0F < (br - 0.4F) * 2.0F && this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper - EndermanEscapeEvent this.setTarget(null); this.teleport(); } -@@ -358,21 +_,25 @@ - AbstractThrownPotion abstractThrownPotion1 = damageSource.getDirectEntity() instanceof AbstractThrownPotion abstractThrownPotion - ? abstractThrownPotion - : null; -- if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && abstractThrownPotion1 == null) { -+ if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && abstractThrownPotion1 == null) { // Paper - EndermanEscapeEvent - diff on change - below logic relies on this path covering non-projectile damage. - boolean flag = super.hurtServer(level, damageSource, amount); - if (!(damageSource.getEntity() instanceof LivingEntity) && this.random.nextInt(10) != 0) { -+ if (this.tryEscape(damageSource.is(net.minecraft.tags.DamageTypeTags.IS_DROWNING) ? com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.DROWN : com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.CRITICAL_HIT)) { // Paper - EndermanEscapeEvent +@@ -354,21 +_,25 @@ + return false; + } else { + AbstractThrownPotion thrownPotion = source.getDirectEntity() instanceof AbstractThrownPotion potion ? potion : null; +- if (!source.is(DamageTypeTags.IS_PROJECTILE) && thrownPotion == null) { ++ if (!source.is(DamageTypeTags.IS_PROJECTILE) && thrownPotion == null) { // Paper - EndermanEscapeEvent - diff on change - below logic relies on this path covering non-projectile damage. + boolean result = super.hurtServer(level, source, damage); + if (!(source.getEntity() instanceof LivingEntity) && this.random.nextInt(10) != 0) { ++ if (this.tryEscape(source.is(net.minecraft.tags.DamageTypeTags.IS_DROWNING) ? com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.DROWN : com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.CRITICAL_HIT)) { // Paper - EndermanEscapeEvent this.teleport(); + } // Paper - EndermanEscapeEvent } - return flag; + return result; } else { - boolean flag = abstractThrownPotion1 != null && this.hurtWithCleanWater(level, damageSource, abstractThrownPotion1, amount); + boolean hurtWithCleanWater = thrownPotion != null && this.hurtWithCleanWater(level, source, thrownPotion, damage); + if (this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.INDIRECT)) { // Paper - EndermanEscapeEvent for (int i = 0; i < 64; i++) { @@ -81,9 +81,9 @@ } + } // Paper - EndermanEscapeEvent - return flag; + return hurtWithCleanWater; } -@@ -397,6 +_,16 @@ +@@ -393,6 +_,16 @@ this.entityData.set(DATA_STARED_AT, true); } @@ -100,28 +100,28 @@ @Override public boolean requiresCustomPersistence() { return super.requiresCustomPersistence() || this.getCarriedBlock() != null; -@@ -455,16 +_,19 @@ - int floor1 = Mth.floor(this.enderman.getY() + random.nextDouble() * 2.0); - int floor2 = Mth.floor(this.enderman.getZ() - 1.0 + random.nextDouble() * 2.0); - BlockPos blockPos = new BlockPos(floor, floor1, floor2); -- BlockState blockState = level.getBlockState(blockPos); -+ BlockState blockState = level.getBlockStateIfLoaded(blockPos); // Paper - Prevent endermen from loading chunks -+ if (blockState == null) return; // Paper - Prevent endermen from loading chunks - BlockPos blockPos1 = blockPos.below(); - BlockState blockState1 = level.getBlockState(blockPos1); - BlockState carriedBlock = this.enderman.getCarriedBlock(); - if (carriedBlock != null) { - carriedBlock = Block.updateFromNeighbourShapes(carriedBlock, this.enderman.level(), blockPos); - if (this.canPlaceBlock(level, blockPos, carriedBlock, blockState, blockState1, blockPos1)) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, blockPos, carriedBlock)) { // CraftBukkit - Place event - level.setBlock(blockPos, carriedBlock, Block.UPDATE_ALL); - level.gameEvent(GameEvent.BLOCK_PLACE, blockPos, GameEvent.Context.of(this.enderman, carriedBlock)); +@@ -451,16 +_,19 @@ + int yt = Mth.floor(this.enderman.getY() + random.nextDouble() * 2.0); + int zt = Mth.floor(this.enderman.getZ() - 1.0 + random.nextDouble() * 2.0); + BlockPos pos = new BlockPos(xt, yt, zt); +- BlockState targetState = level.getBlockState(pos); ++ BlockState targetState = level.getBlockStateIfLoaded(pos); // Paper - Prevent endermen from loading chunks ++ if (targetState == null) return; // Paper - Prevent endermen from loading chunks + BlockPos below = pos.below(); + BlockState belowState = level.getBlockState(below); + BlockState carried = this.enderman.getCarriedBlock(); + if (carried != null) { + carried = Block.updateFromNeighbourShapes(carried, this.enderman.level(), pos); + if (this.canPlaceBlock(level, pos, carried, targetState, belowState, below)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, pos, carried)) { // CraftBukkit - Place event + level.setBlock(pos, carried, Block.UPDATE_ALL); + level.gameEvent(GameEvent.BLOCK_PLACE, pos, GameEvent.Context.of(this.enderman, carried)); this.enderman.setCarriedBlock(null); + } // CraftBukkit } } } -@@ -561,7 +_,7 @@ +@@ -552,7 +_,7 @@ } else { if (this.target != null && !this.enderman.isPassenger()) { if (this.enderman.isBeingStaredBy((Player)this.target)) { @@ -130,21 +130,21 @@ this.enderman.teleport(); } -@@ -600,15 +_,18 @@ - int floor1 = Mth.floor(this.enderman.getY() + random.nextDouble() * 3.0); - int floor2 = Mth.floor(this.enderman.getZ() - 2.0 + random.nextDouble() * 4.0); - BlockPos blockPos = new BlockPos(floor, floor1, floor2); -- BlockState blockState = level.getBlockState(blockPos); -+ BlockState blockState = level.getBlockStateIfLoaded(blockPos); // Paper - Prevent endermen from loading chunks +@@ -591,15 +_,18 @@ + int yt = Mth.floor(this.enderman.getY() + random.nextDouble() * 3.0); + int zt = Mth.floor(this.enderman.getZ() - 2.0 + random.nextDouble() * 4.0); + BlockPos pos = new BlockPos(xt, yt, zt); +- BlockState blockState = level.getBlockState(pos); ++ BlockState blockState = level.getBlockStateIfLoaded(pos); // Paper - Prevent endermen from loading chunks + if (blockState == null) return; // Paper - Prevent endermen from loading chunks - Vec3 vec3 = new Vec3(this.enderman.getBlockX() + 0.5, floor1 + 0.5, this.enderman.getBlockZ() + 0.5); - Vec3 vec31 = new Vec3(floor + 0.5, floor1 + 0.5, floor2 + 0.5); - BlockHitResult blockHitResult = level.clip(new ClipContext(vec3, vec31, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.enderman)); - boolean flag = blockHitResult.getBlockPos().equals(blockPos); - if (blockState.is(BlockTags.ENDERMAN_HOLDABLE) && flag) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, blockPos, blockState.getFluidState().createLegacyBlock())) { // CraftBukkit - Place event // Paper - fix wrong block state - level.removeBlock(blockPos, false); - level.gameEvent(GameEvent.BLOCK_DESTROY, blockPos, GameEvent.Context.of(this.enderman, blockState)); + Vec3 from = new Vec3(this.enderman.getBlockX() + 0.5, yt + 0.5, this.enderman.getBlockZ() + 0.5); + Vec3 to = new Vec3(xt + 0.5, yt + 0.5, zt + 0.5); + BlockHitResult result = level.clip(new ClipContext(from, to, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.enderman)); + boolean reachable = result.getBlockPos().equals(pos); + if (blockState.is(BlockTags.ENDERMAN_HOLDABLE) && reachable) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, pos, blockState.getFluidState().createLegacyBlock())) { // Paper - Place event + level.removeBlock(pos, false); + level.gameEvent(GameEvent.BLOCK_DESTROY, pos, GameEvent.Context.of(this.enderman, blockState)); this.enderman.setCarriedBlock(blockState.getBlock().defaultBlockState()); + } // CraftBukkit } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Ghast.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Ghast.java.patch index 39e04cd088fe..831086353ed5 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Ghast.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Ghast.java.patch @@ -10,14 +10,14 @@ + } + // Paper end + - private static boolean isReflectedFireball(DamageSource damageSource) { - return damageSource.getDirectEntity() instanceof LargeFireball && damageSource.getEntity() instanceof Player; + private static boolean isReflectedFireball(final DamageSource source) { + return source.getDirectEntity() instanceof LargeFireball && source.getEntity() instanceof Player; } -@@ -368,6 +_,7 @@ +@@ -375,6 +_,7 @@ } - LargeFireball largeFireball = new LargeFireball(level, this.ghast, vec3.normalize(), this.ghast.getExplosionPower()); -+ largeFireball.bukkitYield = largeFireball.explosionPower = this.ghast.getExplosionPower(); // CraftBukkit - set bukkitYield when setting explosionPower - largeFireball.setPos(this.ghast.getX() + viewVector.x * 4.0, this.ghast.getY(0.5) + 0.5, largeFireball.getZ() + viewVector.z * 4.0); - level.addFreshEntity(largeFireball); + LargeFireball entity = new LargeFireball(level, this.ghast, direction.normalize(), this.ghast.getExplosionPower()); ++ entity.bukkitYield = entity.explosionPower = this.ghast.getExplosionPower(); // CraftBukkit - set bukkitYield when setting explosionPower + entity.setPos(this.ghast.getX() + viewVector.x * 4.0, this.ghast.getY(0.5) + 0.5, entity.getZ() + viewVector.z * 4.0); + level.addFreshEntity(entity); this.chargeTime = -40; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Guardian.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Guardian.java.patch index e15e6f685444..70b864f1a433 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Guardian.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Guardian.java.patch @@ -6,14 +6,14 @@ public @Nullable RandomStrollGoal randomStrollGoal; + public Guardian.GuardianAttackGoal guardianAttackGoal; // CraftBukkit - add field - public Guardian(EntityType type, Level level) { + public Guardian(final EntityType type, final Level level) { super(type, level); @@ -72,7 +_,7 @@ protected void registerGoals() { - MoveTowardsRestrictionGoal moveTowardsRestrictionGoal = new MoveTowardsRestrictionGoal(this, 1.0); + MoveTowardsRestrictionGoal goal = new MoveTowardsRestrictionGoal(this, 1.0); this.randomStrollGoal = new RandomStrollGoal(this, 1.0, 80); - this.goalSelector.addGoal(4, new Guardian.GuardianAttackGoal(this)); + this.goalSelector.addGoal(4, this.guardianAttackGoal = new Guardian.GuardianAttackGoal(this)); // CraftBukkit - assign field - this.goalSelector.addGoal(5, moveTowardsRestrictionGoal); + this.goalSelector.addGoal(5, goal); this.goalSelector.addGoal(7, this.randomStrollGoal); this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F)); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Monster.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Monster.java.patch index 2e5c2131d42e..2e282f76bf20 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Monster.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Monster.java.patch @@ -4,8 +4,8 @@ return false; } else { DimensionType dimensionType = level.dimensionType(); -- int i = dimensionType.monsterSpawnBlockLightLimit(); -+ int i = level.getLevel().paperConfig().entities.spawning.monsterSpawnMaxLightLevel.or(dimensionType.monsterSpawnBlockLightLimit()); // Paper - Configurable max block light for monster spawning - if (i < 15 && level.getBrightness(LightLayer.BLOCK, pos) > i) { +- int blockLightLimit = dimensionType.monsterSpawnBlockLightLimit(); ++ int blockLightLimit = level.getLevel().paperConfig().entities.spawning.monsterSpawnMaxLightLevel.or(dimensionType.monsterSpawnBlockLightLimit()); // Paper - Configurable max block light for monster spawning + if (blockLightLimit < 15 && level.getBrightness(LightLayer.BLOCK, pos) > blockLightLimit) { return false; } else { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Phantom.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Phantom.java.patch index 65f2c4e2f0cf..26878838bcfa 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Phantom.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Phantom.java.patch @@ -1,17 +1,17 @@ --- a/net/minecraft/world/entity/monster/Phantom.java +++ b/net/minecraft/world/entity/monster/Phantom.java -@@ -49,6 +_,10 @@ - Vec3 moveTargetPoint = Vec3.ZERO; - @Nullable public BlockPos anchorPoint; - Phantom.AttackPhase attackPhase = Phantom.AttackPhase.CIRCLE; +@@ -50,6 +_,10 @@ + private Vec3 moveTargetPoint = Vec3.ZERO; + public @Nullable BlockPos anchorPoint; + private Phantom.AttackPhase attackPhase = Phantom.AttackPhase.CIRCLE; + // Paper start + public java.util.@Nullable UUID spawningEntity; + public boolean shouldBurnInDay = true; + // Paper end - public Phantom(EntityType type, Level level) { + public Phantom(final EntityType type, final Level level) { super(type, level); -@@ -136,6 +_,13 @@ +@@ -137,6 +_,13 @@ } } @@ -23,9 +23,9 @@ + // Paper end + @Override - protected void checkFallDamage(double y, boolean onGround, BlockState state, BlockPos pos) { + protected void checkFallDamage(final double ya, final boolean onGround, final BlockState onState, final BlockPos pos) { } -@@ -164,6 +_,10 @@ +@@ -165,6 +_,10 @@ super.readAdditionalSaveData(input); this.anchorPoint = input.read("anchor_pos", BlockPos.CODEC).orElse(null); this.setPhantomSize(input.getIntOr("size", 0)); @@ -36,7 +36,7 @@ } @Override -@@ -171,6 +_,10 @@ +@@ -172,6 +_,10 @@ super.addAdditionalSaveData(output); output.storeNullable("anchor_pos", BlockPos.CODEC, this.anchorPoint); output.putInt("size", this.getPhantomSize()); @@ -47,10 +47,10 @@ } @Override -@@ -244,8 +_,10 @@ +@@ -240,8 +_,10 @@ - for (Player player : nearbyPlayers) { - if (Phantom.this.canAttack(serverLevel, player, TargetingConditions.DEFAULT)) { + for (Player player : players) { + if (Phantom.this.canAttack(level, player, TargetingConditions.DEFAULT)) { - Phantom.this.setTarget(player); + if (!level().paperConfig().entities.behavior.phantomsOnlyAttackInsomniacs || EntitySelector.IS_INSOMNIAC.test(player)) { // Paper - Add phantom creative and insomniac controls + Phantom.this.setTarget(player, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER); // CraftBukkit - reason diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Ravager.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Ravager.java.patch index a0e52436cfed..9b646732659e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Ravager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Ravager.java.patch @@ -1,31 +1,31 @@ --- a/net/minecraft/world/entity/monster/Ravager.java +++ b/net/minecraft/world/entity/monster/Ravager.java -@@ -155,12 +_,19 @@ - BlockState blockState = serverLevel.getBlockState(blockPos); - Block block = blockState.getBlock(); +@@ -153,12 +_,19 @@ + BlockState state = serverLevel.getBlockState(pos); + Block block = state.getBlock(); if (block instanceof LeavesBlock) { + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockPos, blockState.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, pos, state.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state + continue; + } + // CraftBukkit end - flag = serverLevel.destroyBlock(blockPos, true, this) || flag; + destroyedBlock = serverLevel.destroyBlock(pos, true, this) || destroyedBlock; } } - if (!flag && this.onGround()) { + if (!destroyedBlock && this.onGround()) { + if (new com.destroystokyo.paper.event.entity.EntityJumpEvent(getBukkitLivingEntity()).callEvent()) { // Paper - Entity Jump API this.jumpFromGround(); + } else { this.setJumping(false); } // Paper - Entity Jump API; setJumping(false) stops a potential loop } } -@@ -250,7 +_,7 @@ - double d = entity.getX() - this.getX(); - double d1 = entity.getZ() - this.getZ(); - double max = Math.max(d * d + d1 * d1, 0.001); -- entity.push(d / max * 4.0, 0.2, d1 / max * 4.0); -+ entity.push(d / max * 4.0, 0.2, d1 / max * 4.0, this); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent +@@ -249,7 +_,7 @@ + double xd = entity.getX() - this.getX(); + double zd = entity.getZ() - this.getZ(); + double dd = Math.max(xd * xd + zd * zd, 0.001); +- entity.push(xd / dd * 4.0, 0.2, zd / dd * 4.0); ++ entity.push(xd / dd * 4.0, 0.2, zd / dd * 4.0, this); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Shulker.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Shulker.java.patch index a709e8da5f32..877221a21da5 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Shulker.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Shulker.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/monster/Shulker.java +++ b/net/minecraft/world/entity/monster/Shulker.java -@@ -276,8 +_,10 @@ +@@ -277,8 +_,10 @@ } @Override @@ -13,32 +13,32 @@ if (this.level().isClientSide()) { this.clientOldAttachPosition = this.blockPosition(); } -@@ -388,6 +_,14 @@ - && this.level().getWorldBorder().isWithinBounds(blockPos1) - && this.level().noCollision(this, new AABB(blockPos1).deflate(1.0E-6))) { - Direction direction = this.findAttachableSurface(blockPos1); +@@ -389,6 +_,14 @@ + && this.level().getWorldBorder().isWithinBounds(target) + && this.level().noCollision(this, new AABB(target).deflate(1.0E-6))) { + Direction attachmentDirection = this.findAttachableSurface(target); + // CraftBukkit start -+ org.bukkit.event.entity.EntityTeleportEvent teleportEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTeleportEvent(this, blockPos1.getX(), blockPos1.getY(), blockPos1.getZ()); ++ org.bukkit.event.entity.EntityTeleportEvent teleportEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTeleportEvent(this, target.getX(), target.getY(), target.getZ()); + if (teleportEvent.isCancelled() || teleportEvent.getTo() == null) { // Paper + return false; + } else { -+ blockPos1 = org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(teleportEvent.getTo()); ++ target = org.bukkit.craftbukkit.util.CraftLocation.toBlockPos(teleportEvent.getTo()); + } + // CraftBukkit end - if (direction != null) { + if (attachmentDirection != null) { this.unRide(); - this.setAttachFace(direction); -@@ -452,7 +_,12 @@ - if (shulker != null) { - shulker.setVariant(this.getVariant()); - shulker.snapTo(vec3); -- this.level().addFreshEntity(shulker); + this.setAttachFace(attachmentDirection); +@@ -453,7 +_,12 @@ + if (baby != null) { + baby.setVariant(this.getVariant()); + baby.snapTo(oldPosition); +- this.level().addFreshEntity(baby); + // Paper start - Call ShulkerDuplicateEvent -+ if (!new io.papermc.paper.event.entity.ShulkerDuplicateEvent((org.bukkit.entity.Shulker) shulker.getBukkitEntity(), (org.bukkit.entity.Shulker) this.getBukkitEntity()).callEvent()) { ++ if (!new io.papermc.paper.event.entity.ShulkerDuplicateEvent((org.bukkit.entity.Shulker) baby.getBukkitEntity(), (org.bukkit.entity.Shulker) this.getBukkitEntity()).callEvent()) { + return; + } + // Paper end - Call ShulkerDuplicateEvent -+ this.level().addFreshEntity(shulker, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - the mysteries of life ++ this.level().addFreshEntity(baby, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - the mysteries of life } } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Silverfish.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Silverfish.java.patch index d764f077a1cb..0bf09e3dd85b 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Silverfish.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Silverfish.java.patch @@ -10,15 +10,15 @@ } } @@ -168,9 +_,14 @@ - BlockPos blockPos = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5, this.mob.getZ()).relative(this.selectedDirection); - BlockState blockState = levelAccessor.getBlockState(blockPos); + BlockPos pos = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5, this.mob.getZ()).relative(this.selectedDirection); + BlockState blockState = level.getBlockState(pos); if (InfestedBlock.isCompatibleHostBlock(blockState)) { + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockPos, InfestedBlock.infestedStateByHost(blockState))) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, pos, InfestedBlock.infestedStateByHost(blockState))) { + return; + } + // CraftBukkit end - levelAccessor.setBlock(blockPos, InfestedBlock.infestedStateByHost(blockState), Block.UPDATE_ALL); + level.setBlock(pos, InfestedBlock.infestedStateByHost(blockState), Block.UPDATE_ALL); this.mob.spawnAnim(); - this.mob.discard(); + this.mob.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.ENTER_BLOCK); // CraftBukkit - add Bukkit remove cause @@ -26,15 +26,15 @@ } } @@ -210,6 +_,12 @@ - BlockState blockState = level.getBlockState(blockPos1); + BlockState blockState = level.getBlockState(testPos); Block block = blockState.getBlock(); if (block instanceof InfestedBlock) { + // CraftBukkit start -+ BlockState afterState = getServerLevel(level).getGameRules().get(GameRules.MOB_GRIEFING) ? blockState.getFluidState().createLegacyBlock() : ((InfestedBlock) block).hostStateByInfested(level.getBlockState(blockPos1)); // Paper - fix wrong block state -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.silverfish, blockPos1, afterState)) { // Paper - fix wrong block state ++ BlockState afterState = getServerLevel(level).getGameRules().get(GameRules.MOB_GRIEFING) ? blockState.getFluidState().createLegacyBlock() : ((InfestedBlock) block).hostStateByInfested(level.getBlockState(testPos)); // Paper - fix wrong block state ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.silverfish, testPos, afterState)) { // Paper - fix wrong block state + continue; + } + // CraftBukkit end if (getServerLevel(level).getGameRules().get(GameRules.MOB_GRIEFING)) { - level.destroyBlock(blockPos1, true, this.silverfish); + level.destroyBlock(testPos, true, this.silverfish); } else { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Slime.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Slime.java.patch index 9ed5f13b01f3..5bcb18c5999e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Slime.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Slime.java.patch @@ -6,7 +6,7 @@ private boolean wasOnGround = false; + private boolean canWander = true; // Paper - Slime pathfinder events - public Slime(EntityType type, Level level) { + public Slime(final EntityType type, final Level level) { super(type, level); @@ -113,6 +_,7 @@ super.addAdditionalSaveData(output); @@ -28,34 +28,34 @@ } @Override -- public void remove(Entity.RemovalReason reason) { -+ public void remove(Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause eventCause) { // CraftBukkit - add Bukkit remove cause +- public void remove(final Entity.RemovalReason reason) { ++ public void remove(final Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause eventCause) { // CraftBukkit - add Bukkit remove cause int size = this.getSize(); if (!this.level().isClientSide() && size > 1 && this.isDeadOrDying()) { float width = this.getDimensions(this.getPose()).width(); @@ -202,18 +_,43 @@ - int i = size / 2; - int i1 = 2 + this.random.nextInt(3); + int halfSize = size / 2; + int count = 2 + this.random.nextInt(3); PlayerTeam team = this.getTeam(); + // CraftBukkit start -+ org.bukkit.event.entity.SlimeSplitEvent event = new org.bukkit.event.entity.SlimeSplitEvent((org.bukkit.entity.Slime) this.getBukkitEntity(), i1); ++ org.bukkit.event.entity.SlimeSplitEvent event = new org.bukkit.event.entity.SlimeSplitEvent((org.bukkit.entity.Slime) this.getBukkitEntity(), count); + if (event.callEvent() && event.getCount() > 0) { -+ i1 = event.getCount(); ++ count = event.getCount(); + } else { + super.remove(reason, eventCause); // CraftBukkit - add Bukkit remove cause + return; + } + -+ java.util.List slimes = new java.util.ArrayList<>(i1); ++ java.util.List slimes = new java.util.ArrayList<>(count); + // CraftBukkit end - for (int i2 = 0; i2 < i1; i2++) { - float f1 = (i2 % 2 - 0.5F) * f; - float f2 = (i2 / 2 - 0.5F) * f; -- this.convertTo(this.getType(), new ConversionParams(ConversionType.SPLIT_ON_DEATH, false, false, team), EntitySpawnReason.TRIGGERED, mob -> { -+ Slime converted = this.convertTo(this.getType(), new ConversionParams(ConversionType.SPLIT_ON_DEATH, false, false, team), EntitySpawnReason.TRIGGERED, (mob) -> { // CraftBukkit - mob.setSize(i, true); - mob.snapTo(this.getX() + f1, this.getY() + 0.5, this.getZ() + f2, this.random.nextFloat() * 360.0F, 0.0F); + for (int i = 0; i < count; i++) { + float xd = (i % 2 - 0.5F) * xzSlimeSpawnOffset; + float zd = (i / 2 - 0.5F) * xzSlimeSpawnOffset; +- this.convertTo(this.getType(), new ConversionParams(ConversionType.SPLIT_ON_DEATH, false, false, team), EntitySpawnReason.TRIGGERED, slime -> { ++ Slime converted = this.convertTo(this.getType(), new ConversionParams(ConversionType.SPLIT_ON_DEATH, false, false, team), EntitySpawnReason.TRIGGERED, slime -> { // CraftBukkit + slime.setSize(halfSize, true); + slime.snapTo(this.getX() + xd, this.getY() + 0.5, this.getZ() + zd, this.random.nextFloat() * 360.0F, 0.0F); - }); - } + // CraftBukkit start @@ -81,8 +81,8 @@ } @Override -@@ -279,7 +_,11 @@ - return checkMobSpawnRules(entityType, level, spawnReason, pos, random); +@@ -276,7 +_,11 @@ + return checkMobSpawnRules(type, level, spawnReason, pos, random); } - if (level.getBiome(pos).is(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS) && pos.getY() > 50 && pos.getY() < 70) { @@ -91,24 +91,24 @@ + final double minHeightSwamp = level.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.surfaceBiome.minimum; + // Paper end - Replace rules for Height in Swamp Biomes + if (level.getBiome(pos).is(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS) && pos.getY() > minHeightSwamp && pos.getY() < maxHeightSwamp) { // Paper - Replace rules for Height in Swamp Biomes - float value = level.environmentAttributes().getValue(EnvironmentAttributes.SURFACE_SLIME_SPAWN_CHANCE, pos); - if (random.nextFloat() < value && level.getMaxLocalRawBrightness(pos) <= random.nextInt(8)) { - return checkMobSpawnRules(entityType, level, spawnReason, pos, random); -@@ -291,8 +_,11 @@ + float surfaceSlimeSpawnChance = level.environmentAttributes().getValue(EnvironmentAttributes.SURFACE_SLIME_SPAWN_CHANCE, pos); + if (random.nextFloat() < surfaceSlimeSpawnChance && level.getMaxLocalRawBrightness(pos) <= random.nextInt(8)) { + return checkMobSpawnRules(type, level, spawnReason, pos, random); +@@ -288,8 +_,11 @@ } - ChunkPos chunkPos = new ChunkPos(pos); -- boolean flag = WorldgenRandom.seedSlimeChunk(chunkPos.x, chunkPos.z, ((WorldGenLevel)level).getSeed(), 987234911L).nextInt(10) == 0; -- if (random.nextInt(10) == 0 && flag && pos.getY() < 40) { -+ boolean flag = level.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(chunkPos.x, chunkPos.z, ((WorldGenLevel) level).getSeed(), level.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Paper + ChunkPos chunkPos = ChunkPos.containing(pos); +- boolean slimeChunk = WorldgenRandom.seedSlimeChunk(chunkPos.x(), chunkPos.z(), ((WorldGenLevel)level).getSeed(), 987234911L).nextInt(10) == 0; +- if (random.nextInt(10) == 0 && slimeChunk && pos.getY() < 40) { ++ boolean slimeChunk = level.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(chunkPos.x(), chunkPos.z(), ((WorldGenLevel) level).getSeed(), level.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Paper + // Paper start - Replace rules for Height in Slime Chunks + final double maxHeightSlimeChunk = level.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.slimeChunk.maximum; -+ if (random.nextInt(10) == 0 && flag && pos.getY() < maxHeightSlimeChunk) { ++ if (random.nextInt(10) == 0 && slimeChunk && pos.getY() < maxHeightSlimeChunk) { + // Paper end - Replace rules for Height in Slime Chunks - return checkMobSpawnRules(entityType, level, spawnReason, pos, random); + return checkMobSpawnRules(type, level, spawnReason, pos, random); } } -@@ -350,6 +_,16 @@ +@@ -347,6 +_,16 @@ return super.getDefaultDimensions(pose).scale(this.getSize()); } @@ -122,10 +122,10 @@ + } + // Paper end - Slime pathfinder events + - static class SlimeAttackGoal extends Goal { + private static class SlimeAttackGoal extends Goal { private final Slime slime; private int growTiredTimer; -@@ -362,7 +_,16 @@ +@@ -359,7 +_,16 @@ @Override public boolean canUse() { LivingEntity target = this.slime.getTarget(); @@ -143,7 +143,7 @@ } @Override -@@ -374,7 +_,16 @@ +@@ -371,7 +_,16 @@ @Override public boolean canContinueToUse() { LivingEntity target = this.slime.getTarget(); @@ -161,7 +161,7 @@ } @Override -@@ -393,6 +_,13 @@ +@@ -390,6 +_,13 @@ slimeMoveControl.setDirection(this.slime.getYRot(), this.slime.isDealsDamage()); } } @@ -174,8 +174,8 @@ + // Paper end - Slime pathfinder events } - static class SlimeFloatGoal extends Goal { -@@ -406,7 +_,7 @@ + private static class SlimeFloatGoal extends Goal { +@@ -403,7 +_,7 @@ @Override public boolean canUse() { @@ -184,7 +184,7 @@ } @Override -@@ -436,7 +_,7 @@ +@@ -433,7 +_,7 @@ @Override public boolean canUse() { @@ -193,7 +193,7 @@ } @Override -@@ -514,7 +_,7 @@ +@@ -511,7 +_,7 @@ @Override public boolean canUse() { @@ -202,7 +202,7 @@ && (this.slime.onGround() || this.slime.isInWater() || this.slime.isInLava() || this.slime.hasEffect(MobEffects.LEVITATION)) && this.slime.getMoveControl() instanceof Slime.SlimeMoveControl; } -@@ -524,6 +_,11 @@ +@@ -521,6 +_,11 @@ if (--this.nextRandomizeTime <= 0) { this.nextRandomizeTime = this.adjustedTickDelay(40 + this.slime.getRandom().nextInt(60)); this.chosenDegrees = this.slime.getRandom().nextInt(360); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Strider.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Strider.java.patch index 7da583c2e8c5..1293ab1271f9 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Strider.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Strider.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/entity/monster/Strider.java +++ b/net/minecraft/world/entity/monster/Strider.java -@@ -294,7 +_,14 @@ - || blockStateOnLegacy.is(BlockTags.STRIDER_WARM_BLOCKS) +@@ -299,7 +_,14 @@ + || stateOn.is(BlockTags.STRIDER_WARM_BLOCKS) || this.getFluidHeight(FluidTags.LAVA) > 0.0; - boolean flag1 = this.getVehicle() instanceof Strider strider && strider.isSuffocating(); -- this.setSuffocating(!flag || flag1); + boolean onWarmStrider = this.getVehicle() instanceof Strider strider && !strider.isSuffocating(); +- this.setSuffocating(!inWarmBlocks && !onWarmStrider); + // CraftBukkit start -+ boolean suffocating = !flag || flag1; ++ boolean suffocating = !inWarmBlocks && !onWarmStrider; + if (suffocating ^ this.isSuffocating()) { + if (org.bukkit.craftbukkit.event.CraftEventFactory.callStriderTemperatureChangeEvent(this, suffocating)) { + this.setSuffocating(suffocating); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Vex.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Vex.java.patch index 2c78ad8e6565..aa33a3442320 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Vex.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Vex.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/monster/Vex.java +++ b/net/minecraft/world/entity/monster/Vex.java -@@ -288,7 +_,7 @@ +@@ -293,7 +_,7 @@ @Override public void start() { Mob owner = Vex.this.getOwner(); @@ -9,15 +9,15 @@ super.start(); } } -@@ -347,7 +_,10 @@ +@@ -355,7 +_,10 @@ - for (int i = 0; i < 3; i++) { - BlockPos blockPos = boundOrigin.offset(Vex.this.random.nextInt(15) - 7, Vex.this.random.nextInt(11) - 5, Vex.this.random.nextInt(15) - 7); -- if (Vex.this.level().isEmptyBlock(blockPos)) { + for (int attempts = 0; attempts < 3; attempts++) { + BlockPos testPos = boundOrigin.offset(Vex.this.random.nextInt(15) - 7, Vex.this.random.nextInt(11) - 5, Vex.this.random.nextInt(15) - 7); +- if (Vex.this.level().isEmptyBlock(testPos)) { + // Paper start - Don't load chunks -+ final net.minecraft.world.level.block.state.BlockState blockState = Vex.this.level().getBlockStateIfLoaded(blockPos); ++ final net.minecraft.world.level.block.state.BlockState blockState = Vex.this.level().getBlockStateIfLoaded(testPos); + if (blockState != null && blockState.isAir()) { + // Paper end - Don't load chunks - Vex.this.moveControl.setWantedPosition(blockPos.getX() + 0.5, blockPos.getY() + 0.5, blockPos.getZ() + 0.5, 0.25); + Vex.this.moveControl.setWantedPosition(testPos.getX() + 0.5, testPos.getY() + 0.5, testPos.getZ() + 0.5, 0.25); if (Vex.this.getTarget() == null) { - Vex.this.getLookControl().setLookAt(blockPos.getX() + 0.5, blockPos.getY() + 0.5, blockPos.getZ() + 0.5, 180.0F, 20.0F); + Vex.this.getLookControl().setLookAt(testPos.getX() + 0.5, testPos.getY() + 0.5, testPos.getZ() + 0.5, 180.0F, 20.0F); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Witch.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Witch.java.patch index a0ab0a12072c..ba743428ffc4 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Witch.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Witch.java.patch @@ -1,26 +1,26 @@ --- a/net/minecraft/world/entity/monster/Witch.java +++ b/net/minecraft/world/entity/monster/Witch.java @@ -123,8 +_,14 @@ - ItemStack mainHandItem = this.getMainHandItem(); + ItemStack itemStack = this.getMainHandItem(); this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY); - PotionContents potionContents = mainHandItem.get(DataComponents.POTION_CONTENTS); + PotionContents potion = itemStack.get(DataComponents.POTION_CONTENTS); + // Paper start - WitchConsumePotionEvent -+ if (mainHandItem.is(Items.POTION)) { -+ com.destroystokyo.paper.event.entity.WitchConsumePotionEvent event = new com.destroystokyo.paper.event.entity.WitchConsumePotionEvent((org.bukkit.entity.Witch) this.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(mainHandItem)); -+ potionContents = event.callEvent() ? org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getPotion()).get(DataComponents.POTION_CONTENTS) : null; ++ if (itemStack.is(Items.POTION)) { ++ com.destroystokyo.paper.event.entity.WitchConsumePotionEvent event = new com.destroystokyo.paper.event.entity.WitchConsumePotionEvent((org.bukkit.entity.Witch) this.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack)); ++ potion = event.callEvent() ? org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getPotion()).get(DataComponents.POTION_CONTENTS) : null; + } + // Paper end - WitchConsumePotionEvent - if (mainHandItem.is(Items.POTION) && potionContents != null) { -- potionContents.forEachEffect(this::addEffect, mainHandItem.getOrDefault(DataComponents.POTION_DURATION_SCALE, 1.0F)); -+ potionContents.forEachEffect(effect -> this.addEffect(effect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK), mainHandItem.getOrDefault(DataComponents.POTION_DURATION_SCALE, 1.0F)); // CraftBukkit + if (itemStack.is(Items.POTION) && potion != null) { +- potion.forEachEffect(this::addEffect, itemStack.getOrDefault(DataComponents.POTION_DURATION_SCALE, 1.0F)); ++ potion.forEachEffect(effect -> this.addEffect(effect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK), itemStack.getOrDefault(DataComponents.POTION_DURATION_SCALE, 1.0F)); // CraftBukkit } this.gameEvent(GameEvent.DRINK); @@ -148,26 +_,7 @@ } - if (holder != null) { -- this.setItemSlot(EquipmentSlot.MAINHAND, PotionContents.createItemStack(Items.POTION, holder)); + if (potion != null) { +- this.setItemSlot(EquipmentSlot.MAINHAND, PotionContents.createItemStack(Items.POTION, potion)); - this.usingTime = this.getMainHandItem().getUseDuration(this); - this.setUsingItem(true); - if (!this.isSilent()) { @@ -37,10 +37,10 @@ - ); - } - -- AttributeInstance attribute = this.getAttribute(Attributes.MOVEMENT_SPEED); -- attribute.removeModifier(SPEED_MODIFIER_DRINKING_ID); -- attribute.addTransientModifier(SPEED_MODIFIER_DRINKING); -+ this.setDrinkingPotion(PotionContents.createItemStack(Items.POTION, holder)); // Paper - logic moved into setDrinkingPotion, copy exact impl into the method and then comment out +- AttributeInstance speed = this.getAttribute(Attributes.MOVEMENT_SPEED); +- speed.removeModifier(SPEED_MODIFIER_DRINKING_ID); +- speed.addTransientModifier(SPEED_MODIFIER_DRINKING); ++ this.setDrinkingPotion(PotionContents.createItemStack(Items.POTION, potion)); // Paper - logic moved into setDrinkingPotion, copy exact impl into the method and then comment out } } @@ -58,10 +58,10 @@ + this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.WITCH_DRINK, this.getSoundSource(), 1.0F, 0.8F + this.random.nextFloat() * 0.4F); + } + -+ AttributeInstance attribute = this.getAttribute(Attributes.MOVEMENT_SPEED); ++ AttributeInstance speed = this.getAttribute(Attributes.MOVEMENT_SPEED); + -+ attribute.removeModifier(Witch.SPEED_MODIFIER_DRINKING_ID); -+ attribute.addTransientModifier(Witch.SPEED_MODIFIER_DRINKING); ++ speed.removeModifier(Witch.SPEED_MODIFIER_DRINKING_ID); ++ speed.addTransientModifier(Witch.SPEED_MODIFIER_DRINKING); + } + // Paper end + @@ -71,7 +71,7 @@ @@ -245,6 +_,13 @@ if (this.level() instanceof ServerLevel serverLevel) { - ItemStack itemStack = PotionContents.createItemStack(Items.SPLASH_POTION, holder); + ItemStack itemStack = PotionContents.createItemStack(Items.SPLASH_POTION, potion); + // Paper start - WitchThrowPotionEvent + com.destroystokyo.paper.event.entity.WitchThrowPotionEvent event = new com.destroystokyo.paper.event.entity.WitchThrowPotionEvent((org.bukkit.entity.Witch) this.getBukkitEntity(), (org.bukkit.entity.LivingEntity) target.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack)); + if (!event.callEvent()) { @@ -79,6 +79,6 @@ + } + itemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getPotion()); + // Paper end - WitchThrowPotionEven - Projectile.spawnProjectileUsingShoot(ThrownSplashPotion::new, serverLevel, itemStack, this, d, d1 + squareRoot * 0.2, d2, 0.75F, 8.0F); - } - + Projectile.spawnProjectileUsingShoot( + ThrownSplashPotion::new, serverLevel, itemStack, this, xd, yd + dist * 0.2, zd, dist <= 2.0 ? 0.45F : 0.75F, 8.0F + ); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/breeze/Breeze.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/breeze/Breeze.java.patch index c6c994ce5369..0cbe4ba51bf8 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/breeze/Breeze.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/breeze/Breeze.java.patch @@ -1,10 +1,14 @@ --- a/net/minecraft/world/entity/monster/breeze/Breeze.java +++ b/net/minecraft/world/entity/monster/breeze/Breeze.java -@@ -245,6 +_,7 @@ +@@ -242,6 +_,11 @@ @Override - public boolean canAttackType(EntityType type) { -+ if (this.getTarget() != null) return this.getTarget().getType() == type; // SPIGOT-7957: Allow attack if target from brain was set - return type == EntityType.PLAYER || type == EntityType.IRON_GOLEM; + public boolean canAttack(final LivingEntity target) { ++ // TODO - snapshot - reimplement but without reintroducing MC-199589 ++ // CraftBukkit start - SPIGOT-7957: Allow attack if target from brain was set ++ // LivingEntity targetFromBrain = this.getBrain().getMemory(MemoryModuleType.ATTACK_TARGET).orElse(null); ++ // if (targetFromBrain != null) return target.is(targetFromBrain); ++ // CraftBukkit end + return (target.is(EntityType.PLAYER) || target.is(EntityType.IRON_GOLEM)) && super.canAttack(target); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/creaking/Creaking.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/creaking/Creaking.java.patch index cebc12470b3a..e1f7aa867c05 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/creaking/Creaking.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/creaking/Creaking.java.patch @@ -1,18 +1,18 @@ --- a/net/minecraft/world/entity/monster/creaking/Creaking.java +++ b/net/minecraft/world/entity/monster/creaking/Creaking.java -@@ -192,9 +_,9 @@ +@@ -191,9 +_,9 @@ } @Override -- public void push(double x, double y, double z) { -+ public void push(double x, double y, double z, @Nullable Entity pushingEntity) { // Paper - add push source entity param +- public void push(final double xa, final double ya, final double za) { ++ public void push(final double xa, final double ya, final double za, @Nullable final Entity pushingEntity) { // Paper - add push source entity param if (this.canMove()) { -- super.push(x, y, z); -+ super.push(x, y, z, pushingEntity); // Paper - add push source entity param +- super.push(xa, ya, za); ++ super.push(xa, ya, za, pushingEntity); // Paper - add push source entity param } } -@@ -319,7 +_,7 @@ +@@ -318,7 +_,7 @@ } this.makeSound(this.getDeathSound()); @@ -20,16 +20,16 @@ + this.remove(Entity.RemovalReason.DISCARDED, null); // CraftBukkit - add Bukkit remove cause } - public void creakingDeathEffects(DamageSource damageSource) { -@@ -447,9 +_,9 @@ + public void creakingDeathEffects(final DamageSource source) { +@@ -446,9 +_,9 @@ } @Override -- public void knockback(double strength, double x, double z) { -+ public void knockback(double strength, double x, double z, @Nullable Entity attacker, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause cause) { // Paper - knockback events +- public void knockback(final double power, final double xd, final double zd) { ++ public void knockback(final double power, final double xd, final double zd, @Nullable final Entity attacker, final io.papermc.paper.event.entity.EntityKnockbackEvent.Cause cause) { // Paper - knockback events if (this.canMove()) { -- super.knockback(strength, x, z); -+ super.knockback(strength, x, z, attacker, cause); // Paper - knockback events +- super.knockback(power, xd, zd); ++ super.knockback(power, xd, zd, attacker, cause); // Paper - knockback events } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/hoglin/Hoglin.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/hoglin/Hoglin.java.patch index 3d2981564dac..690df3a34440 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/hoglin/Hoglin.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/hoglin/Hoglin.java.patch @@ -1,11 +1,13 @@ --- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java +++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java -@@ -266,7 +_,12 @@ +@@ -237,9 +_,12 @@ } private void finishConversion() { -- this.convertTo(EntityType.ZOGLIN, ConversionParams.single(this, true, false), mob -> mob.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0))); -+ final Entity converted = this.convertTo(EntityType.ZOGLIN, ConversionParams.single(this, true, false), mob -> {mob.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0));}, org.bukkit.event.entity.EntityTransformEvent.TransformReason.PIGLIN_ZOMBIFIED, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.PIGLIN_ZOMBIFIED); // CraftBukkit - add spawn and transform reasons +- this.convertTo( +- EntityType.ZOGLIN, ConversionParams.single(this, true, false), zoglin -> zoglin.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0)) +- ); ++ final Entity converted = this.convertTo(EntityType.ZOGLIN, ConversionParams.single(this, true, false), zoglin -> {zoglin.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0));}, org.bukkit.event.entity.EntityTransformEvent.TransformReason.PIGLIN_ZOMBIFIED, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.PIGLIN_ZOMBIFIED); // CraftBukkit - add spawn and transform reasons + // Paper start - Fix issues with mob conversion; reset to prevent event spam + if (converted == null) { + this.timeInOverworld = 0; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/hoglin/HoglinBase.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/hoglin/HoglinBase.java.patch index 273ab55a4d24..52948f5cd93f 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/hoglin/HoglinBase.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/hoglin/HoglinBase.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/monster/hoglin/HoglinBase.java +++ b/net/minecraft/world/entity/monster/hoglin/HoglinBase.java -@@ -45,7 +_,7 @@ - double d3 = d * (hoglin.level().random.nextFloat() * 0.5F + 0.2F); - Vec3 vec3 = new Vec3(d1, 0.0, d2).normalize().scale(d3).yRot(f); - double d4 = d * hoglin.level().random.nextFloat() * 0.5; -- target.push(vec3.x, d4, vec3.z); -+ target.push(vec3.x, d4, vec3.z, hoglin); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent +@@ -47,7 +_,7 @@ + double horizontalScale = effectiveKnockbackPower * (random.nextFloat() * 0.5F + 0.2F); + Vec3 horizontalPushVector = new Vec3(xd, 0.0, zd).normalize().scale(horizontalScale).yRot(horizontalPushAngle); + double verticalScale = effectiveKnockbackPower * random.nextFloat() * 0.5; +- target.push(horizontalPushVector.x, verticalScale, horizontalPushVector.z); ++ target.push(horizontalPushVector.x, verticalScale, horizontalPushVector.z, body); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent target.hurtMarked = true; } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Evoker.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Evoker.java.patch index 1c8641669946..48f194a1f2da 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Evoker.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Evoker.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/monster/illager/Evoker.java +++ b/net/minecraft/world/entity/monster/illager/Evoker.java -@@ -247,7 +_,7 @@ - serverLevel.getScoreboard().addPlayerToTeam(vex.getScoreboardName(), team); +@@ -271,7 +_,7 @@ + serverLevel.getScoreboard().addPlayerToTeam(vex.getScoreboardName(), evokerTeam); } - serverLevel.addFreshEntityWithPassengers(vex); + serverLevel.addFreshEntityWithPassengers(vex, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPELL); // CraftBukkit - Add SpawnReason - serverLevel.gameEvent(GameEvent.ENTITY_PLACE, blockPos, GameEvent.Context.of(Evoker.this)); + serverLevel.gameEvent(GameEvent.ENTITY_PLACE, pos, GameEvent.Context.of(Evoker.this)); } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Illusioner.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Illusioner.java.patch index 07a12f5fd228..9be002440e38 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Illusioner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Illusioner.java.patch @@ -1,39 +1,39 @@ --- a/net/minecraft/world/entity/monster/illager/Illusioner.java +++ b/net/minecraft/world/entity/monster/illager/Illusioner.java -@@ -174,7 +_,8 @@ +@@ -175,7 +_,8 @@ @Override - public void performRangedAttack(LivingEntity target, float distanceFactor) { -- ItemStack itemInHand = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW)); + public void performRangedAttack(final LivingEntity target, final float power) { +- ItemStack bowItem = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW)); + net.minecraft.world.InteractionHand hand = ProjectileUtil.getWeaponHoldingHand(this, Items.BOW); // Paper - call EntityShootBowEvent -+ ItemStack itemInHand = this.getItemInHand(hand); // Paper - call EntityShootBowEvent - ItemStack projectile = this.getProjectile(itemInHand); - AbstractArrow mobArrow = ProjectileUtil.getMobArrow(this, projectile, distanceFactor, itemInHand); - double d = target.getX() - this.getX(); -@@ -182,9 +_,21 @@ - double d2 = target.getZ() - this.getZ(); - double squareRoot = Math.sqrt(d * d + d2 * d2); ++ ItemStack bowItem = this.getItemInHand(hand); // Paper - call EntityShootBowEvent + ItemStack projectile = this.getProjectile(bowItem); + AbstractArrow arrow = ProjectileUtil.getMobArrow(this, projectile, power, bowItem); + double xd = target.getX() - this.getX(); +@@ -183,9 +_,21 @@ + double zd = target.getZ() - this.getZ(); + double distanceToTarget = Math.sqrt(xd * xd + zd * zd); if (this.level() instanceof ServerLevel serverLevel) { - Projectile.spawnProjectileUsingShoot( + Projectile.Delayed delayedEntity = Projectile.spawnProjectileUsingShootDelayed( // Paper - delayed - mobArrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4 + arrow, serverLevel, projectile, xd, yd + distanceToTarget * 0.2F, zd, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4 ); + + // Paper start - call EntityShootBowEvent -+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, itemInHand, mobArrow.getPickupItem(), mobArrow, hand, distanceFactor, true); ++ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, bowItem, arrow.getPickupItem(), arrow, hand, power, true); + if (event.isCancelled()) { + event.getProjectile().remove(); + return; + } + -+ if (event.getProjectile() == mobArrow.getBukkitEntity()) { ++ if (event.getProjectile() == arrow.getBukkitEntity()) { + delayedEntity.spawn(); + } + // Paper end - call EntityShootBowEvent } this.playSound(SoundEvents.SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); -@@ -231,7 +_,7 @@ +@@ -237,7 +_,7 @@ @Override protected void performSpellCasting() { @@ -42,7 +42,7 @@ } @Override -@@ -263,7 +_,7 @@ +@@ -274,7 +_,7 @@ @Override protected void performSpellCasting() { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Pillager.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Pillager.java.patch index 575d644e7872..cce9a641bcac 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Pillager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Pillager.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/entity/monster/illager/Pillager.java @@ -215,7 +_,7 @@ this.onItemPickup(entity); - ItemStack itemStack = this.inventory.addItem(item); - if (itemStack.isEmpty()) { + ItemStack remainder = this.inventory.addItem(itemStack); + if (remainder.isEmpty()) { - entity.discard(); + entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause } else { - item.setCount(itemStack.getCount()); + itemStack.setCount(remainder.getCount()); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/SpellcasterIllager.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/SpellcasterIllager.java.patch index 5eaaf1fba832..3c1ea820e027 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/SpellcasterIllager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/SpellcasterIllager.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/monster/illager/SpellcasterIllager.java +++ b/net/minecraft/world/entity/monster/illager/SpellcasterIllager.java -@@ -210,6 +_,11 @@ +@@ -216,6 +_,11 @@ public void tick() { this.attackWarmupDelay--; if (this.attackWarmupDelay == 0) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Vindicator.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Vindicator.java.patch index 9e3dfa15dd41..a88a50bae4af 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Vindicator.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Vindicator.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/entity/monster/illager/Vindicator.java +++ b/net/minecraft/world/entity/monster/illager/Vindicator.java -@@ -183,7 +_,7 @@ +@@ -181,7 +_,7 @@ - static class VindicatorBreakDoorGoal extends BreakDoorGoal { - public VindicatorBreakDoorGoal(Mob mob) { + private static class VindicatorBreakDoorGoal extends BreakDoorGoal { + public VindicatorBreakDoorGoal(final Mob mob) { - super(mob, 6, Vindicator.DOOR_BREAKING_PREDICATE); + super(mob, 6, com.google.common.base.Predicates.in(mob.level().paperConfig().entities.behavior.doorBreakingDifficulty.getOrDefault(mob.getType(), mob.level().paperConfig().entities.behavior.doorBreakingDifficulty.get(EntityType.VINDICATOR)))); // Paper - Configurable door breaking difficulty this.setFlags(EnumSet.of(Goal.Flag.MOVE)); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java.patch index c8b899872eaf..f0ec4bf4ca57 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java.patch @@ -1,13 +1,15 @@ --- a/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java +++ b/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java -@@ -102,9 +_,14 @@ +@@ -102,11 +_,16 @@ } - protected void finishConversion(ServerLevel level) { + protected void finishConversion(final ServerLevel level) { - this.convertTo( -- EntityType.ZOMBIFIED_PIGLIN, ConversionParams.single(this, true, true), mob -> mob.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0)) + net.minecraft.world.entity.Entity converted = this.convertTo( // Paper - Fix issues with mob conversion; reset to prevent event spam -+ EntityType.ZOMBIFIED_PIGLIN, ConversionParams.single(this, true, true), mob -> {mob.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0));}, org.bukkit.event.entity.EntityTransformEvent.TransformReason.PIGLIN_ZOMBIFIED, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.PIGLIN_ZOMBIFIED // CraftBukkit - add spawn and transform reasons + EntityType.ZOMBIFIED_PIGLIN, + ConversionParams.single(this, true, true), +- zombified -> zombified.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0)) ++ zombified -> {zombified.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0));}, org.bukkit.event.entity.EntityTransformEvent.TransformReason.PIGLIN_ZOMBIFIED, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.PIGLIN_ZOMBIFIED // CraftBukkit - add spawn and transform reasons ); + // Paper start - Fix issues with mob conversion; reset to prevent event spam + if (converted == null) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/Piglin.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/Piglin.java.patch index 531622193478..cc9ead32c126 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/Piglin.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/Piglin.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/entity/monster/piglin/Piglin.java +++ b/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -129,6 +_,12 @@ - MemoryModuleType.SPEAR_ENGAGE_TIME, - MemoryModuleType.SPEAR_STATUS +@@ -96,6 +_,12 @@ + List.of(SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.NEAREST_ITEMS, SensorType.HURT_BY, SensorType.PIGLIN_SPECIFIC_SENSOR), + PiglinAi::getActivities ); + // CraftBukkit start - Custom bartering and interest list + public java.util.Set allowedBarterItems = new java.util.HashSet<>(); @@ -11,9 +11,9 @@ + .byNameCodec().listOf().xmap(java.util.HashSet::new, List::copyOf); + // CraftBukkit end - public Piglin(EntityType type, Level level) { + public Piglin(final EntityType type, final Level level) { super(type, level); -@@ -141,6 +_,10 @@ +@@ -108,6 +_,10 @@ output.putBoolean("IsBaby", this.isBaby()); output.putBoolean("CannotHunt", this.cannotHunt); this.writeInventoryToTag(output); @@ -24,7 +24,7 @@ } @Override -@@ -149,6 +_,10 @@ +@@ -116,6 +_,10 @@ this.setBaby(input.getBooleanOr("IsBaby", false)); this.setCannotHunt(input.getBooleanOr("CannotHunt", false)); this.readInventoryFromTag(input); @@ -35,9 +35,9 @@ } @VisibleForDebug -@@ -314,7 +_,9 @@ +@@ -282,7 +_,9 @@ @Override - protected void finishConversion(ServerLevel level) { + protected void finishConversion(final ServerLevel level) { PiglinAi.cancelAdmiring(level, this); + this.forceDrops = true; // Paper - Add missing forceDrop toggles this.inventory.removeAllItems().forEach(itemStack -> this.spawnAtLocation(level, itemStack)); @@ -45,29 +45,30 @@ super.finishConversion(level); } -@@ -391,7 +_,7 @@ +@@ -359,7 +_,7 @@ } - protected void holdInOffHand(ItemStack stack) { -- if (stack.is(PiglinAi.BARTERING_ITEM)) { -+ if (stack.is(PiglinAi.BARTERING_ITEM) || this.allowedBarterItems.contains(stack.getItem())) { // CraftBukkit - Changes to accept custom payment items - this.setItemSlot(EquipmentSlot.OFFHAND, stack); + protected void holdInOffHand(final ItemStack itemStack) { +- if (itemStack.is(PiglinAi.BARTERING_ITEM)) { ++ if (itemStack.is(PiglinAi.BARTERING_ITEM) || this.allowedBarterItems.contains(itemStack.getItem())) { // CraftBukkit - Changes to accept custom payment items + this.setItemSlot(EquipmentSlot.OFFHAND, itemStack); this.setGuaranteedDrop(EquipmentSlot.OFFHAND); } else { -@@ -416,15 +_,15 @@ +@@ -384,8 +_,8 @@ return false; } else { TagKey preferredWeaponType = this.getPreferredWeaponType(); -- boolean flag = PiglinAi.isLovedItem(newItem) || preferredWeaponType != null && newItem.is(preferredWeaponType); -- boolean flag1 = PiglinAi.isLovedItem(currentItem) || preferredWeaponType != null && currentItem.is(preferredWeaponType); -+ boolean flag = PiglinAi.isLovedItem(newItem, this) || preferredWeaponType != null && newItem.is(preferredWeaponType); // CraftBukkit -+ boolean flag1 = PiglinAi.isLovedItem(currentItem, this) || preferredWeaponType != null && currentItem.is(preferredWeaponType); // CraftBukkit - return flag && !flag1 || (flag || !flag1) && super.canReplaceCurrentItem(newItem, currentItem, slot); +- boolean newItemWanted = PiglinAi.isLovedItem(newItemStack) || preferredWeaponType != null && newItemStack.is(preferredWeaponType); +- boolean currentItemWanted = PiglinAi.isLovedItem(currentItemStack) || preferredWeaponType != null && currentItemStack.is(preferredWeaponType); ++ boolean newItemWanted = PiglinAi.isLovedItem(newItemStack, this) || preferredWeaponType != null && newItemStack.is(preferredWeaponType); // CraftBukkit ++ boolean currentItemWanted = PiglinAi.isLovedItem(currentItemStack, this) || preferredWeaponType != null && currentItemStack.is(preferredWeaponType); // CraftBukkit + return newItemWanted && !currentItemWanted + || (newItemWanted || !currentItemWanted) && super.canReplaceCurrentItem(newItemStack, currentItemStack, slot); } - } +@@ -393,7 +_,7 @@ @Override - protected void pickUpItem(ServerLevel level, ItemEntity entity) { + protected void pickUpItem(final ServerLevel level, final ItemEntity entity) { - this.onItemPickup(entity); + // this.onItemPickup(entity); // Paper - EntityPickupItemEvent fixes; call in PiglinAi#pickUpItem after EntityPickupItemEvent is fired PiglinAi.pickUpItem(level, this, entity); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch index d0c0a517b7e0..ae7121d1021c 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch @@ -1,154 +1,143 @@ --- a/net/minecraft/world/entity/monster/piglin/PiglinAi.java +++ b/net/minecraft/world/entity/monster/piglin/PiglinAi.java -@@ -335,24 +_,26 @@ - - protected static void pickUpItem(ServerLevel level, Piglin piglin, ItemEntity itemEntity) { - stopWalking(piglin); -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(piglin, itemEntity, itemEntity.getItem().is(Items.GOLD_NUGGET) ? 0 : itemEntity.getItem().getCount() - 1).isCancelled()) return; // Paper -+ piglin.onItemPickup(itemEntity); // Paper - moved from Piglin#pickUpItem - call prior to item entity modification - ItemStack item; +@@ -338,16 +_,18 @@ + protected static void pickUpItem(final ServerLevel level, final Piglin body, final ItemEntity itemEntity) { + stopWalking(body); + ItemStack taken; ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(body, itemEntity, itemEntity.getItem().is(Items.GOLD_NUGGET) ? 0 : itemEntity.getItem().getCount() - 1).isCancelled()) return; // Paper ++ body.onItemPickup(itemEntity); // Paper - moved from Piglin#pickUpItem - call prior to item entity modification if (itemEntity.getItem().is(Items.GOLD_NUGGET)) { -- piglin.take(itemEntity, itemEntity.getItem().getCount()); -+ piglin.take(itemEntity, itemEntity.getItem().getCount()); // Paper - diff on change for above event - item = itemEntity.getItem(); + body.take(itemEntity, itemEntity.getItem().getCount()); + taken = itemEntity.getItem(); - itemEntity.discard(); + itemEntity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause } else { -- piglin.take(itemEntity, 1); -+ piglin.take(itemEntity, 1); // Paper - diff on change for above event - item = removeOneItemFromItemEntity(itemEntity); + body.take(itemEntity, 1); + taken = removeOneItemFromItemEntity(itemEntity); } -- if (isLovedItem(item)) { -+ if (isLovedItem(item, piglin)) { // CraftBukkit - Changes to allow for custom payment in bartering - piglin.getBrain().eraseMemory(MemoryModuleType.TIME_TRYING_TO_REACH_ADMIRE_ITEM); - holdInOffhand(level, piglin, item); - admireGoldItem(piglin); - } else if (isFood(item) && !hasEatenRecently(piglin)) { - eat(piglin); - } else { -- boolean flag = !piglin.equipItemIfPossible(level, item).equals(ItemStack.EMPTY); -+ boolean flag = !piglin.equipItemIfPossible(level, item, null).equals(ItemStack.EMPTY); // CraftBukkit // Paper - pass null item entity to prevent duplicate pickup item event call - called above. - if (!flag) { - putInInventory(piglin, item); - } -@@ -361,7 +_,9 @@ +- if (isLovedItem(taken)) { ++ if (isLovedItem(taken, body)) { // CraftBukkit - Changes to allow for custom payment in bartering + body.getBrain().eraseMemory(MemoryModuleType.TIME_TRYING_TO_REACH_ADMIRE_ITEM); + holdInOffhand(level, body, taken); + admireGoldItem(body); +@@ -363,7 +_,9 @@ - private static void holdInOffhand(ServerLevel level, Piglin piglin, ItemStack stack) { - if (isHoldingItemInOffHand(piglin)) { -+ piglin.forceDrops = true; // Paper - Add missing forceDrop toggles - piglin.spawnAtLocation(level, piglin.getItemInHand(InteractionHand.OFF_HAND)); -+ piglin.forceDrops = false; // Paper - Add missing forceDrop toggles + private static void holdInOffhand(final ServerLevel level, final Piglin body, final ItemStack itemStack) { + if (isHoldingItemInOffHand(body)) { ++ body.forceDrops = true; // Paper - Add missing forceDrop toggles + body.spawnAtLocation(level, body.getItemInHand(InteractionHand.OFF_HAND)); ++ body.forceDrops = false; // Paper - Add missing forceDrop toggles } - piglin.holdInOffHand(stack); -@@ -371,7 +_,7 @@ - ItemStack item = itemEntity.getItem(); - ItemStack itemStack = item.split(1); - if (item.isEmpty()) { + body.holdInOffHand(itemStack); +@@ -373,7 +_,7 @@ + ItemStack sourceStack = itemEntity.getItem(); + ItemStack removedStack = sourceStack.split(1); + if (sourceStack.isEmpty()) { - itemEntity.discard(); + itemEntity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause } else { - itemEntity.setItem(item); + itemEntity.setItem(sourceStack); } -@@ -383,9 +_,14 @@ - ItemStack itemInHand = piglin.getItemInHand(InteractionHand.OFF_HAND); - piglin.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY); - if (piglin.isAdult()) { -- boolean isBarterCurrency = isBarterCurrency(itemInHand); -+ boolean isBarterCurrency = isBarterCurrency(itemInHand, piglin); // CraftBukkit - Changes to allow custom payment for bartering - if (barter && isBarterCurrency) { -- throwItems(piglin, getBarterResponseItems(piglin)); +@@ -385,9 +_,14 @@ + ItemStack itemStack = body.getItemInHand(InteractionHand.OFF_HAND); + body.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY); + if (body.isAdult()) { +- boolean barterCurrency = isBarterCurrency(itemStack); ++ boolean barterCurrency = isBarterCurrency(itemStack, body); // CraftBukkit - Changes to allow custom payment for bartering + if (barteringEnabled && barterCurrency) { +- throwItems(body, getBarterResponseItems(body)); + // CraftBukkit start -+ org.bukkit.event.entity.PiglinBarterEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPiglinBarterEvent(piglin, getBarterResponseItems(piglin), itemInHand); ++ org.bukkit.event.entity.PiglinBarterEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPiglinBarterEvent(body, getBarterResponseItems(body), itemStack); + if (!event.isCancelled()) { -+ throwItems(piglin, event.getOutcome().stream().map(org.bukkit.craftbukkit.inventory.CraftItemStack::asNMSCopy).collect(java.util.stream.Collectors.toList())); ++ throwItems(body, event.getOutcome().stream().map(org.bukkit.craftbukkit.inventory.CraftItemStack::asNMSCopy).collect(java.util.stream.Collectors.toList())); + } + // CraftBukkit end - } else if (!isBarterCurrency) { - boolean flag = !piglin.equipItemIfPossible(level, itemInHand).isEmpty(); - if (!flag) { -@@ -396,7 +_,7 @@ - boolean isBarterCurrency = !piglin.equipItemIfPossible(level, itemInHand).isEmpty(); - if (!isBarterCurrency) { - ItemStack mainHandItem = piglin.getMainHandItem(); + } else if (!barterCurrency) { + boolean equipped = !body.equipItemIfPossible(level, itemStack).isEmpty(); + if (!equipped) { +@@ -398,7 +_,7 @@ + boolean equipped = !body.equipItemIfPossible(level, itemStack).isEmpty(); + if (!equipped) { + ItemStack mainHandItem = body.getMainHandItem(); - if (isLovedItem(mainHandItem)) { -+ if (isLovedItem(mainHandItem, piglin)) { // CraftBukkit - Changes to allow for custom payment in bartering - putInInventory(piglin, mainHandItem); ++ if (isLovedItem(mainHandItem, body)) { // CraftBukkit - Changes to allow for custom payment in bartering + putInInventory(body, mainHandItem); } else { - throwItems(piglin, Collections.singletonList(mainHandItem)); -@@ -409,7 +_,9 @@ + throwItems(body, Collections.singletonList(mainHandItem)); +@@ -411,7 +_,9 @@ - protected static void cancelAdmiring(ServerLevel level, Piglin piglin) { - if (isAdmiringItem(piglin) && !piglin.getOffhandItem().isEmpty()) { -+ piglin.forceDrops = true; // Paper - Add missing forceDrop toggles - piglin.spawnAtLocation(level, piglin.getOffhandItem()); -+ piglin.forceDrops = false; // Paper - Add missing forceDrop toggles - piglin.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY); + protected static void cancelAdmiring(final ServerLevel level, final Piglin body) { + if (isAdmiringItem(body) && !body.getOffhandItem().isEmpty()) { ++ body.forceDrops = true; // Paper - Add missing forceDrop toggles + body.spawnAtLocation(level, body.getOffhandItem()); ++ body.forceDrops = false; // Paper - Add missing forceDrop toggles + body.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY); } } -@@ -465,7 +_,7 @@ +@@ -467,7 +_,7 @@ return false; - } else if (isAdmiringDisabled(piglin) && piglin.getBrain().hasMemoryValue(MemoryModuleType.ATTACK_TARGET)) { + } else if (isAdmiringDisabled(body) && body.getBrain().hasMemoryValue(MemoryModuleType.ATTACK_TARGET)) { return false; -- } else if (isBarterCurrency(stack)) { -+ } else if (isBarterCurrency(stack, piglin)) { // CraftBukkit - return isNotHoldingLovedItemInOffHand(piglin); +- } else if (isBarterCurrency(itemStack)) { ++ } else if (isBarterCurrency(itemStack, body)) { // CraftBukkit + return isNotHoldingLovedItemInOffHand(body); } else { - boolean canAddToInventory = piglin.canAddToInventory(stack); -@@ -474,11 +_,16 @@ - } else if (isFood(stack)) { - return !hasEatenRecently(piglin) && canAddToInventory; + boolean hasSpace = body.canAddToInventory(itemStack); +@@ -476,11 +_,16 @@ + } else if (isFood(itemStack)) { + return !hasEatenRecently(body) && hasSpace; } else { -- return !isLovedItem(stack) ? piglin.canReplaceCurrentItem(stack) : isNotHoldingLovedItemInOffHand(piglin) && canAddToInventory; -+ return !isLovedItem(stack, piglin) ? piglin.canReplaceCurrentItem(stack) : isNotHoldingLovedItemInOffHand(piglin) && canAddToInventory; // Paper - upstream missed isLovedItem check +- return !isLovedItem(itemStack) ? body.canReplaceCurrentItem(itemStack) : isNotHoldingLovedItemInOffHand(body) && hasSpace; ++ return !isLovedItem(itemStack, body) ? body.canReplaceCurrentItem(itemStack) : isNotHoldingLovedItemInOffHand(body) && hasSpace; // Paper - upstream missed isLovedItem check } } } + // CraftBukkit start - Added method to allow checking for custom payment items -+ protected static boolean isLovedItem(ItemStack item, Piglin piglin) { -+ return PiglinAi.isLovedItem(item) || (piglin.interestItems.contains(item.getItem()) || piglin.allowedBarterItems.contains(item.getItem())); ++ protected static boolean isLovedItem(final ItemStack itemStack, final Piglin body) { ++ return PiglinAi.isLovedItem(itemStack) || (body.interestItems.contains(itemStack.getItem()) || body.allowedBarterItems.contains(itemStack.getItem())); + } + // CraftBukkit end - protected static boolean isLovedItem(ItemStack item) { - return item.is(ItemTags.PIGLIN_LOVED); + protected static boolean isLovedItem(final ItemStack itemStack) { + return itemStack.is(ItemTags.PIGLIN_LOVED); } -@@ -530,6 +_,7 @@ +@@ -540,6 +_,7 @@ } - public static void angerNearbyPiglins(ServerLevel level, Player player, boolean requireLineOfSight) { + public static void angerNearbyPiglins(final ServerLevel level, final Player player, final boolean onlyIfTheySeeThePlayer) { + if (!player.level().paperConfig().entities.behavior.piglinsGuardChests) return; // Paper - Config option for Piglins guarding chests - List entitiesOfClass = player.level().getEntitiesOfClass(Piglin.class, player.getBoundingBox().inflate(16.0)); - entitiesOfClass.stream().filter(PiglinAi::isIdle).filter(piglin -> !requireLineOfSight || BehaviorUtils.canSee(piglin, player)).forEach(piglin -> { + List nearbyPiglins = player.level().getEntitiesOfClass(Piglin.class, player.getBoundingBox().inflate(16.0)); + nearbyPiglins.stream().filter(PiglinAi::isIdle).filter(piglin -> !onlyIfTheySeeThePlayer || BehaviorUtils.canSee(piglin, player)).forEach(piglin -> { if (level.getGameRules().get(GameRules.UNIVERSAL_ANGER)) { -@@ -554,7 +_,7 @@ +@@ -564,7 +_,7 @@ } - protected static boolean canAdmire(Piglin piglin, ItemStack stack) { -- return !isAdmiringDisabled(piglin) && !isAdmiringItem(piglin) && piglin.isAdult() && isBarterCurrency(stack); -+ return !isAdmiringDisabled(piglin) && !isAdmiringItem(piglin) && piglin.isAdult() && isBarterCurrency(stack, piglin); // CraftBukkit + protected static boolean canAdmire(final Piglin body, final ItemStack playerHeldItemStack) { +- return !isAdmiringDisabled(body) && !isAdmiringItem(body) && body.isAdult() && isBarterCurrency(playerHeldItemStack); ++ return !isAdmiringDisabled(body) && !isAdmiringItem(body) && body.isAdult() && isBarterCurrency(playerHeldItemStack, body); // CraftBukkit } - protected static void wasHurtBy(ServerLevel level, Piglin piglin, LivingEntity entity) { -@@ -802,6 +_,11 @@ - return piglin.getBrain().hasMemoryValue(MemoryModuleType.ADMIRING_ITEM); + protected static void wasHurtBy(final ServerLevel level, final Piglin body, final LivingEntity attacker) { +@@ -813,6 +_,11 @@ + return body.getBrain().hasMemoryValue(MemoryModuleType.ADMIRING_ITEM); } + // CraftBukkit start - Changes to allow custom payment for bartering -+ private static boolean isBarterCurrency(ItemStack item, Piglin piglin) { -+ return PiglinAi.isBarterCurrency(item) || piglin.allowedBarterItems.contains(item.getItem()); ++ private static boolean isBarterCurrency(final ItemStack itemStack, final Piglin body) { ++ return PiglinAi.isBarterCurrency(itemStack) || body.allowedBarterItems.contains(itemStack.getItem()); + } + // CraftBukkit end - private static boolean isBarterCurrency(ItemStack stack) { - return stack.is(BARTERING_ITEM); + private static boolean isBarterCurrency(final ItemStack itemStack) { + return itemStack.is(BARTERING_ITEM); } -@@ -839,7 +_,7 @@ +@@ -850,7 +_,7 @@ } - private static boolean isNotHoldingLovedItemInOffHand(Piglin piglin) { -- return piglin.getOffhandItem().isEmpty() || !isLovedItem(piglin.getOffhandItem()); -+ return piglin.getOffhandItem().isEmpty() || !isLovedItem(piglin.getOffhandItem(), piglin); // CraftBukkit - Changes to allow custom payment for bartering + private static boolean isNotHoldingLovedItemInOffHand(final Piglin body) { +- return body.getOffhandItem().isEmpty() || !isLovedItem(body.getOffhandItem()); ++ return body.getOffhandItem().isEmpty() || !isLovedItem(body.getOffhandItem(), body); // CraftBukkit - Changes to allow custom payment for bartering } - public static boolean isZombified(EntityType entityType) { + public static boolean isZombified(final Entity entity) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java.patch index 108bb28ad52d..0f515a9b2f00 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java +++ b/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java -@@ -66,6 +_,7 @@ +@@ -71,6 +_,7 @@ AbstractSkeleton.this.setAggressive(true); } }; + private boolean shouldBurnInDay = true; // Paper - shouldBurnInDay API - protected AbstractSkeleton(EntityType type, Level level) { + protected AbstractSkeleton(final EntityType type, final Level level) { super(type, level); -@@ -90,6 +_,21 @@ +@@ -95,6 +_,21 @@ return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.25); } @@ -28,9 +28,9 @@ + // Paper end - shouldBurnInDay API + @Override - protected void playStepSound(BlockPos pos, BlockState block) { + protected void playStepSound(final BlockPos pos, final BlockState blockState) { this.playSound(this.getStepSound(), 0.15F, 1.0F); -@@ -120,7 +_,7 @@ +@@ -125,7 +_,7 @@ this.populateDefaultEquipmentSlots(random, difficulty); this.populateDefaultEquipmentEnchantments(level, random, difficulty); this.reassessWeaponGoal(); @@ -39,27 +39,27 @@ if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty() && SpecialDates.isHalloween() && random.nextFloat() < 0.25F) { this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(random.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN)); this.setDropChance(EquipmentSlot.HEAD, 0.0F); -@@ -158,7 +_,8 @@ +@@ -163,7 +_,8 @@ @Override - public void performRangedAttack(LivingEntity target, float distanceFactor) { -- ItemStack itemInHand = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW)); + public void performRangedAttack(final LivingEntity target, final float power) { +- ItemStack bowItem = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW)); + net.minecraft.world.InteractionHand hand = ProjectileUtil.getWeaponHoldingHand(this, Items.BOW); // Paper - call EntityShootBowEvent -+ ItemStack itemInHand = this.getItemInHand(hand); // Paper - call EntityShootBowEvent - ItemStack projectile = this.getProjectile(itemInHand); - AbstractArrow arrow = this.getArrow(projectile, distanceFactor, itemInHand); - double d = target.getX() - this.getX(); -@@ -166,9 +_,21 @@ - double d2 = target.getZ() - this.getZ(); - double squareRoot = Math.sqrt(d * d + d2 * d2); ++ ItemStack bowItem = this.getItemInHand(hand); // Paper - call EntityShootBowEvent + ItemStack projectile = this.getProjectile(bowItem); + AbstractArrow arrow = this.getArrow(projectile, power, bowItem); + double xd = target.getX() - this.getX(); +@@ -171,9 +_,21 @@ + double zd = target.getZ() - this.getZ(); + double distanceToTarget = Math.sqrt(xd * xd + zd * zd); if (this.level() instanceof ServerLevel serverLevel) { - Projectile.spawnProjectileUsingShoot( + Projectile.Delayed delayedEntity = Projectile.spawnProjectileUsingShootDelayed( // Paper - delayed - arrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4 + arrow, serverLevel, projectile, xd, yd + distanceToTarget * 0.2F, zd, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4 ); + + // Paper start - call EntityShootBowEvent -+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, itemInHand, arrow.getPickupItem(), arrow, hand, distanceFactor, true); ++ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, bowItem, arrow.getPickupItem(), arrow, hand, power, true); + if (event.isCancelled()) { + event.getProjectile().remove(); + return; @@ -72,17 +72,27 @@ } this.playSound(SoundEvents.SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); -@@ -192,11 +_,22 @@ - protected void readAdditionalSaveData(ValueInput input) { +@@ -197,11 +_,14 @@ + protected void readAdditionalSaveData(final ValueInput input) { super.readAdditionalSaveData(input); this.reassessWeaponGoal(); -- } -- -- @Override -- public void onEquipItem(EquipmentSlot slot, ItemStack oldItem, ItemStack newItem) { -- super.onEquipItem(slot, oldItem, newItem); + this.shouldBurnInDay = input.getBooleanOr("Paper.ShouldBurnInDay", true); // Paper - shouldBurnInDay API -+ } + } + ++ // Paper start - silent equipping + @Override +- public void onEquipItem(final EquipmentSlot slot, final ItemStack oldStack, final ItemStack stack) { +- super.onEquipItem(slot, oldStack, stack); ++ public void onEquipItem(final EquipmentSlot slot, final ItemStack oldStack, final ItemStack stack, boolean silent) { ++ super.onEquipItem(slot, oldStack, stack, silent); ++ // Paper end - silent equipping + if (!this.level().isClientSide()) { + this.reassessWeaponGoal(); + } +@@ -215,4 +_,12 @@ + public boolean wantsToPickUp(final ServerLevel level, final ItemStack itemStack) { + return !itemStack.is(ItemTags.SPEARS) && super.wantsToPickUp(level, itemStack); + } + + // Paper start - shouldBurnInDay API + @Override @@ -91,12 +101,4 @@ + output.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); + } + // Paper end - shouldBurnInDay API -+ -+ // Paper start - silent equipping -+ @Override -+ public void onEquipItem(EquipmentSlot slot, ItemStack oldItem, ItemStack newItem, boolean silent) { -+ super.onEquipItem(slot, oldItem, newItem, silent); -+ // Paper end - silent equipping - if (!this.level().isClientSide()) { - this.reassessWeaponGoal(); - } + } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/Bogged.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/Bogged.java.patch index 787f5c998ca1..f01a51ba7da2 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/Bogged.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/Bogged.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/monster/skeleton/Bogged.java +++ b/net/minecraft/world/entity/monster/skeleton/Bogged.java @@ -72,7 +_,19 @@ - ItemStack itemInHand = player.getItemInHand(hand); - if (itemInHand.is(Items.SHEARS) && this.readyForShearing()) { - if (this.level() instanceof ServerLevel serverLevel) { -- this.shear(serverLevel, SoundSource.PLAYERS, itemInHand); + ItemStack itemStack = player.getItemInHand(hand); + if (itemStack.is(Items.SHEARS) && this.readyForShearing()) { + if (this.level() instanceof ServerLevel level) { +- this.shear(level, SoundSource.PLAYERS, itemStack); + // CraftBukkit start + // Paper start - custom shear drops -+ java.util.List drops = this.generateDefaultDrops(serverLevel, itemInHand); -+ org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemInHand, hand, drops); ++ java.util.List drops = this.generateDefaultDrops(level, itemStack); ++ org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemStack, hand, drops); + if (event != null) { + if (event.isCancelled()) { + return InteractionResult.PASS; @@ -17,42 +17,40 @@ + // Paper end - custom shear drops + } + // CraftBukkit end -+ this.shear(serverLevel, SoundSource.PLAYERS, itemInHand, drops); // Paper - custom shear drops ++ this.shear(level, SoundSource.PLAYERS, itemStack, drops); // Paper - custom shear drops this.gameEvent(GameEvent.SHEAR, player); - itemInHand.hurtAndBreak(1, player, hand.asEquipmentSlot()); + itemStack.hurtAndBreak(1, player, hand.asEquipmentSlot()); } -@@ -125,15 +_,33 @@ +@@ -125,13 +_,33 @@ @Override - public void shear(ServerLevel level, SoundSource source, ItemStack shears) { + public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool) { + // Paper start - custom shear drops -+ this.shear(level, source, shears, this.generateDefaultDrops(level, shears)); ++ this.shear(level, soundSource, tool, this.generateDefaultDrops(level, tool)); + } + + @Override -+ public java.util.List generateDefaultDrops(final ServerLevel level, final ItemStack shears) { ++ public java.util.List generateDefaultDrops(final ServerLevel level, final ItemStack tool) { + final java.util.List drops = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); -+ this.dropFromShearingLootTable(level, BuiltInLootTables.BOGGED_SHEAR, shears, (ignored, stack) -> { ++ this.dropFromShearingLootTable(level, BuiltInLootTables.BOGGED_SHEAR, tool, (ignored, stack) -> { + drops.add(stack); + }); + return drops; + } + + @Override -+ public void shear(ServerLevel level, SoundSource source, ItemStack shears, java.util.List drops) { ++ public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool, final java.util.List drops) { + // Paper end - custom shear drops - level.playSound(null, this, SoundEvents.BOGGED_SHEAR, source, 1.0F, 1.0F); -- this.spawnShearedMushrooms(level, shears); -+ this.spawnShearedMushrooms(level, shears, drops); // Paper - custom shear drops + level.playSound(null, this, SoundEvents.BOGGED_SHEAR, soundSource, 1.0F, 1.0F); +- this.spawnShearedMushrooms(level, tool); ++ this.spawnShearedMushrooms(level, tool, drops); // Paper - custom shear drops this.setSheared(true); } -- private void spawnShearedMushrooms(ServerLevel level, ItemStack stack) { -- this.dropFromShearingLootTable( -- level, BuiltInLootTables.BOGGED_SHEAR, stack, (serverLevel, itemStack) -> this.spawnAtLocation(serverLevel, itemStack, this.getBbHeight()) -- ); +- private void spawnShearedMushrooms(final ServerLevel level, final ItemStack tool) { +- this.dropFromShearingLootTable(level, BuiltInLootTables.BOGGED_SHEAR, tool, (l, drop) -> this.spawnAtLocation(l, drop, this.getBbHeight())); + // Paper start - custom shear drops -+ private void spawnShearedMushrooms(ServerLevel level, ItemStack stack, java.util.List drops) { ++ private void spawnShearedMushrooms(final ServerLevel level, final ItemStack tool, java.util.List drops) { + this.forceDrops = true; // Paper - Add missing forceDrop toggles + drops.forEach(drop -> this.spawnAtLocation(level, drop, this.getBbHeight())); + this.forceDrops = false; // Paper - Add missing forceDrop toggles diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/Skeleton.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/Skeleton.java.patch index 828eee49ccaf..131968034af6 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/Skeleton.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/Skeleton.java.patch @@ -4,15 +4,15 @@ } protected void doFreezeConversion() { -- this.convertTo(EntityType.STRAY, ConversionParams.single(this, true, true), mob -> { -+ final Stray stray = this.convertTo(EntityType.STRAY, ConversionParams.single(this, true, true), mob -> { // Paper - Fix issues with mob conversion; reset conversion time to prevent event spam +- this.convertTo(EntityType.STRAY, ConversionParams.single(this, true, true), stray -> { ++ final Stray entity = this.convertTo(EntityType.STRAY, ConversionParams.single(this, true, true), stray -> { // Paper - Fix issues with mob conversion; reset conversion time to prevent event spam if (!this.isSilent()) { this.level().levelEvent(null, LevelEvent.SOUND_SKELETON_TO_STRAY, this.blockPosition(), 0); } - }); + // Paper start - add spawn and transform reasons + }, org.bukkit.event.entity.EntityTransformEvent.TransformReason.FROZEN, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.FROZEN); -+ if (stray == null) { ++ if (entity == null) { + // Reset conversion time to prevent event spam + this.conversionTime = 300; + } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java.patch index aabf999cbe62..0ddacc7eb85a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/WitherSkeleton.java.patch @@ -12,8 +12,8 @@ @@ -111,6 +_,6 @@ @Override - public boolean canBeAffected(MobEffectInstance effectInstance) { -- return !effectInstance.is(MobEffects.WITHER) && super.canBeAffected(effectInstance); -+ return (!effectInstance.is(net.minecraft.world.effect.MobEffects.WITHER) || !this.level().paperConfig().entities.mobEffects.immuneToWitherEffect.witherSkeleton) && super.canBeAffected(effectInstance); // Paper - Add config for mobs immune to default effects + public boolean canBeAffected(final MobEffectInstance newEffect) { +- return !newEffect.is(MobEffects.WITHER) && super.canBeAffected(newEffect); ++ return (!newEffect.is(MobEffects.WITHER) || !this.level().paperConfig().entities.mobEffects.immuneToWitherEffect.witherSkeleton) && super.canBeAffected(newEffect); // Paper - Add config for mobs immune to default effects } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/spider/CaveSpider.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/spider/CaveSpider.java.patch index cf4e7adfc5c8..f7dea5bb6029 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/spider/CaveSpider.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/spider/CaveSpider.java.patch @@ -3,9 +3,9 @@ @@ -38,7 +_,7 @@ } - if (i > 0) { -- ((LivingEntity)target).addEffect(new MobEffectInstance(MobEffects.POISON, i * 20, 0), this); -+ ((LivingEntity)target).addEffect(new MobEffectInstance(MobEffects.POISON, i * 20, 0), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + if (poisonTime > 0) { +- ((LivingEntity)target).addEffect(new MobEffectInstance(MobEffects.POISON, poisonTime * 20, 0), this); ++ ((LivingEntity)target).addEffect(new MobEffectInstance(MobEffects.POISON, poisonTime * 20, 0), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/spider/Spider.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/spider/Spider.java.patch index 6448027f791a..461c9155a69c 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/spider/Spider.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/spider/Spider.java.patch @@ -5,25 +5,25 @@ super.tick(); if (!this.level().isClientSide()) { - this.setClimbing(this.horizontalCollision); -+ this.setClimbing(this.horizontalCollision && (this.level().paperConfig().entities.behavior.allowSpiderWorldBorderClimbing || !(io.papermc.paper.FeatureHooks.isSpiderCollidingWithWorldBorder(this) && this.level().getWorldBorder().isInsideCloseToBorder(this, this.getBoundingBox())))); // Paper - Add config option for spider worldborder climbing (Inflate by +EPSILON as collision will just barely place us outside border) ++ this.setClimbing(this.horizontalCollision && (this.level().paperConfig().entities.behavior.allowSpiderWorldBorderClimbing || !io.papermc.paper.FeatureHooks.isCollidingWithWorldBorder(this))); // Paper - Add config option for spider worldborder climbing } } @@ -123,7 +_,7 @@ @Override - public boolean canBeAffected(MobEffectInstance effectInstance) { -- return !effectInstance.is(MobEffects.POISON) && super.canBeAffected(effectInstance); -+ return (!effectInstance.is(MobEffects.POISON) || !this.level().paperConfig().entities.mobEffects.spidersImmuneToPoisonEffect) && super.canBeAffected(effectInstance); // Paper - Add config for mobs immune to default effects + public boolean canBeAffected(final MobEffectInstance newEffect) { +- return !newEffect.is(MobEffects.POISON) && super.canBeAffected(newEffect); ++ return (!newEffect.is(MobEffects.POISON) || !this.level().paperConfig().entities.mobEffects.spidersImmuneToPoisonEffect) && super.canBeAffected(newEffect); // Paper - Add config for mobs immune to default effects } public boolean isClimbing() { @@ -166,7 +_,7 @@ - if (spawnGroupData instanceof Spider.SpiderEffectsGroupData spiderEffectsGroupData) { - Holder holder = spiderEffectsGroupData.effect; - if (holder != null) { -- this.addEffect(new MobEffectInstance(holder, -1)); -+ this.addEffect(new MobEffectInstance(holder, -1), null, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.SPIDER_SPAWN, level instanceof net.minecraft.server.level.ServerLevel); // CraftBukkit + if (groupData instanceof Spider.SpiderEffectsGroupData spiderEffectsGroupData) { + Holder effect = spiderEffectsGroupData.effect; + if (effect != null) { +- this.addEffect(new MobEffectInstance(effect, -1)); ++ this.addEffect(new MobEffectInstance(effect, -1), null, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.SPIDER_SPAWN, level instanceof net.minecraft.server.level.ServerLevel); // CraftBukkit } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/warden/AngerManagement.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/warden/AngerManagement.java.patch index 12bd7bec5a60..94d69cd0ece1 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/warden/AngerManagement.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/warden/AngerManagement.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/monster/warden/AngerManagement.java +++ b/net/minecraft/world/entity/monster/warden/AngerManagement.java -@@ -146,7 +_,7 @@ +@@ -142,7 +_,7 @@ - public int increaseAnger(Entity entity, int offset) { - boolean flag = !this.angerBySuspect.containsKey(entity); -- int i = this.angerBySuspect.computeInt(entity, (entity1, integer) -> Math.min(150, (integer == null ? 0 : integer) + offset)); -+ int i = this.angerBySuspect.computeInt(entity, (entity1, integer) -> Math.min(150, (integer == null ? 0 : integer) + offset)); // Paper - diff on change (Warden#increaseAngerAt WardenAngerChangeEvent) - if (flag) { - int i1 = this.angerByUuid.removeInt(entity.getUUID()); - i += i1; + public int increaseAnger(final Entity entity, final int increment) { + boolean newSuspect = !this.angerBySuspect.containsKey(entity); +- int currentAnger = this.angerBySuspect.computeInt(entity, (k, anger) -> Math.min(150, (anger == null ? 0 : anger) + increment)); ++ int currentAnger = this.angerBySuspect.computeInt(entity, (k, anger) -> Math.min(150, (anger == null ? 0 : anger) + increment)); // Paper - diff on change (Warden#increaseAngerAt WardenAngerChangeEvent) + if (newSuspect) { + int serializedAnger = this.angerByUuid.removeInt(entity.getUUID()); + currentAnger += serializedAnger; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/warden/Warden.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/warden/Warden.java.patch index 0cc4dc341d9a..576a3eeafcaa 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/warden/Warden.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/warden/Warden.java.patch @@ -1,27 +1,30 @@ --- a/net/minecraft/world/entity/monster/warden/Warden.java +++ b/net/minecraft/world/entity/monster/warden/Warden.java -@@ -395,7 +_,7 @@ +@@ -404,7 +_,7 @@ - public static void applyDarknessAround(ServerLevel level, Vec3 pos, @Nullable Entity source, int radius) { - MobEffectInstance mobEffectInstance = new MobEffectInstance(MobEffects.DARKNESS, 260, 0, false, false); -- MobEffectUtil.addEffectToPlayersAround(level, source, pos, radius, mobEffectInstance, 200); -+ MobEffectUtil.addEffectToPlayersAround(level, source, pos, radius, mobEffectInstance, 200, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.WARDEN); // CraftBukkit - Add EntityPotionEffectEvent.Cause + public static void applyDarknessAround(final ServerLevel level, final Vec3 position, final @Nullable Entity source, final int darknessRadius) { + MobEffectInstance darkness = new MobEffectInstance(MobEffects.DARKNESS, 260, 0, false, false); +- MobEffectUtil.addEffectToPlayersAround(level, source, position, darknessRadius, darkness, 200); ++ MobEffectUtil.addEffectToPlayersAround(level, source, position, darknessRadius, darkness, 200, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.WARDEN); // CraftBukkit - Add EntityPotionEffectEvent.Cause } @Override -@@ -439,6 +_,15 @@ +@@ -446,8 +_,17 @@ + } + @VisibleForTesting - public void increaseAngerAt(@Nullable Entity entity, int offset, boolean playListeningSound) { +- public void increaseAngerAt(final @Nullable Entity entity, final int amount, final boolean playSound) { ++ public void increaseAngerAt(final @Nullable Entity entity, int amount, final boolean playSound) { // Paper - Add WardenAngerChangeEvent if (!this.isNoAi() && this.canTargetEntity(entity)) { + // Paper start - Add WardenAngerChangeEvent + int activeAnger = this.angerManagement.getActiveAnger(entity); -+ io.papermc.paper.event.entity.WardenAngerChangeEvent event = new io.papermc.paper.event.entity.WardenAngerChangeEvent((org.bukkit.entity.Warden) this.getBukkitEntity(), entity.getBukkitEntity(), activeAnger, Math.min(150, activeAnger + offset)); ++ io.papermc.paper.event.entity.WardenAngerChangeEvent event = new io.papermc.paper.event.entity.WardenAngerChangeEvent((org.bukkit.entity.Warden) this.getBukkitEntity(), entity.getBukkitEntity(), activeAnger, Math.min(150, activeAnger + amount)); + this.level().getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } -+ offset = event.getNewAnger() - activeAnger; ++ amount = event.getNewAnger() - activeAnger; + // Paper end - Add WardenAngerChangeEvent WardenAi.setDigCooldown(this); - boolean flag = !(this.getTarget() instanceof Player); - int i = this.angerManagement.increaseAnger(entity, offset); + boolean maybeSwitchTarget = !(this.getTarget() instanceof Player); + int newAnger = this.angerManagement.increaseAnger(entity, amount); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Drowned.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Drowned.java.patch index 1aee8901a3a3..a263f3c521aa 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Drowned.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Drowned.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/entity/monster/zombie/Drowned.java +++ b/net/minecraft/world/entity/monster/zombie/Drowned.java -@@ -89,7 +_,7 @@ +@@ -96,7 +_,7 @@ this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0)); this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Drowned.class).setAlertOthers(ZombifiedPiglin.class)); - this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entity, level) -> this.okTarget(entity))); + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (target, level) -> this.okTarget(target))); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); + if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Paper - Check drowned for villager aggression config this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Husk.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Husk.java.patch index ba69c4fabe36..51508102f963 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Husk.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Husk.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/world/entity/monster/zombie/Husk.java +++ b/net/minecraft/world/entity/monster/zombie/Husk.java -@@ -59,7 +_,7 @@ - boolean flag = super.doHurtTarget(level, target); - if (flag && this.getMainHandItem().isEmpty() && target instanceof LivingEntity) { - float effectiveDifficulty = level.getCurrentDifficultyAt(this.blockPosition()).getEffectiveDifficulty(); -- ((LivingEntity)target).addEffect(new MobEffectInstance(MobEffects.HUNGER, 140 * (int)effectiveDifficulty), this); -+ ((LivingEntity)target).addEffect(new MobEffectInstance(MobEffects.HUNGER, 140 * (int)effectiveDifficulty), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit +@@ -67,7 +_,7 @@ + boolean result = super.doHurtTarget(level, target); + if (result && this.getMainHandItem().isEmpty() && target instanceof LivingEntity) { + float difficulty = level.getCurrentDifficultyAt(this.blockPosition()).getEffectiveDifficulty(); +- ((LivingEntity)target).addEffect(new MobEffectInstance(MobEffects.HUNGER, 140 * (int)difficulty), this); ++ ((LivingEntity)target).addEffect(new MobEffectInstance(MobEffects.HUNGER, 140 * (int)difficulty), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit } - return flag; -@@ -86,7 +_,7 @@ - spawnGroupData = super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData); - float specialMultiplier = difficulty.getSpecialMultiplier(); + return result; +@@ -94,7 +_,7 @@ + groupData = super.finalizeSpawn(level, difficulty, spawnReason, groupData); + float difficultyModifier = difficulty.getSpecialMultiplier(); if (spawnReason != EntitySpawnReason.CONVERSION) { -- this.setCanPickUpLoot(random.nextFloat() < 0.55F * specialMultiplier); -+ this.setCanPickUpLoot(level.getLevel().paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.zombies || random.nextFloat() < 0.55F * specialMultiplier); // Paper - Add world settings for mobs picking up loot +- this.setCanPickUpLoot(random.nextFloat() < 0.55F * difficultyModifier); ++ this.setCanPickUpLoot(level.getLevel().paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.zombies || random.nextFloat() < 0.55F * difficultyModifier); // Paper - Add world settings for mobs picking up loot } - if (spawnGroupData != null) { + if (groupData != null) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Zombie.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Zombie.java.patch index 79a714e53818..ec8291af250e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Zombie.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Zombie.java.patch @@ -1,17 +1,19 @@ --- a/net/minecraft/world/entity/monster/zombie/Zombie.java +++ b/net/minecraft/world/entity/monster/zombie/Zombie.java -@@ -67,9 +_,7 @@ +@@ -71,8 +_,10 @@ public class Zombie extends Monster { private static final Identifier SPEED_MODIFIER_BABY_ID = Identifier.withDefaultNamespace("baby"); - private static final AttributeModifier SPEED_MODIFIER_BABY = new AttributeModifier( - SPEED_MODIFIER_BABY_ID, 0.5, AttributeModifier.Operation.ADD_MULTIPLIED_BASE -- ); -+ private final AttributeModifier babyModifier = new AttributeModifier(Zombie.SPEED_MODIFIER_BABY_ID, this.level().paperConfig().entities.behavior.babyZombieMovementModifier, AttributeModifier.Operation.ADD_MULTIPLIED_BASE); // Paper - Make baby speed configurable ++ // Paper start - Make baby speed configurable ++ private final AttributeModifier SPEED_MODIFIER_BABY = new AttributeModifier( ++ SPEED_MODIFIER_BABY_ID, this.level().paperConfig().entities.behavior.babyZombieMovementModifier, AttributeModifier.Operation.ADD_MULTIPLIED_BASE ++ // Paper end + ); private static final Identifier REINFORCEMENT_CALLER_CHARGE_ID = Identifier.withDefaultNamespace("reinforcement_caller_charge"); private static final AttributeModifier ZOMBIE_REINFORCEMENT_CALLEE_CHARGE = new AttributeModifier( - Identifier.withDefaultNamespace("reinforcement_callee_charge"), -0.05F, AttributeModifier.Operation.ADD_VALUE -@@ -90,13 +_,15 @@ +@@ -96,13 +_,15 @@ private static final boolean DEFAULT_BABY = false; private static final boolean DEFAULT_CAN_BREAK_DOORS = false; private static final int DEFAULT_IN_WATER_TIME = 0; @@ -22,13 +24,13 @@ public int conversionTime; + private boolean shouldBurnInDay = true; // Paper - Add more Zombie API - public Zombie(EntityType type, Level level) { + public Zombie(final EntityType type, final Level level) { super(type, level); + this.breakDoorGoal = new BreakDoorGoal(this, com.google.common.base.Predicates.in(level.paperConfig().entities.behavior.doorBreakingDifficulty.getOrDefault(type, level.paperConfig().entities.behavior.doorBreakingDifficulty.get(EntityType.ZOMBIE)))); // Paper - Configurable door breaking difficulty } - public Zombie(Level level) { -@@ -105,7 +_,7 @@ + public Zombie(final Level level) { +@@ -111,7 +_,7 @@ @Override protected void registerGoals() { @@ -37,7 +39,7 @@ this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F)); this.goalSelector.addGoal(8, new RandomLookAroundGoal(this)); this.addBehaviourGoals(); -@@ -118,7 +_,7 @@ +@@ -124,7 +_,7 @@ this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0)); this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers(ZombifiedPiglin.class)); this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true)); @@ -46,10 +48,10 @@ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR)); } -@@ -172,11 +_,16 @@ +@@ -178,11 +_,16 @@ @Override - protected int getBaseExperienceReward(ServerLevel level) { + protected int getBaseExperienceReward(final ServerLevel level) { + final int previousReward = this.xpReward; // Paper - store previous value to reset after calculating XP reward if (this.isBaby()) { this.xpReward = (int)(this.xpReward * 2.5); @@ -64,19 +66,7 @@ } @Override -@@ -184,9 +_,9 @@ - this.getEntityData().set(DATA_BABY_ID, childZombie); - if (this.level() != null && !this.level().isClientSide()) { - AttributeInstance attribute = this.getAttribute(Attributes.MOVEMENT_SPEED); -- attribute.removeModifier(SPEED_MODIFIER_BABY_ID); -+ attribute.removeModifier(this.babyModifier.id()); // Paper - Make baby speed configurable - if (childZombie) { -- attribute.addTransientModifier(SPEED_MODIFIER_BABY); -+ attribute.addTransientModifier(this.babyModifier); // Paper - Make baby speed configurable - } - } - } -@@ -227,6 +_,13 @@ +@@ -233,6 +_,13 @@ super.tick(); } @@ -87,21 +77,24 @@ + } + // Paper end - Add more Zombie API + - public void startUnderWaterConversion(int conversionTime) { - this.conversionTime = conversionTime; + public void startUnderWaterConversion(final int time) { + this.conversionTime = time; this.getEntityData().set(DATA_DROWNED_CONVERSION_ID, true); -@@ -240,31 +_,50 @@ +@@ -246,17 +_,28 @@ } - protected void convertToZombieType(ServerLevel level, EntityType entityType) { + protected void convertToZombieType(final ServerLevel level, final EntityType zombieType) { - this.convertTo( + Zombie converted = this.convertTo( // CraftBukkit - entityType, + zombieType, ConversionParams.single(this, true, true), -- zombie -> zombie.handleAttributes(level.getCurrentDifficultyAt(zombie.blockPosition()).getSpecialMultiplier()) -- ); +- newZombie -> newZombie.handleAttributes( + // CraftBukkit start -+ zombie -> { zombie.handleAttributes(level.getCurrentDifficultyAt(zombie.blockPosition()).getSpecialMultiplier()); }, ++ newZombie -> { newZombie.handleAttributes( + level.getCurrentDifficultyAt(newZombie.blockPosition()).getSpecialMultiplier(), EntitySpawnReason.CONVERSION +- ) +- ); ++ ); }, + org.bukkit.event.entity.EntityTransformEvent.TransformReason.DROWNED, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DROWNED); + if (converted == null) { + ((org.bukkit.entity.Zombie) this.getBukkitEntity()).setConversionTime(-1); // CraftBukkit - SPIGOT-5208: End conversion to stop event spam @@ -110,28 +103,30 @@ } @VisibleForTesting - public boolean convertVillagerToZombieVillager(ServerLevel level, Villager villager) { + public boolean convertVillagerToZombieVillager(final ServerLevel level, final Villager villager) { + // CraftBukkit start + return convertVillagerToZombieVillager(level, villager, this.blockPosition(), this.isSilent(), org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.INFECTION) != null; + } + -+ public static @Nullable ZombieVillager convertVillagerToZombieVillager(ServerLevel level, Villager villager, net.minecraft.core.BlockPos blockPosition, boolean silent, org.bukkit.event.entity.EntityTransformEvent.TransformReason transformReason, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason creatureSpawnReason) { ++ public static @Nullable ZombieVillager convertVillagerToZombieVillager(final ServerLevel level, final Villager villager, final net.minecraft.core.BlockPos blockPosition, boolean silent, org.bukkit.event.entity.EntityTransformEvent.TransformReason transformReason, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason creatureSpawnReason) { + // CraftBukkit end - ZombieVillager zombieVillager = villager.convertTo(EntityType.ZOMBIE_VILLAGER, ConversionParams.single(villager, true, true), mob -> { - mob.finalizeSpawn(level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.CONVERSION, new Zombie.ZombieGroupData(false, true)); - mob.setVillagerData(villager.getVillagerData()); - mob.setGossips(villager.getGossips().copy()); - mob.setTradeOffers(villager.getOffers().copy()); - mob.setVillagerXp(villager.getVillagerXp()); -- if (!this.isSilent()) { -- level.levelEvent(null, LevelEvent.SOUND_ZOMBIE_INFECTED, this.blockPosition(), 0); -+ // CraftBukkit start -+ if (!silent) { -+ level.levelEvent(null, LevelEvent.SOUND_ZOMBIE_INFECTED, blockPosition, 0); - } -- }); + ZombieVillager zombieVillager = villager.convertTo( + EntityType.ZOMBIE_VILLAGER, + ConversionParams.single(villager, true, true), +@@ -268,17 +_,24 @@ + zombie.setGossips(villager.getGossips().copy()); + zombie.setTradeOffers(villager.getOffers().copy()); + zombie.setVillagerXp(villager.getVillagerXp()); +- if (!this.isSilent()) { +- level.levelEvent(null, LevelEvent.SOUND_ZOMBIE_INFECTED, this.blockPosition(), 0); ++ // CraftBukkit start ++ if (!silent) { ++ level.levelEvent(null, LevelEvent.SOUND_ZOMBIE_INFECTED, blockPosition, 0); + } +- } +- ); - return zombieVillager != null; -+ }, transformReason, creatureSpawnReason); ++ }, transformReason, creatureSpawnReason); + return zombieVillager; + // CraftBukkit end } @@ -149,31 +144,33 @@ + // Paper end - Add more Zombie API @Override - public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { -@@ -297,13 +_,13 @@ - if (SpawnPlacements.isSpawnPositionOk(type, level, blockPos) - && SpawnPlacements.checkSpawnRules(type, level, EntitySpawnReason.REINFORCEMENT, blockPos, level.random)) { - zombie.setPos(i1, i2, i3); -- if (!level.hasNearbyAlivePlayer(i1, i2, i3, 7.0) -+ if (!level.hasNearbyAlivePlayerThatAffectsSpawning(i1, i2, i3, 7.0) // Paper - affects spawning api - && level.isUnobstructed(zombie) - && level.noCollision(zombie) - && (zombie.canSpawnInLiquids() || !level.containsAnyLiquid(zombie.getBoundingBox()))) { -- zombie.setTarget(target); -+ zombie.setTarget(target, org.bukkit.event.entity.EntityTargetEvent.TargetReason.REINFORCEMENT_TARGET); // CraftBukkit - zombie.finalizeSpawn(level, level.getCurrentDifficultyAt(zombie.blockPosition()), EntitySpawnReason.REINFORCEMENT, null); -- level.addFreshEntityWithPassengers(zombie); -+ level.addFreshEntityWithPassengers(zombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.REINFORCEMENTS); // CraftBukkit + public boolean hurtServer(final ServerLevel level, final DamageSource source, final float damage) { +@@ -311,15 +_,15 @@ + if (SpawnPlacements.isSpawnPositionOk(type, level, spawnPos) + && SpawnPlacements.checkSpawnRules(type, level, EntitySpawnReason.REINFORCEMENT, spawnPos, level.getRandom())) { + reinforcement.setPos(xt, yt, zt); +- if (!level.hasNearbyAlivePlayer(xt, yt, zt, 7.0) ++ if (!level.hasNearbyAlivePlayerThatAffectsSpawning(xt, yt, zt, 7.0) // Paper - affects spawning api + && level.isUnobstructed(reinforcement) + && level.noCollision(reinforcement) + && (reinforcement.canSpawnInLiquids() || !level.containsAnyLiquid(reinforcement.getBoundingBox()))) { +- reinforcement.setTarget(target); ++ reinforcement.setTarget(target, org.bukkit.event.entity.EntityTargetEvent.TargetReason.REINFORCEMENT_TARGET); // CraftBukkit + reinforcement.finalizeSpawn( + level, level.getCurrentDifficultyAt(reinforcement.blockPosition()), EntitySpawnReason.REINFORCEMENT, null + ); +- level.addFreshEntityWithPassengers(reinforcement); ++ level.addFreshEntityWithPassengers(reinforcement, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.REINFORCEMENTS); // CraftBukkit AttributeInstance attribute = this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE); AttributeModifier modifier = attribute.getModifier(REINFORCEMENT_CALLER_CHARGE_ID); - double d = modifier != null ? modifier.amount() : 0.0; -@@ -328,7 +_,12 @@ - if (flag) { - float effectiveDifficulty = level.getCurrentDifficultyAt(this.blockPosition()).getEffectiveDifficulty(); - if (this.getMainHandItem().isEmpty() && this.isOnFire() && this.random.nextFloat() < effectiveDifficulty * 0.3F) { -- target.igniteForSeconds(2 * (int)effectiveDifficulty); + double existingAmount = modifier != null ? modifier.amount() : 0.0; +@@ -344,7 +_,12 @@ + if (result) { + float difficulty = level.getCurrentDifficultyAt(this.blockPosition()).getEffectiveDifficulty(); + if (this.getMainHandItem().isEmpty() && this.isOnFire() && this.random.nextFloat() < difficulty * 0.3F) { +- target.igniteForSeconds(2 * (int)difficulty); + // CraftBukkit start -+ org.bukkit.event.entity.EntityCombustByEntityEvent event = new org.bukkit.event.entity.EntityCombustByEntityEvent(this.getBukkitEntity(), target.getBukkitEntity(), (float) (2 * (int)effectiveDifficulty)); ++ org.bukkit.event.entity.EntityCombustByEntityEvent event = new org.bukkit.event.entity.EntityCombustByEntityEvent(this.getBukkitEntity(), target.getBukkitEntity(), (float) (2 * (int)difficulty)); + if (event.callEvent()) { + target.igniteForSeconds(event.getDuration(), false); + } @@ -181,7 +178,7 @@ } } -@@ -390,6 +_,7 @@ +@@ -406,6 +_,7 @@ output.putBoolean("CanBreakDoors", this.canBreakDoors()); output.putInt("InWaterTime", this.isInWater() ? this.inWaterTime : -1); output.putInt("DrownedConversionTime", this.isUnderWaterConverting() ? this.conversionTime : -1); @@ -189,7 +186,7 @@ } @Override -@@ -404,13 +_,15 @@ +@@ -420,13 +_,15 @@ } else { this.getEntityData().set(DATA_DROWNED_CONVERSION_ID, false); } @@ -197,31 +194,40 @@ } @Override - public boolean killedEntity(ServerLevel level, LivingEntity entity, DamageSource damageSource) { - boolean flag = super.killedEntity(level, entity, damageSource); + public boolean killedEntity(final ServerLevel level, final LivingEntity entity, final DamageSource source) { + boolean perished = super.killedEntity(level, entity, source); - if ((level.getDifficulty() == Difficulty.NORMAL || level.getDifficulty() == Difficulty.HARD) && entity instanceof Villager villager) { - if (level.getDifficulty() != Difficulty.HARD && this.random.nextBoolean()) { + final double fallbackChance = level.getDifficulty() == Difficulty.HARD ? 100 : level.getDifficulty() == Difficulty.NORMAL ? 50 : 0; // Paper - Configurable chance of villager zombie infection - moved up from belows if + if (this.random.nextDouble() * 100 < level.paperConfig().entities.behavior.zombieVillagerInfectionChance.or(fallbackChance) && entity instanceof Villager villager) { // Paper - Configurable chance of villager zombie infection + if (false && level.getDifficulty() != Difficulty.HARD && this.random.nextBoolean()) { // Paper - Configurable chance of villager zombie infection - moved to "fallbackChance" - return flag; + return perished; } -@@ -445,7 +_,7 @@ - spawnGroupData = super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData); - float specialMultiplier = difficulty.getSpecialMultiplier(); +@@ -461,7 +_,7 @@ + groupData = super.finalizeSpawn(level, difficulty, spawnReason, groupData); + float difficultyModifier = difficulty.getSpecialMultiplier(); if (spawnReason != EntitySpawnReason.CONVERSION) { -- this.setCanPickUpLoot(random.nextFloat() < 0.55F * specialMultiplier); -+ this.setCanPickUpLoot(level.getLevel().paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.zombies || random.nextFloat() < 0.55F * specialMultiplier); // Paper - Add world settings for mobs picking up loot +- this.setCanPickUpLoot(random.nextFloat() < 0.55F * difficultyModifier); ++ this.setCanPickUpLoot(level.getLevel().paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.zombies || random.nextFloat() < 0.55F * difficultyModifier); // Paper - Add world settings for mobs picking up loot } - if (spawnGroupData == null) { -@@ -472,7 +_,7 @@ - chicken1.finalizeSpawn(level, difficulty, EntitySpawnReason.JOCKEY, null); - chicken1.setChickenJockey(true); - this.startRiding(chicken1, false, false); -- level.addFreshEntity(chicken1); -+ level.addFreshEntity(chicken1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.MOUNT); // CraftBukkit + if (groupData == null) { +@@ -488,7 +_,7 @@ + chicken.finalizeSpawn(level, difficulty, EntitySpawnReason.JOCKEY, null); + chicken.setChickenJockey(true); + this.startRiding(chicken, false, false); +- level.addFreshEntity(chicken); ++ level.addFreshEntity(chicken, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.MOUNT); // CraftBukkit } } } +@@ -514,7 +_,7 @@ + protected void onOffspringSpawnedFromEgg(final Player spawner, final Mob offspring) { + if (this.level() instanceof ServerLevel serverLevel) { + float difficultyModifier = serverLevel.getCurrentDifficultyAt(offspring.blockPosition()).getSpecialMultiplier(); +- offspring.setCanPickUpLoot(this.random.nextFloat() < 0.55F * difficultyModifier); ++ offspring.setCanPickUpLoot(serverLevel.paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.zombies || this.random.nextFloat() < 0.55F * difficultyModifier); // Paper - Add world settings for mobs picking up loot + } + } + diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/ZombieVillager.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/ZombieVillager.java.patch index 3550e14d7231..23251af58f11 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/ZombieVillager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/ZombieVillager.java.patch @@ -1,52 +1,52 @@ --- a/net/minecraft/world/entity/monster/zombie/ZombieVillager.java +++ b/net/minecraft/world/entity/monster/zombie/ZombieVillager.java -@@ -189,12 +_,20 @@ +@@ -200,12 +_,20 @@ } - public void startConverting(@Nullable UUID conversionStarter, int villagerConversionTime) { + public void startConverting(final @Nullable UUID player, final int time) { + // Paper start - missing entity behaviour api - converting without entity event -+ this.startConverting(conversionStarter, villagerConversionTime, true); ++ this.startConverting(player, time, true); + } + -+ public void startConverting(@Nullable UUID conversionStarter, int villagerConversionTime, boolean broadcastEntityEvent) { ++ public void startConverting(@Nullable UUID player, int time, boolean broadcastEntityEvent) { + // Paper end - missing entity behaviour api - converting without entity event - this.conversionStarter = conversionStarter; - this.villagerConversionTime = villagerConversionTime; + this.conversionStarter = player; + this.villagerConversionTime = time; this.getEntityData().set(DATA_CONVERTING_ID, true); - this.removeEffect(MobEffects.WEAKNESS); -- this.addEffect(new MobEffectInstance(MobEffects.STRENGTH, villagerConversionTime, Math.min(this.level().getDifficulty().getId() - 1, 0))); +- this.addEffect(new MobEffectInstance(MobEffects.STRENGTH, time, Math.min(this.level().getDifficulty().getId() - 1, 0))); - this.level().broadcastEntityEvent(this, EntityEvent.ZOMBIE_CONVERTING); + // CraftBukkit start + this.removeEffect(MobEffects.WEAKNESS, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); -+ this.addEffect(new MobEffectInstance(MobEffects.STRENGTH, villagerConversionTime, Math.min(this.level().getDifficulty().getId() - 1, 0)), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); ++ this.addEffect(new MobEffectInstance(MobEffects.STRENGTH, time, Math.min(this.level().getDifficulty().getId() - 1, 0)), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); + // CraftBukkit end + if (broadcastEntityEvent) this.level().broadcastEntityEvent(this, EntityEvent.ZOMBIE_CONVERTING); // Paper - missing entity behaviour api - converting without entity event } @Override -@@ -219,7 +_,7 @@ +@@ -235,7 +_,7 @@ } - private void finishConversion(ServerLevel level) { + private void finishConversion(final ServerLevel level) { - this.convertTo( + Villager converted = this.convertTo( // CraftBukkit EntityType.VILLAGER, ConversionParams.single(this, false, false), - mob -> { -@@ -245,19 +_,24 @@ - mob.finalizeSpawn(level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.CONVERSION, null); - mob.refreshBrain(level); + villager -> { +@@ -261,19 +_,24 @@ + villager.finalizeSpawn(level, level.getCurrentDifficultyAt(villager.blockPosition()), EntitySpawnReason.CONVERSION, null); + villager.refreshBrain(level); if (this.conversionStarter != null) { -- Player playerByUuid = level.getPlayerByUUID(this.conversionStarter); -+ Player playerByUuid = level.getGlobalPlayerByUUID(this.conversionStarter); // Paper - check global player list where appropriate - if (playerByUuid instanceof ServerPlayer) { - CriteriaTriggers.CURED_ZOMBIE_VILLAGER.trigger((ServerPlayer)playerByUuid, this, mob); - level.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, playerByUuid, mob); +- Player player = level.getPlayerByUUID(this.conversionStarter); ++ Player player = level.getGlobalPlayerByUUID(this.conversionStarter); // Paper - check global player list where appropriate + if (player instanceof ServerPlayer) { + CriteriaTriggers.CURED_ZOMBIE_VILLAGER.trigger((ServerPlayer)player, this, villager); + level.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, player, villager); } } -- mob.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0)); -+ mob.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); // CraftBukkit +- villager.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0)); ++ villager.addEffect(new MobEffectInstance(MobEffects.NAUSEA, 200, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); // CraftBukkit if (!this.isSilent()) { level.levelEvent(null, LevelEvent.SOUND_ZOMBIE_CONVERTED, this.blockPosition(), 0); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java.patch index 923e5403fa79..42cdc2e833f7 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java +++ b/net/minecraft/world/entity/monster/zombie/ZombifiedPiglin.java -@@ -57,6 +_,7 @@ +@@ -61,6 +_,7 @@ private static final int ALERT_RANGE_Y = 10; private static final UniformInt ALERT_INTERVAL = TimeUtil.rangeOfSeconds(4, 6); private int ticksUntilNextAlert; + private HurtByTargetGoal hurtByTargetGoal; // Paper - fix PigZombieAngerEvent cancellation - public ZombifiedPiglin(EntityType type, Level level) { + public ZombifiedPiglin(final EntityType type, final Level level) { super(type, level); -@@ -68,7 +_,7 @@ +@@ -72,7 +_,7 @@ this.goalSelector.addGoal(1, new SpearUseGoal<>(this, 1.0, 1.0, 10.0F, 2.0F)); this.goalSelector.addGoal(2, new ZombieAttackGoal(this, 1.0, false)); this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0)); @@ -17,21 +17,21 @@ this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt)); this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true)); } -@@ -141,7 +_,7 @@ - .filter(zombifiedPiglin -> zombifiedPiglin != this) - .filter(zombifiedPiglin -> zombifiedPiglin.getTarget() == null) - .filter(zombifiedPiglin -> !zombifiedPiglin.isAlliedTo(this.getTarget())) -- .forEach(zombifiedPiglin -> zombifiedPiglin.setTarget(this.getTarget())); -+ .forEach(zombifiedPiglin -> zombifiedPiglin.setTarget(this.getTarget(), org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY)); // CraftBukkit +@@ -145,7 +_,7 @@ + .filter(other -> other != this) + .filter(other -> other.getTarget() == null) + .filter(other -> !other.isAlliedTo(this.getTarget())) +- .forEach(other -> other.setTarget(this.getTarget())); ++ .forEach(other -> other.setTarget(this.getTarget(), org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY)); // CraftBukkit } private void playAngerSound() { -@@ -149,18 +_,27 @@ +@@ -153,18 +_,27 @@ } @Override -- public void setTarget(@Nullable LivingEntity target) { -+ public boolean setTarget(@Nullable LivingEntity target, org.bukkit.event.entity.EntityTargetEvent.@Nullable TargetReason reason) { // CraftBukkit - signature +- public void setTarget(final @Nullable LivingEntity target) { ++ public boolean setTarget(final @Nullable LivingEntity target, org.bukkit.event.entity.EntityTargetEvent.@Nullable TargetReason reason) { // CraftBukkit - signature if (this.getTarget() == null && target != null) { this.playFirstAngerSoundIn = FIRST_ANGER_SOUND_DELAY.sample(this.random); this.ticksUntilNextAlert = ALERT_INTERVAL.sample(this.random); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch index d18c0f63ded7..80cca6bf31af 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/world/entity/npc/CatSpawner.java +++ b/net/minecraft/world/entity/npc/CatSpawner.java @@ -65,12 +_,12 @@ - private void spawnCat(BlockPos pos, ServerLevel level, boolean persistent) { + private void spawnCat(final BlockPos spawnPos, final ServerLevel level, final boolean makePersistent) { Cat cat = EntityType.CAT.create(level, EntitySpawnReason.NATURAL); if (cat != null) { -+ cat.snapTo(pos, 0.0F, 0.0F); // Paper - move up - Fix MC-147659 - cat.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), EntitySpawnReason.NATURAL, null); - if (persistent) { ++ cat.snapTo(spawnPos, 0.0F, 0.0F); // Paper - move up - Fix MC-147659 + cat.finalizeSpawn(level, level.getCurrentDifficultyAt(spawnPos), EntitySpawnReason.NATURAL, null); + if (makePersistent) { cat.setPersistenceRequired(); } -- cat.snapTo(pos, 0.0F, 0.0F); +- cat.snapTo(spawnPos, 0.0F, 0.0F); level.addFreshEntityWithPassengers(cat); } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/npc/InventoryCarrier.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/npc/InventoryCarrier.java.patch index 106c9c66cfb6..c1f34c5bd98e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/npc/InventoryCarrier.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/npc/InventoryCarrier.java.patch @@ -5,19 +5,19 @@ } + // CraftBukkit start -+ ItemStack remaining = new SimpleContainer(inventory).addItem(item); ++ ItemStack remaining = new SimpleContainer(inventory).addItem(itemStack); + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(mob, itemEntity, remaining.getCount()).isCancelled()) { + return; + } + // CraftBukkit end + mob.onItemPickup(itemEntity); - int count = item.getCount(); - ItemStack itemStack = inventory.addItem(item); - mob.take(itemEntity, count - itemStack.getCount()); - if (itemStack.isEmpty()) { + int count = itemStack.getCount(); + ItemStack remainder = inventory.addItem(itemStack); + mob.take(itemEntity, count - remainder.getCount()); + if (remainder.isEmpty()) { - itemEntity.discard(); + itemEntity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause } else { - item.setCount(itemStack.getCount()); + itemStack.setCount(remainder.getCount()); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/npc/villager/AbstractVillager.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/npc/villager/AbstractVillager.java.patch index f032420bf2ec..2cba8c7b6ee2 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/npc/villager/AbstractVillager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/npc/villager/AbstractVillager.java.patch @@ -1,15 +1,15 @@ --- a/net/minecraft/world/entity/npc/villager/AbstractVillager.java +++ b/net/minecraft/world/entity/npc/villager/AbstractVillager.java -@@ -43,7 +_,7 @@ - private static final int VILLAGER_INVENTORY_SIZE = 8; +@@ -56,7 +_,7 @@ + private static final EntityDataAccessor DATA_UNHAPPY_COUNTER = SynchedEntityData.defineId(AbstractVillager.class, EntityDataSerializers.INT); private @Nullable Player tradingPlayer; protected @Nullable MerchantOffers offers; - private final SimpleContainer inventory = new SimpleContainer(8); + private final SimpleContainer inventory = new SimpleContainer(8, (org.bukkit.craftbukkit.entity.CraftAbstractVillager) this.getBukkitEntity()); // CraftBukkit - add argument - public AbstractVillager(EntityType type, Level level) { + public AbstractVillager(final EntityType type, final Level level) { super(type, level); -@@ -95,6 +_,20 @@ +@@ -108,6 +_,20 @@ return this.tradingPlayer != null; } @@ -30,8 +30,8 @@ @Override public MerchantOffers getOffers() { if (this.level() instanceof ServerLevel serverLevel) { -@@ -117,11 +_,24 @@ - public void overrideXp(int xp) { +@@ -130,11 +_,24 @@ + public void overrideXp(final int xp) { } + // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent @@ -48,7 +48,7 @@ + // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent + @Override - public void notifyTrade(MerchantOffer offer) { + public void notifyTrade(final MerchantOffer offer) { - offer.increaseUses(); + // offer.increaseUses(); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent this.ambientSoundTime = -this.getAmbientSoundInterval(); @@ -57,25 +57,86 @@ if (this.tradingPlayer instanceof ServerPlayer) { CriteriaTriggers.TRADE.trigger((ServerPlayer)this.tradingPlayer, this, offer.getResult()); } -@@ -225,7 +_,20 @@ - while (i < maxNumbers && !list.isEmpty()) { - MerchantOffer offer = list.remove(this.random.nextInt(list.size())).getOffer(level, this, this.random); +@@ -232,6 +_,11 @@ + protected abstract void updateTrades(ServerLevel level); + + protected void addOffersFromTradeSet(final ServerLevel level, final MerchantOffers offers, final ResourceKey resourceKey) { ++ // Paper start - More vanilla friendly methods to update trades ++ addOffersFromTradeSet(level, offers, resourceKey, -1); ++ } ++ protected void addOffersFromTradeSet(final ServerLevel level, final MerchantOffers offers, final ResourceKey resourceKey, int offerCount) { ++ // Paper end - More vanilla friendly methods to update trades + Optional tradeSetOpt = this.registryAccess().lookupOrThrow(Registries.TRADE_SET).getOptional(resourceKey); + if (tradeSetOpt.isEmpty()) { + LOGGER.debug("Missing expected trade set {}", resourceKey); +@@ -245,17 +_,17 @@ + .create(LootContextParamSets.VILLAGER_TRADE) + ) + .create(tradeSet.randomSequence()); +- int numberOfOffers = tradeSet.calculateNumberOfTrades(lootContext); ++ int numberOfOffers = offerCount != -1 ? offerCount : tradeSet.calculateNumberOfTrades(lootContext); // Paper - More vanilla friendly methods to update trades + if (tradeSet.allowDuplicates()) { +- addOffersFromItemListings(lootContext, offers, tradeSet.getTrades(), numberOfOffers); ++ addOffersFromItemListings(lootContext, offers, tradeSet.getTrades(), numberOfOffers, this); // Paper + } else { +- addOffersFromItemListingsWithoutDuplicates(lootContext, offers, tradeSet.getTrades(), numberOfOffers); ++ addOffersFromItemListingsWithoutDuplicates(lootContext, offers, tradeSet.getTrades(), numberOfOffers, this); // Paper + } + } + } + + private static void addOffersFromItemListings( +- final LootContext lootContext, final MerchantOffers merchantOffers, final HolderSet potentialOffers, final int numberOfOffers ++ final LootContext lootContext, final MerchantOffers merchantOffers, final HolderSet potentialOffers, final int numberOfOffers, AbstractVillager villager // Paper + ) { + int offersFound = 0; + +@@ -267,14 +_,14 @@ + + MerchantOffer offer = villagerTrade.get().value().getOffer(lootContext); + if (offer != null) { +- merchantOffers.add(offer); ++ tryAcquireTrade(villager, merchantOffers, offer); // Paper + offersFound++; + } + } + } + + private static void addOffersFromItemListingsWithoutDuplicates( +- final LootContext lootContext, final MerchantOffers merchantOffers, final HolderSet potentialOffers, final int numberOfOffers ++ final LootContext lootContext, final MerchantOffers merchantOffers, final HolderSet potentialOffers, final int numberOfOffers, final AbstractVillager villager // Paper + ) { + List> leftoverOffers = Lists.newArrayList(potentialOffers); + int offersFound = 0; +@@ -283,11 +_,30 @@ + Holder villagerTrade = leftoverOffers.remove(lootContext.getRandom().nextInt(leftoverOffers.size())); + MerchantOffer offer = villagerTrade.value().getOffer(lootContext); if (offer != null) { -- givenMerchantOffers.add(offer); -+ // CraftBukkit start -+ org.bukkit.event.entity.VillagerAcquireTradeEvent event = new org.bukkit.event.entity.VillagerAcquireTradeEvent((org.bukkit.entity.AbstractVillager) this.getBukkitEntity(), offer.asBukkit()); -+ // Suppress during worldgen -+ if (this.valid) { -+ event.callEvent(); -+ } -+ if (!event.isCancelled()) { -+ // Paper start - Fix crash from invalid ingredient list -+ final org.bukkit.craftbukkit.inventory.CraftMerchantRecipe craftMerchantRecipe = org.bukkit.craftbukkit.inventory.CraftMerchantRecipe.fromBukkit(event.getRecipe()); -+ if (craftMerchantRecipe.getIngredients().isEmpty()) return; -+ givenMerchantOffers.add(craftMerchantRecipe.toMinecraft()); -+ // Paper end - Fix crash from invalid ingredient list -+ } -+ // CraftBukkit end - i++; +- merchantOffers.add(offer); ++ tryAcquireTrade(villager, merchantOffers, offer); // Paper + offersFound++; } } + } ++ ++ // Paper start ++ private static void tryAcquireTrade(final AbstractVillager villager, final MerchantOffers merchantOffers, MerchantOffer offer) { ++ // CraftBukkit start ++ org.bukkit.event.entity.VillagerAcquireTradeEvent event = new org.bukkit.event.entity.VillagerAcquireTradeEvent((org.bukkit.entity.AbstractVillager) villager.getBukkitEntity(), offer.asBukkit()); ++ // Suppress during worldgen ++ if (villager.valid) { ++ event.callEvent(); ++ } ++ if (!event.isCancelled()) { ++ // Paper start - Fix crash from invalid ingredient list ++ final org.bukkit.craftbukkit.inventory.CraftMerchantRecipe craftMerchantRecipe = org.bukkit.craftbukkit.inventory.CraftMerchantRecipe.fromBukkit(event.getRecipe()); ++ if (craftMerchantRecipe.getIngredients().isEmpty()) return; ++ merchantOffers.add(craftMerchantRecipe.toMinecraft()); ++ // Paper end - Fix crash from invalid ingredient list ++ } ++ // CraftBukkit end ++ } ++ // Paper end + + @Override + public Vec3 getRopeHoldPosition(final float partialTickTime) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/npc/villager/Villager.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/npc/villager/Villager.java.patch index 9065f2e46d63..80d743b511a9 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/npc/villager/Villager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/npc/villager/Villager.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/npc/villager/Villager.java +++ b/net/minecraft/world/entity/npc/villager/Villager.java -@@ -287,7 +_,7 @@ +@@ -262,7 +_,7 @@ this.increaseProfessionLevelOnUpdate = false; } @@ -9,140 +9,134 @@ } } -@@ -396,7 +_,12 @@ +@@ -376,7 +_,12 @@ this.updateDemand(); - for (MerchantOffer merchantOffer : this.getOffers()) { -- merchantOffer.resetUses(); + for (MerchantOffer offer : this.getOffers()) { +- offer.resetUses(); + // CraftBukkit start -+ org.bukkit.event.entity.VillagerReplenishTradeEvent event = new org.bukkit.event.entity.VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), merchantOffer.asBukkit()); ++ org.bukkit.event.entity.VillagerReplenishTradeEvent event = new org.bukkit.event.entity.VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), offer.asBukkit()); + if (event.callEvent()) { -+ merchantOffer.resetUses(); ++ offer.resetUses(); + } + // CraftBukkit end } this.resendOffersToTradingPlayer(); -@@ -452,7 +_,12 @@ - int i = 2 - this.numberOfRestocksToday; - if (i > 0) { - for (MerchantOffer merchantOffer : this.getOffers()) { -- merchantOffer.resetUses(); +@@ -437,7 +_,12 @@ + int missedUpdates = 2 - this.numberOfRestocksToday; + if (missedUpdates > 0) { + for (MerchantOffer offer : this.getOffers()) { +- offer.resetUses(); + // CraftBukkit start -+ org.bukkit.event.entity.VillagerReplenishTradeEvent event = new org.bukkit.event.entity.VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), merchantOffer.asBukkit()); ++ org.bukkit.event.entity.VillagerReplenishTradeEvent event = new org.bukkit.event.entity.VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), offer.asBukkit()); + if (event.callEvent()) { -+ merchantOffer.resetUses(); ++ offer.resetUses(); + } + // CraftBukkit end } } -@@ -473,6 +_,7 @@ - int playerReputation = this.getPlayerReputation(player); - if (playerReputation != 0) { - for (MerchantOffer merchantOffer : this.getOffers()) { -+ if (merchantOffer.ignoreDiscounts) continue; // Paper - Add ignore discounts API - merchantOffer.addToSpecialPriceDiff(-Mth.floor(playerReputation * merchantOffer.getPriceMultiplier())); +@@ -458,6 +_,7 @@ + int reputation = this.getPlayerReputation(player); + if (reputation != 0) { + for (MerchantOffer offer : this.getOffers()) { ++ if (offer.ignoreDiscounts) continue; // Paper - Add ignore discounts API + offer.addToSpecialPriceDiff(-Mth.floor(reputation * offer.getPriceMultiplier())); } } -@@ -482,6 +_,7 @@ +@@ -467,6 +_,7 @@ int amplifier = effect.getAmplifier(); - for (MerchantOffer merchantOffer1 : this.getOffers()) { -+ if (merchantOffer1.ignoreDiscounts) continue; // Paper - Add ignore discounts API - double d = 0.3 + 0.0625 * amplifier; - int i = (int)Math.floor(d * merchantOffer1.getBaseCostA().getCount()); - merchantOffer1.addToSpecialPriceDiff(-Math.max(i, 1)); -@@ -589,7 +_,7 @@ + for (MerchantOffer offer : this.getOffers()) { ++ if (offer.ignoreDiscounts) continue; // Paper - Add ignore discounts API + double modifier = 0.3 + 0.0625 * amplifier; + int costReduction = (int)Math.floor(modifier * offer.getBaseCostA().getCount()); + offer.addToSpecialPriceDiff(-Math.max(costReduction, 1)); +@@ -582,7 +_,7 @@ } if (offer.shouldRewardExp()) { -- this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), i)); -+ this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper +- this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), popXp)); ++ this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), popXp, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper } } -@@ -607,7 +_,7 @@ +@@ -600,7 +_,7 @@ @Override - public void die(DamageSource damageSource) { -- LOGGER.info("Villager {} died, message: '{}'", this, damageSource.getLocalizedDeathMessage(this).getString()); -+ if (org.spigotmc.SpigotConfig.logVillagerDeaths) LOGGER.info("Villager {} died, message: '{}'", this, damageSource.getLocalizedDeathMessage(this).getString()); // Spigot - Entity entity = damageSource.getEntity(); - if (entity != null) { - this.tellWitnessesThatIWasMurdered(entity); -@@ -706,7 +_,7 @@ - return VillagerData.canLevelUp(level) && this.villagerXp >= VillagerData.getMaxXpPerLevel(level); + public void die(final DamageSource source) { +- LOGGER.info("Villager {} died, message: '{}'", this, source.getLocalizedDeathMessage(this).getString()); ++ if (org.spigotmc.SpigotConfig.logVillagerDeaths) LOGGER.info("Villager {} died, message: '{}'", this, source.getLocalizedDeathMessage(this).getString()); // Spigot + Entity murderer = source.getEntity(); + if (murderer != null) { + this.tellWitnessesThatIWasMurdered(murderer); +@@ -699,7 +_,7 @@ + return VillagerData.canLevelUp(currentLevel) && this.villagerXp >= VillagerData.getMaxXpPerLevel(currentLevel); } -- private void increaseMerchantCareer(ServerLevel level) { -+ public void increaseMerchantCareer(ServerLevel level) { // Paper - public +- private void increaseMerchantCareer(final ServerLevel level) { ++ public void increaseMerchantCareer(final ServerLevel level) { // Paper - public this.setVillagerData(this.getVillagerData().withLevel(this.getVillagerData().level() + 1)); this.updateTrades(level); } -@@ -773,12 +_,19 @@ +@@ -762,12 +_,19 @@ @Override - public void thunderHit(ServerLevel level, LightningBolt lightning) { + public void thunderHit(final ServerLevel level, final LightningBolt lightningBolt) { if (level.getDifficulty() != Difficulty.PEACEFUL) { -- LOGGER.info("Villager {} was struck by lightning {}.", this, lightning); +- LOGGER.info("Villager {} was struck by lightning {}.", this, lightningBolt); + // Paper - Add EntityZapEvent; move log down, event can cancel - Witch witch = this.convertTo(EntityType.WITCH, ConversionParams.single(this, false, false), witch1 -> { + Witch witch = this.convertTo(EntityType.WITCH, ConversionParams.single(this, false, false), w -> { + // Paper start - Add EntityZapEvent -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityZapEvent(this, lightning, witch1).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityZapEvent(this, lightningBolt, w).isCancelled()) { + return false; + } -+ if (org.spigotmc.SpigotConfig.logVillagerDeaths) LOGGER.info("Villager {} was struck by lightning {}.", this, lightning); // Move down ++ if (org.spigotmc.SpigotConfig.logVillagerDeaths) LOGGER.info("Villager {} was struck by lightning {}.", this, lightningBolt); // Move down + // Paper end - Add EntityZapEvent - witch1.finalizeSpawn(level, level.getCurrentDifficultyAt(witch1.blockPosition()), EntitySpawnReason.CONVERSION, null); - witch1.setPersistenceRequired(); + w.finalizeSpawn(level, level.getCurrentDifficultyAt(w.blockPosition()), EntitySpawnReason.CONVERSION, null); + w.setPersistenceRequired(); this.releaseAllPois(); - }); -+ return true; // Paper start - Add EntityZapEvent ++ return true; // Paper - Add EntityZapEvent + }, org.bukkit.event.entity.EntityTransformEvent.TransformReason.LIGHTNING, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit if (witch == null) { - super.thunderHit(level, lightning); + super.thunderHit(level, lightningBolt); } -@@ -818,6 +_,12 @@ +@@ -807,15 +_,23 @@ @Override - protected void updateTrades(ServerLevel level) { + protected void updateTrades(final ServerLevel level) { + // Paper start - More vanilla friendly methods to update trades -+ this.updateTrades(TRADES_PER_LEVEL); ++ this.updateTrades(level, -1); + } + -+ public boolean updateTrades(int amount) { ++ public boolean updateTrades(final ServerLevel level, int amount) { + // Paper end - More vanilla friendly methods to update trades - VillagerData villagerData = this.getVillagerData(); - ResourceKey resourceKey = villagerData.profession().unwrapKey().orElse(null); - if (resourceKey != null) { -@@ -833,13 +_,15 @@ - VillagerTrades.ItemListing[] itemListings = map1.get(villagerData.level()); - if (itemListings != null) { - MerchantOffers offers = this.getOffers(); -- this.addOffersFromItemListings(level, offers, itemListings, 2); -+ this.addOffersFromItemListings((net.minecraft.server.level.ServerLevel) this.level(), offers, itemListings, amount); // Paper - More vanilla friendly methods to update trades - if (SharedConstants.DEBUG_UNLOCK_ALL_TRADES && villagerData.level() < map1.size()) { -- this.increaseMerchantCareer(level); -+ this.increaseMerchantCareer((net.minecraft.server.level.ServerLevel) this.level()); - } -+ return true; // Paper - More vanilla friendly methods to update trades - } + VillagerData data = this.getVillagerData(); + VillagerProfession profession = data.profession().value(); + ResourceKey trades = profession.getTrades(data.level()); + if (trades != null) { +- this.addOffersFromTradeSet(level, this.getOffers(), trades); ++ this.addOffersFromTradeSet(level, this.getOffers(), trades, amount); // Paper - More vanilla friendly methods to update trades + if (SharedConstants.DEBUG_UNLOCK_ALL_TRADES && data.level() < 5) { + this.increaseMerchantCareer(level); } ++ return true; // Paper - More vanilla friendly methods to update trades } + return false; // Paper - More vanilla friendly methods to update trades } - public void gossip(ServerLevel level, Villager target, long gameTime) { -@@ -868,7 +_,7 @@ - List entitiesOfClass = level.getEntitiesOfClass(Villager.class, aabb); - List list = entitiesOfClass.stream().filter(villager -> villager.wantsToSpawnGolem(gameTime)).limit(5L).toList(); - if (list.size() >= minVillagerAmount) { + public void gossip(final ServerLevel level, final Villager target, final long timestamp) { +@@ -847,7 +_,7 @@ + .limit(5L) + .toList(); + if (nearbyVillagersThatWantAGolem.size() >= villagersNeededToAgree) { - if (!SpawnUtil.trySpawnMob( + if (SpawnUtil.trySpawnMob( // Paper - Set Golem Last Seen to stop it from spawning another one - switch to isPresent EntityType.IRON_GOLEM, EntitySpawnReason.MOB_SUMMONED, level, -@@ -877,9 +_,11 @@ +@@ -856,9 +_,11 @@ 8, 6, SpawnUtil.Strategy.LEGACY_IRON_GOLEM, @@ -153,6 +147,6 @@ ) - .isEmpty()) { + .isPresent()) { // Paper - Set Golem Last Seen to stop it from spawning another one - switch to isPresent - entitiesOfClass.forEach(GolemSensor::golemDetected); + nearbyVillagers.forEach(GolemSensor::golemDetected); } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/npc/villager/VillagerTrades.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/npc/villager/VillagerTrades.java.patch deleted file mode 100644 index 3df5d43063e8..000000000000 --- a/paper-server/patches/sources/net/minecraft/world/entity/npc/villager/VillagerTrades.java.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- a/net/minecraft/world/entity/npc/villager/VillagerTrades.java -+++ b/net/minecraft/world/entity/npc/villager/VillagerTrades.java -@@ -1737,7 +_,8 @@ - - @Override - public @Nullable MerchantOffer getOffer(ServerLevel level, Entity trader, RandomSource random) { -- BlockPos blockPos = level.findNearestMapStructure(this.destination, trader.blockPosition(), 100, true); -+ if (!level.paperConfig().environment.treasureMaps.enabled) return null; // Paper - Configurable cartographer treasure maps -+ BlockPos blockPos = level.findNearestMapStructure(this.destination, trader.blockPosition(), 100, !level.paperConfig().environment.treasureMaps.findAlreadyDiscoveredVillager); // Paper - Configurable cartographer treasure maps - if (blockPos != null) { - ItemStack itemStack = MapItem.create(level, blockPos.getX(), blockPos.getZ(), (byte)2, true, true); - MapItem.renderBiomePreviewMap(level, itemStack); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java.patch index ba03df93c684..0c52d50e362a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTrader.java.patch @@ -9,36 +9,35 @@ + public boolean canDrinkMilk = true; + // Paper end - Add more WanderingTrader API - public WanderingTrader(EntityType type, Level level) { + public WanderingTrader(final EntityType type, final Level level) { super(type, level); -@@ -67,7 +_,7 @@ +@@ -67,14 +_,14 @@ this, PotionContents.createItemStack(Items.POTION, Potions.INVISIBILITY), SoundEvents.WANDERING_TRADER_DISAPPEARED, -- wanderingTrader -> this.level().isDarkOutside() && !wanderingTrader.isInvisible() -+ wanderingTrader -> this.canDrinkPotion && this.level().isDarkOutside() && !wanderingTrader.isInvisible() // Paper - Add more WanderingTrader API +- e -> this.level().isDarkOutside() && !e.isInvisible() ++ e -> this.canDrinkPotion && this.level().isDarkOutside() && !e.isInvisible() // Paper - Add more WanderingTrader API ) ); this.goalSelector -@@ -77,7 +_,7 @@ - this, - new ItemStack(Items.MILK_BUCKET), - SoundEvents.WANDERING_TRADER_REAPPEARED, -- wanderingTrader -> this.level().isBrightOutside() && wanderingTrader.isInvisible() -+ wanderingTrader -> this.canDrinkMilk && this.level().isBrightOutside() && wanderingTrader.isInvisible() // Paper - Add more WanderingTrader API + .addGoal( + 0, + new UseItemGoal<>( +- this, new ItemStack(Items.MILK_BUCKET), SoundEvents.WANDERING_TRADER_REAPPEARED, e -> this.level().isBrightOutside() && e.isInvisible() ++ this, new ItemStack(Items.MILK_BUCKET), SoundEvents.WANDERING_TRADER_REAPPEARED, e -> this.canDrinkMilk && this.level().isBrightOutside() && e.isInvisible() // Paper - Add more WanderingTrader API ) ); this.goalSelector.addGoal(1, new TradeWithPlayerGoal(this)); -@@ -164,7 +_,7 @@ - protected void rewardTradeXp(MerchantOffer offer) { +@@ -159,7 +_,7 @@ + protected void rewardTradeXp(final MerchantOffer offer) { if (offer.shouldRewardExp()) { - int i = 3 + this.random.nextInt(4); -- this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), i)); -+ this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper + int popXp = 3 + this.random.nextInt(4); +- this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), popXp)); ++ this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), popXp, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper } } -@@ -216,7 +_,7 @@ +@@ -211,7 +_,7 @@ private void maybeDespawn() { if (this.despawnDelay > 0 && !this.isTrading() && --this.despawnDelay == 0) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java.patch index f88caff4d5cc..1c205cd062e8 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java.patch @@ -1,60 +1,43 @@ --- a/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java +++ b/net/minecraft/world/entity/npc/wanderingtrader/WanderingTraderSpawner.java -@@ -38,36 +_,45 @@ +@@ -37,30 +_,38 @@ - public WanderingTraderSpawner(ServerLevelData serverLevelData) { - this.serverLevelData = serverLevelData; + public WanderingTraderSpawner(final SavedDataStorage savedDataStorage) { + this.savedDataStorage = savedDataStorage; - this.tickDelay = 1200; -- this.spawnDelay = serverLevelData.getWanderingTraderSpawnDelay(); -- this.spawnChance = serverLevelData.getWanderingTraderSpawnChance(); -- if (this.spawnDelay == 0 && this.spawnChance == 0) { -- this.spawnDelay = 24000; -- serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay); -- this.spawnChance = 25; -- serverLevelData.setWanderingTraderSpawnChance(this.spawnChance); -- } -+ // Paper start - Add Wandering Trader spawn rate config options -+ this.tickDelay = Integer.MIN_VALUE; -+ // this.spawnDelay = serverLevelData.getWanderingTraderSpawnDelay(); -+ // this.spawnChance = serverLevelData.getWanderingTraderSpawnChance(); -+ // if (this.spawnDelay == 0 && this.spawnChance == 0) { -+ // this.spawnDelay = 24000; -+ // serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay); -+ // this.spawnChance = 25; -+ // serverLevelData.setWanderingTraderSpawnChance(this.spawnChance); -+ // } -+ // Paper end - Add Wandering Trader spawn rate config options ++ this.tickDelay = Integer.MIN_VALUE; // Paper - Add Wandering Trader spawn rate config options + this.traderData = null; } @Override - public void tick(ServerLevel level, boolean spawnEnemies) { + public void tick(final ServerLevel level, final boolean spawnEnemies) { + // Paper start - Add Wandering Trader spawn rate config options + if (this.tickDelay == Integer.MIN_VALUE) { + this.tickDelay = level.paperConfig().entities.spawning.wanderingTrader.spawnMinuteLength; -+ this.spawnDelay = level.paperConfig().entities.spawning.wanderingTrader.spawnDayLength; -+ this.spawnChance = level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin; ++ WanderingTraderData data = this.getTraderData(); ++ data.setSpawnDelay(level.paperConfig().entities.spawning.wanderingTrader.spawnDayLength); ++ data.setSpawnChance(level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin); + } if (level.getGameRules().get(GameRules.SPAWN_WANDERING_TRADERS)) { - if (--this.tickDelay <= 0) { - this.tickDelay = 1200; -- this.spawnDelay -= 1200; -- this.serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay); + if (this.tickDelay - 1 <= 0) { // Paper - Prevent tickDelay going below 0 + this.tickDelay = level.paperConfig().entities.spawning.wanderingTrader.spawnMinuteLength; -+ this.spawnDelay = this.spawnDelay - level.paperConfig().entities.spawning.wanderingTrader.spawnMinuteLength; -+ //this.serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay); // Paper - We don't need to save this value to disk if it gets set back to a hardcoded value anyways - if (this.spawnDelay <= 0) { -- this.spawnDelay = 24000; -+ this.spawnDelay = level.paperConfig().entities.spawning.wanderingTrader.spawnDayLength; - int i = this.spawnChance; -- this.spawnChance = Mth.clamp(this.spawnChance + 25, 25, 75); -- this.serverLevelData.setWanderingTraderSpawnChance(this.spawnChance); -+ this.spawnChance = Mth.clamp(this.spawnChance + level.paperConfig().entities.spawning.wanderingTrader.spawnChanceFailureIncrement, level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin, level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMax); -+ //this.serverLevelData.setWanderingTraderSpawnChance(this.spawnChance); // Paper - We don't need to save this value to disk if it gets set back to a hardcoded value anyways - if (this.random.nextInt(100) <= i) { + WanderingTraderData data = this.getTraderData(); +- int spawnDelay = data.spawnDelay() - 1200; ++ int spawnDelay = data.spawnDelay() - level.paperConfig().entities.spawning.wanderingTrader.spawnMinuteLength; + data.setSpawnDelay(spawnDelay); + if (spawnDelay <= 0) { +- data.setSpawnDelay(24000); ++ data.setSpawnDelay(level.paperConfig().entities.spawning.wanderingTrader.spawnDayLength); + int chanceToSpawn = data.spawnChance(); +- int newSpawnChance = Mth.clamp(chanceToSpawn + 25, 25, 75); ++ int newSpawnChance = Mth.clamp(chanceToSpawn + level.paperConfig().entities.spawning.wanderingTrader.spawnChanceFailureIncrement, level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin, level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMax); + data.setSpawnChance(newSpawnChance); + if (this.random.nextInt(100) <= chanceToSpawn) { if (this.spawn(level)) { -- this.spawnChance = 25; -+ this.spawnChance = level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin; +- data.setSpawnChance(25); ++ data.setSpawnChance(level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin); + // Paper end - Add Wandering Trader spawn rate config options } } @@ -64,29 +47,28 @@ } } -@@ -89,14 +_,14 @@ +@@ -90,13 +_,13 @@ return false; } -- WanderingTrader wanderingTrader = EntityType.WANDERING_TRADER.spawn(level, blockPos2, EntitySpawnReason.EVENT); -+ WanderingTrader wanderingTrader = EntityType.WANDERING_TRADER.spawn(level, trader -> trader.setDespawnDelay(48000), blockPos2, EntitySpawnReason.EVENT, false, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit // Paper - set despawnTimer before spawn events called - if (wanderingTrader != null) { - for (int i1 = 0; i1 < 2; i1++) { - this.tryToSpawnLlamaFor(level, wanderingTrader, 4); +- WanderingTrader trader = EntityType.WANDERING_TRADER.spawn(level, spawnPosition, EntitySpawnReason.EVENT); ++ WanderingTrader trader = EntityType.WANDERING_TRADER.spawn(level, wanderingTrader -> wanderingTrader.setDespawnDelay(48000), spawnPosition, EntitySpawnReason.EVENT, false, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit // Paper - set despawnTimer before spawn events called + if (trader != null) { + for (int i = 0; i < 2; i++) { + this.tryToSpawnLlamaFor(level, trader, 4); } - this.serverLevelData.setWanderingTraderId(wanderingTrader.getUUID()); -- wanderingTrader.setDespawnDelay(48000); -+ // wanderingTrader.setDespawnDelay(48000); // Paper - moved above, modifiable by plugins on CreatureSpawnEvent - wanderingTrader.setWanderTarget(blockPos1); - wanderingTrader.setHomeTo(blockPos1, 16); +- trader.setDespawnDelay(48000); ++ // trader.setDespawnDelay(48000); // Paper - moved above, modifiable by plugins on CreatureSpawnEvent + trader.setWanderTarget(referencePos); + trader.setHomeTo(referencePos, 16); return true; @@ -110,7 +_,7 @@ - private void tryToSpawnLlamaFor(ServerLevel level, WanderingTrader trader, int maxDistance) { - BlockPos blockPos = this.findSpawnPositionNear(level, trader.blockPosition(), maxDistance); - if (blockPos != null) { -- TraderLlama traderLlama = EntityType.TRADER_LLAMA.spawn(level, blockPos, EntitySpawnReason.EVENT); -+ TraderLlama traderLlama = EntityType.TRADER_LLAMA.spawn(level, blockPos, EntitySpawnReason.EVENT, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit - if (traderLlama != null) { - traderLlama.setLeashedTo(trader, true); + private void tryToSpawnLlamaFor(final ServerLevel level, final WanderingTrader trader, final int radius) { + BlockPos spawnPosition = this.findSpawnPositionNear(level, trader.blockPosition(), radius); + if (spawnPosition != null) { +- TraderLlama llama = EntityType.TRADER_LLAMA.spawn(level, spawnPosition, EntitySpawnReason.EVENT); ++ TraderLlama llama = EntityType.TRADER_LLAMA.spawn(level, spawnPosition, EntitySpawnReason.EVENT, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit + if (llama != null) { + llama.setLeashedTo(trader, true); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/player/Inventory.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/player/Inventory.java.patch index 2680b11436cb..22732ca3cc5c 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/player/Inventory.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/player/Inventory.java.patch @@ -79,18 +79,18 @@ + } + // Paper end - add fields and methods - public Inventory(Player player, EntityEquipment equipment) { + public Inventory(final Player player, final EntityEquipment equipment) { this.player = player; @@ -93,10 +_,39 @@ - private boolean hasRemainingSpaceForItem(ItemStack destination, ItemStack origin) { - return !destination.isEmpty() -- && ItemStack.isSameItemSameComponents(destination, origin) - && destination.isStackable() -- && destination.getCount() < this.getMaxStackSize(destination); + private boolean hasRemainingSpaceForItem(final ItemStack slotItemStack, final ItemStack newItemStack) { + return !slotItemStack.isEmpty() +- && ItemStack.isSameItemSameComponents(slotItemStack, newItemStack) + && slotItemStack.isStackable() +- && slotItemStack.getCount() < this.getMaxStackSize(slotItemStack); - } -+ && destination.getCount() < this.getMaxStackSize(destination) -+ && ItemStack.isSameItemSameComponents(destination, origin); // Paper - check if itemstack is stackable first ++ && slotItemStack.getCount() < this.getMaxStackSize(slotItemStack) ++ && ItemStack.isSameItemSameComponents(slotItemStack, newItemStack); // Paper - check if itemstack is stackable first + } + + // CraftBukkit start - Watch method above! :D @@ -125,28 +125,28 @@ public int getFreeSlot() { for (int i = 0; i < this.items.size(); i++) { @@ -108,8 +_,10 @@ - return -1; + return NOT_FOUND_INDEX; } -- public void addAndPickItem(ItemStack stack) { +- public void addAndPickItem(final ItemStack itemStack) { - this.setSelectedSlot(this.getSuitableHotbarSlot()); + // Paper start - Add PlayerPickItemEvent -+ public void addAndPickItem(ItemStack stack, final int targetSlot) { ++ public void addAndPickItem(final ItemStack itemStack, final int targetSlot) { + this.setSelectedSlot(targetSlot); + // Paper end - Add PlayerPickItemEvent if (!this.items.get(this.selected).isEmpty()) { int freeSlot = this.getFreeSlot(); - if (freeSlot != -1) { + if (freeSlot != NOT_FOUND_INDEX) { @@ -120,8 +_,10 @@ - this.items.set(this.selected, stack); + this.items.set(this.selected, itemStack); } -- public void pickSlot(int index) { +- public void pickSlot(final int slot) { - this.setSelectedSlot(this.getSuitableHotbarSlot()); + // Paper start - Add PlayerPickItemEvent -+ public void pickSlot(int index, final int targetSlot) { ++ public void pickSlot(final int slot, final int targetSlot) { + this.setSelectedSlot(targetSlot); + // Paper end - Add PlayerPickItemEvent - ItemStack itemStack = this.items.get(this.selected); - this.items.set(this.selected, this.items.get(index)); - this.items.set(index, itemStack); + ItemStack tmp = this.items.get(this.selected); + this.items.set(this.selected, this.items.get(slot)); + this.items.set(slot, tmp); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch index e45bd8c05b06..924df35c276e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch @@ -1,18 +1,18 @@ --- a/net/minecraft/world/entity/player/Player.java +++ b/net/minecraft/world/entity/player/Player.java -@@ -151,7 +_,7 @@ - private static final int DEFAULT_CURRENT_IMPULSE_CONTEXT_RESET_GRACE_TIME = 0; +@@ -150,7 +_,7 @@ + private static final int DEFAULT_SCORE = 0; public static final float CREATIVE_ENTITY_INTERACTION_RANGE_MODIFIER_VALUE = 2.0F; - final Inventory inventory; + private final Inventory inventory; - protected PlayerEnderChestContainer enderChestInventory = new PlayerEnderChestContainer(); + protected PlayerEnderChestContainer enderChestInventory = new PlayerEnderChestContainer(this); // CraftBukkit - add "this" to constructor public final InventoryMenu inventoryMenu; public AbstractContainerMenu containerMenu; protected FoodData foodData = new FoodData(); -@@ -177,6 +_,18 @@ - public @Nullable Entity currentExplosionCause; - private boolean ignoreFallDamageFromCurrentImpulse = false; - private int currentImpulseContextResetGraceTime = 0; +@@ -172,6 +_,18 @@ + private Optional lastDeathLocation = Optional.empty(); + public @Nullable FishingHook fishing; + public float hurtDir; + public boolean affectsSpawning = true; // Paper - Affects Spawning API + public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET; // Paper - flying fall damage + @@ -26,9 +26,9 @@ + } + // CraftBukkit end - public Player(Level level, GameProfile gameProfile) { + public Player(final Level level, final GameProfile gameProfile) { super(EntityType.PLAYER, level); -@@ -244,6 +_,13 @@ +@@ -238,6 +_,13 @@ if (this.isSleeping()) { this.sleepCounter++; @@ -42,16 +42,16 @@ if (this.sleepCounter > 100) { this.sleepCounter = 100; } -@@ -273,7 +_,7 @@ - ItemStack mainHandItem = this.getMainHandItem(); - if (!ItemStack.matches(this.lastItemInMainHand, mainHandItem)) { - if (!ItemStack.isSameItem(this.lastItemInMainHand, mainHandItem)) { +@@ -267,7 +_,7 @@ + ItemStack mainHandItemStack = this.getMainHandItem(); + if (!ItemStack.matches(this.lastItemInMainHand, mainHandItemStack)) { + if (!ItemStack.isSameItem(this.lastItemInMainHand, mainHandItemStack)) { - this.resetAttackStrengthTicker(); + this.resetAttackStrengthTicker(); // Paper - diff on change (detectEquipmentUpdates override) } - this.lastItemInMainHand = mainHandItem.copy(); -@@ -327,7 +_,7 @@ + this.lastItemInMainHand = mainHandItemStack.copy(); +@@ -318,7 +_,7 @@ } private void turtleHelmetTick() { @@ -59,8 +59,8 @@ + this.addEffect(new MobEffectInstance(MobEffects.WATER_BREATHING, 200, 0, false, false, true), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TURTLE_HELMET); // CraftBukkit } - private boolean isEquipped(Item item) { -@@ -428,6 +_,18 @@ + private boolean isEquipped(final Item item) { +@@ -419,6 +_,18 @@ } } @@ -79,7 +79,7 @@ public void closeContainer() { this.containerMenu = this.inventoryMenu; } -@@ -439,8 +_,14 @@ +@@ -430,8 +_,14 @@ public void rideTick() { if (!this.level().isClientSide() && this.wantsToStopRiding() && this.isPassenger()) { this.stopRiding(); @@ -96,27 +96,27 @@ super.rideTick(); } } -@@ -699,10 +_,10 @@ +@@ -684,10 +_,10 @@ if (this.isDeadOrDying()) { return false; } else { - this.removeEntitiesOnShoulder(); + // this.removeEntitiesOnShoulder(); // CraftBukkit - moved down - if (damageSource.scalesWithDifficulty()) { + if (source.scalesWithDifficulty()) { if (level.getDifficulty() == Difficulty.PEACEFUL) { -- amount = 0.0F; -+ return false; // CraftBukkit - f = 0.0f -> return false +- damage = 0.0F; ++ return false; // CraftBukkit - damage = 0.0F -> return false } if (level.getDifficulty() == Difficulty.EASY) { -@@ -714,7 +_,14 @@ +@@ -699,7 +_,14 @@ } } -- return amount != 0.0F && super.hurtServer(level, damageSource, amount); -+ // return amount != 0.0F && super.hurtServer(level, damageSource, amount); +- return damage != 0.0F && super.hurtServer(level, source, damage); ++ // return damage != 0.0F && super.hurtServer(level, source, damage); + // CraftBukkit start - Don't filter out 0 damage -+ boolean damaged = super.hurtServer(level, damageSource, amount); ++ boolean damaged = super.hurtServer(level, source, damage); + if (damaged) { + this.removeEntitiesOnShoulder(); + } @@ -125,35 +125,35 @@ } } } -@@ -726,7 +_,7 @@ +@@ -711,7 +_,7 @@ BlocksAttacks blocksAttacks = itemBlockingWith != null ? itemBlockingWith.get(DataComponents.BLOCKS_ATTACKS) : null; - float secondsToDisableBlocking = entity.getSecondsToDisableBlocking(); + float secondsToDisableBlocking = attacker.getSecondsToDisableBlocking(); if (secondsToDisableBlocking > 0.0F && blocksAttacks != null) { - blocksAttacks.disable(level, this, secondsToDisableBlocking, itemBlockingWith); -+ blocksAttacks.disable(level, this, secondsToDisableBlocking, itemBlockingWith, entity); // Paper - Add PlayerShieldDisableEvent ++ blocksAttacks.disable(level, this, secondsToDisableBlocking, itemBlockingWith, attacker); // Paper - Add PlayerShieldDisableEvent } } -@@ -736,9 +_,29 @@ +@@ -721,9 +_,29 @@ } - public boolean canHarmPlayer(Player other) { + public boolean canHarmPlayer(final Player target) { - Team team = this.getTeam(); -- Team team1 = other.getTeam(); -- return team == null || !team.isAlliedTo(team1) || team.isAllowFriendlyFire(); +- Team otherTeam = target.getTeam(); +- return team == null || !team.isAlliedTo(otherTeam) || team.isAllowFriendlyFire(); + // CraftBukkit start - Change to check OTHER player's scoreboard team according to API + // To summarize this method's logic, it's "Can parameter hurt this" + org.bukkit.scoreboard.Team team; -+ if (other instanceof ServerPlayer) { -+ ServerPlayer thatPlayer = (ServerPlayer) other; ++ if (target instanceof ServerPlayer) { ++ ServerPlayer thatPlayer = (ServerPlayer) target; + team = thatPlayer.getBukkitEntity().getScoreboard().getPlayerTeam(thatPlayer.getBukkitEntity()); + if (team == null || team.allowFriendlyFire()) { + return true; + } + } else { + // This should never be called, but is implemented anyway -+ org.bukkit.OfflinePlayer thisPlayer = other.level().getCraftServer().getOfflinePlayer(other.getScoreboardName()); -+ team = other.level().getCraftServer().getScoreboardManager().getMainScoreboard().getPlayerTeam(thisPlayer); ++ org.bukkit.OfflinePlayer thisPlayer = target.level().getCraftServer().getOfflinePlayer(target.getScoreboardName()); ++ team = target.level().getCraftServer().getScoreboardManager().getMainScoreboard().getPlayerTeam(thisPlayer); + if (team == null || team.allowFriendlyFire()) { + return true; + } @@ -167,30 +167,30 @@ } @Override -@@ -752,7 +_,12 @@ +@@ -737,7 +_,12 @@ } @Override -- protected void actuallyHurt(ServerLevel level, DamageSource damageSource, float amount) { +- protected void actuallyHurt(final ServerLevel level, final DamageSource source, float dmg) { + // CraftBukkit start -+ protected boolean actuallyHurt(ServerLevel level, DamageSource damageSource, float amount, org.bukkit.event.entity.EntityDamageEvent event) { // void -> boolean ++ protected boolean actuallyHurt(final ServerLevel level, final DamageSource source, float dmg, org.bukkit.event.entity.EntityDamageEvent event) { // void -> boolean + if (true) { -+ return super.actuallyHurt(level, damageSource, amount, event); ++ return super.actuallyHurt(level, source, dmg, event); + } + // CraftBukkit end - if (!this.isInvulnerableTo(level, damageSource)) { - amount = this.getDamageAfterArmorAbsorb(damageSource, amount); - amount = this.getDamageAfterMagicAbsorb(damageSource, amount); -@@ -764,7 +_,7 @@ + if (!this.isInvulnerableTo(level, source)) { + dmg = this.getDamageAfterArmorAbsorb(source, dmg); + dmg = this.getDamageAfterMagicAbsorb(source, dmg); +@@ -749,7 +_,7 @@ } if (var8 != 0.0F) { -- this.causeFoodExhaustion(damageSource.getFoodExhaustion()); -+ this.causeFoodExhaustion(damageSource.getFoodExhaustion(), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.DAMAGED); // CraftBukkit - EntityExhaustionEvent - this.getCombatTracker().recordDamage(damageSource, var8); +- this.causeFoodExhaustion(source.getFoodExhaustion()); ++ this.causeFoodExhaustion(source.getFoodExhaustion(), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.DAMAGED); // CraftBukkit - EntityExhaustionEvent + this.getCombatTracker().recordDamage(source, var8); this.setHealth(this.getHealth() - var8); if (var8 < 3.4028235E37F) { -@@ -774,6 +_,7 @@ +@@ -759,6 +_,7 @@ this.gameEvent(GameEvent.ENTITY_DAMAGE); } } @@ -198,7 +198,7 @@ } public boolean isTextFilteringEnabled() { -@@ -860,14 +_,16 @@ +@@ -852,14 +_,16 @@ } @Override @@ -218,71 +218,71 @@ } @Override -@@ -946,15 +_,25 @@ +@@ -938,15 +_,25 @@ } - public void attack(Entity target) { -- if (!this.cannotAttack(target)) { + public void attack(final Entity entity) { +- if (!this.cannotAttack(entity)) { + // Paper start - PlayerAttackEntityEvent -+ boolean willAttack = !this.cannotAttack(target); // Vanilla logic ++ boolean willAttack = !this.cannotAttack(entity); // Vanilla logic + io.papermc.paper.event.player.PrePlayerAttackEntityEvent playerAttackEntityEvent = new io.papermc.paper.event.player.PrePlayerAttackEntityEvent( + (org.bukkit.entity.Player) this.getBukkitEntity(), -+ target.getBukkitEntity(), ++ entity.getBukkitEntity(), + willAttack + ); + + if (playerAttackEntityEvent.callEvent() && willAttack) { // Logic moved to willAttack local variable. + // Paper end - PlayerAttackEntityEvent - float f = this.isAutoSpinAttack() ? this.autoSpinAttackDmg : (float)this.getAttributeValue(Attributes.ATTACK_DAMAGE); - ItemStack weaponItem = this.getWeaponItem(); -- DamageSource damageSource = this.createAttackSource(weaponItem); -+ DamageSource damageSource = this.createAttackSource(weaponItem); final DamageSource dmgSourceFinal = damageSource; // Paper - damage events + float baseDamage = this.isAutoSpinAttack() ? this.autoSpinAttackDmg : (float)this.getAttributeValue(Attributes.ATTACK_DAMAGE); + ItemStack attackingItemStack = this.getWeaponItem(); +- DamageSource damageSource = this.createAttackSource(attackingItemStack); ++ DamageSource damageSource = this.createAttackSource(attackingItemStack); final DamageSource dmgSourceFinal = damageSource; // Paper - damage events float attackStrengthScale = this.getAttackStrengthScale(0.5F); - float f1 = attackStrengthScale * (this.getEnchantedDamage(target, f, damageSource) - f); - f *= this.baseDamageScaleFactor(); + float magicBoost = attackStrengthScale * (this.getEnchantedDamage(entity, baseDamage, damageSource) - baseDamage); + baseDamage *= this.baseDamageScaleFactor(); this.onAttack(); -- if (!this.deflectProjectile(target)) { -+ final float dmgFinal = f1; // Paper - damage events -+ if (!this.deflectProjectile(target, () -> !org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(target, dmgSourceFinal, dmgFinal, false))) { - if (f > 0.0F || f1 > 0.0F) { - boolean flag = attackStrengthScale > 0.9F; - boolean flag1; -@@ -967,7 +_,9 @@ - - f += weaponItem.getItem().getAttackDamageBonus(target, f, damageSource); - boolean flag2 = flag && this.canCriticalAttack(target); -+ flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits - if (flag2) { +- if (!this.deflectProjectile(entity)) { ++ final float dmgFinal = magicBoost; // Paper - damage events ++ if (!this.deflectProjectile(entity, () -> !org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(entity, dmgSourceFinal, dmgFinal, false))) { + if (baseDamage > 0.0F || magicBoost > 0.0F) { + boolean fullStrengthAttack = attackStrengthScale > 0.9F; + boolean knockbackAttack; +@@ -959,7 +_,9 @@ + + baseDamage += attackingItemStack.getItem().getAttackDamageBonus(entity, baseDamage, damageSource); + boolean criticalAttack = fullStrengthAttack && this.canCriticalAttack(entity); ++ criticalAttack = criticalAttack && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits + if (criticalAttack) { + damageSource = damageSource.critical(); // Paper - critical damage API - f *= 1.5F; + baseDamage *= 1.5F; } -@@ -990,7 +_,7 @@ - this.setLastHurtMob(target); - this.itemAttackInteraction(target, weaponItem, damageSource, true); - this.damageStatsAndHearts(target, f3); +@@ -982,7 +_,7 @@ + this.setLastHurtMob(entity); + this.itemAttackInteraction(entity, attackingItemStack, damageSource, true); + this.damageStatsAndHearts(entity, oldLivingEntityHealth); - this.causeFoodExhaustion(0.1F); + this.causeFoodExhaustion(this.level().spigotConfig.combatExhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.ATTACK); // CraftBukkit - EntityExhaustionEvent // Spigot - Change to use configurable value } else { this.playServerSideSound(SoundEvents.PLAYER_ATTACK_NODAMAGE); } -@@ -1002,7 +_,7 @@ +@@ -994,7 +_,7 @@ } - private void playServerSideSound(SoundEvent sound) { + private void playServerSideSound(final SoundEvent sound) { - this.level().playSound(null, this.getX(), this.getY(), this.getZ(), sound, this.getSoundSource(), 1.0F, 1.0F); + sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), sound, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility } - private DamageSource createAttackSource(ItemStack stack) { -@@ -1013,11 +_,12 @@ - return !target.isAttackable() || target.skipAttackInteraction(this); + private DamageSource createAttackSource(final ItemStack attackingItemStack) { +@@ -1005,11 +_,12 @@ + return !entity.isAttackable() || entity.skipAttackInteraction(this); } -- private boolean deflectProjectile(Entity target) { -+ private boolean deflectProjectile(Entity target, java.util.function.BooleanSupplier callEvent) { // Paper - damage events - if (target.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE) - && target instanceof Projectile projectile +- private boolean deflectProjectile(final Entity entity) { ++ private boolean deflectProjectile(final Entity entity, final java.util.function.BooleanSupplier callEvent) { // Paper - damage events + if (entity.is(EntityTypeTags.REDIRECTABLE_PROJECTILE) + && entity instanceof Projectile projectile + && callEvent.getAsBoolean() // Paper - damage events && projectile.deflect(ProjectileDeflection.AIM_DEFLECT, this, EntityReference.of(this), true)) { - this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource()); @@ -290,17 +290,15 @@ return true; } else { return false; -@@ -1110,21 +_,43 @@ - public void causeExtraKnockback(Entity target, float strength, Vec3 currentMovement) { - if (strength > 0.0F) { - if (target instanceof LivingEntity livingEntity) { -- livingEntity.knockback(strength, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)), -Mth.cos(this.getYRot() * (float) (Math.PI / 180.0))); -+ livingEntity.knockback(strength, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)), -Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // Paper - knockback events +@@ -1109,19 +_,40 @@ + public void causeExtraKnockback(final Entity entity, final float knockbackAmount, final Vec3 oldMovement) { + if (knockbackAmount > 0.0F) { + if (entity instanceof LivingEntity livingTarget) { +- livingTarget.knockback(knockbackAmount, Mth.sin(this.getYRot() * Mth.DEG_TO_RAD), -Mth.cos(this.getYRot() * Mth.DEG_TO_RAD)); ++ livingTarget.knockback(knockbackAmount, Mth.sin(this.getYRot() * Mth.DEG_TO_RAD), -Mth.cos(this.getYRot() * Mth.DEG_TO_RAD), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // Paper - knockback events } else { - target.push( - -Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)) * strength, 0.1, Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)) * strength -+ , this // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent - ); +- entity.push(-Mth.sin(this.getYRot() * Mth.DEG_TO_RAD) * knockbackAmount, 0.1, Mth.cos(this.getYRot() * Mth.DEG_TO_RAD) * knockbackAmount); ++ entity.push(-Mth.sin(this.getYRot() * Mth.DEG_TO_RAD) * knockbackAmount, 0.1, Mth.cos(this.getYRot() * Mth.DEG_TO_RAD) * knockbackAmount, this); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent } this.setDeltaMovement(this.getDeltaMovement().multiply(0.6, 1.0, 0.6)); @@ -311,11 +309,11 @@ + // Paper end - Configurable sprint interruption on attack } - if (target instanceof ServerPlayer && target.hurtMarked) { + if (entity instanceof ServerPlayer && entity.hurtMarked) { + // CraftBukkit start - Add Velocity Event + boolean cancelled = false; -+ org.bukkit.entity.Player player = (org.bukkit.entity.Player) target.getBukkitEntity(); -+ org.bukkit.util.Vector velocity = org.bukkit.craftbukkit.util.CraftVector.toBukkit(currentMovement); ++ org.bukkit.entity.Player player = (org.bukkit.entity.Player) entity.getBukkitEntity(); ++ org.bukkit.util.Vector velocity = org.bukkit.craftbukkit.util.CraftVector.toBukkit(oldMovement); + + org.bukkit.event.player.PlayerVelocityEvent event = new org.bukkit.event.player.PlayerVelocityEvent(player, velocity.clone()); + this.level().getCraftServer().getPluginManager().callEvent(event); @@ -327,32 +325,32 @@ + } + + if (!cancelled) { - ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); - target.hurtMarked = false; - target.setDeltaMovement(currentMovement); + ((ServerPlayer)entity).connection.send(new ClientboundSetEntityMotionPacket(entity)); + entity.hurtMarked = false; + entity.setDeltaMovement(oldMovement); + } + // CraftBukkit end } } -@@ -1145,8 +_,11 @@ - && !(livingEntity instanceof ArmorStand armorStand && armorStand.isMarker()) - && this.distanceToSqr(livingEntity) < 9.0) { - float f1 = this.getEnchantedDamage(livingEntity, var12, damageSource) * strengthScale; -- if (livingEntity.hurtServer(serverLevel, damageSource, f1)) { -- livingEntity.knockback(0.4F, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)), -Mth.cos(this.getYRot() * (float) (Math.PI / 180.0))); +@@ -1142,8 +_,11 @@ + && !(nearby instanceof ArmorStand armorStand && armorStand.isMarker()) + && this.distanceToSqr(nearby) < 9.0) { + float enchantedDamage = this.getEnchantedDamage(nearby, var12, damageSource) * attackStrengthScale; +- if (nearby.hurtServer(serverLevel, damageSource, enchantedDamage)) { +- nearby.knockback(0.4F, Mth.sin(this.getYRot() * Mth.DEG_TO_RAD), -Mth.cos(this.getYRot() * Mth.DEG_TO_RAD)); + // Paper start - Only apply knockback if the event is not canceled -+ livingEntity.lastDamageCancelled = false; -+ if (livingEntity.hurtServer(serverLevel, damageSource.knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK), f1) && !livingEntity.lastDamageCancelled) { ++ nearby.lastDamageCancelled = false; ++ if (nearby.hurtServer(serverLevel, damageSource.knownCause(org.bukkit.event.entity.EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK), enchantedDamage) && !nearby.lastDamageCancelled) { + // Paper end - Only apply knockback if the event is not canceled -+ livingEntity.knockback(0.4F, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)), -Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SWEEP_ATTACK); // Paper - knockback events - EnchantmentHelper.doPostAttackEffects(serverLevel, livingEntity, damageSource); ++ nearby.knockback(0.4F, Mth.sin(this.getYRot() * Mth.DEG_TO_RAD), -Mth.cos(this.getYRot() * Mth.DEG_TO_RAD), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SWEEP_ATTACK); // Paper - knockback events + EnchantmentHelper.doPostAttackEffects(serverLevel, nearby, damageSource); } } -@@ -1177,7 +_,16 @@ - - @Override - public boolean stabAttack(EquipmentSlot slot, Entity target, float damageAmount, boolean damage, boolean knockback, boolean dismount) { +@@ -1176,7 +_,16 @@ + public boolean stabAttack( + final EquipmentSlot slot, final Entity target, float baseDamage, final boolean dealsDamage, final boolean dealsKnockback, final boolean dismounts + ) { - if (this.cannotAttack(target)) { + // Paper start - PlayerAttackEntityEvent + boolean cannotAttack = this.cannotAttack(target); // Vanilla logic @@ -366,67 +364,66 @@ + // Paper end - PlayerAttackEntityEvent return false; } else { - ItemStack itemBySlot = this.getItemBySlot(slot); -@@ -1188,7 +_,8 @@ - damageAmount *= this.baseDamageScaleFactor(); + ItemStack weaponItem = this.getItemBySlot(slot); +@@ -1187,7 +_,8 @@ + baseDamage *= this.baseDamageScaleFactor(); } -- if (knockback && this.deflectProjectile(target)) { -+ final float dmgFinal = f; // Paper - damage events -+ if (knockback && this.deflectProjectile(target, () -> !org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(target, damageSource, dmgFinal, false))) { // Paper - damage events +- if (dealsKnockback && this.deflectProjectile(target)) { ++ final float dmgFinal = magicBoost; // Paper - damage events ++ if (dealsKnockback && this.deflectProjectile(target, () -> !org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(target, damageSource, dmgFinal, false))) { // Paper - damage events return true; } else { - float f1 = damage ? damageAmount + f : 0.0F; -@@ -1216,7 +_,7 @@ + float totalDamage = dealsDamage ? baseDamage + magicBoost : 0.0F; +@@ -1215,7 +_,7 @@ this.setLastHurtMob(target); - this.itemAttackInteraction(target, itemBySlot, damageSource, flag); - this.damageStatsAndHearts(target, f2); + this.itemAttackInteraction(target, weaponItem, damageSource, wasHurt); + this.damageStatsAndHearts(target, oldLivingEntityHealth); - this.causeFoodExhaustion(0.1F); + this.causeFoodExhaustion(this.level().spigotConfig.combatExhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.ATTACK); // CraftBukkit - EntityExhaustionEvent // Spigot - Change to use configurable value return true; } } -@@ -1227,8 +_,8 @@ +@@ -1226,8 +_,8 @@ } @Override -- public void remove(Entity.RemovalReason reason) { +- public void remove(final Entity.RemovalReason reason) { - super.remove(reason); -+ public void remove(Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause eventCause) { // CraftBukkit - add Bukkit remove cause ++ public void remove(final Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause eventCause) { // CraftBukkit - add Bukkit remove cause + super.remove(reason, eventCause); // CraftBukkit - add Bukkit remove cause this.inventoryMenu.removed(this); if (this.hasContainerOpen()) { this.doCloseContainer(); -@@ -1296,6 +_,12 @@ +@@ -1295,6 +_,12 @@ } - public Either startSleepInBed(BlockPos bedPos) { + public Either startSleepInBed(final BlockPos pos) { + // CraftBukkit start -+ return this.startSleepInBed(bedPos, false); ++ return this.startSleepInBed(pos, false); + } + -+ public Either startSleepInBed(BlockPos bedPos, boolean force) { ++ public Either startSleepInBed(final BlockPos pos, final boolean force) { + // CraftBukkit end - this.startSleeping(bedPos); + this.startSleeping(pos); this.sleepCounter = 0; return Either.right(Unit.INSTANCE); -@@ -1407,7 +_,7 @@ +@@ -1411,7 +_,7 @@ @Override - public boolean causeFallDamage(double fallDistance, float damageMultiplier, DamageSource damageSource) { + public boolean causeFallDamage(final double fallDistance, final float damageModifier, final DamageSource damageSource) { - if (this.abilities.mayfly) { + if (this.abilities.mayfly && !this.flyingFallDamage.toBooleanOrElse(false)) { // Paper - flying fall damage return false; } else { if (fallDistance >= 2.0) { -@@ -1448,7 +_,15 @@ +@@ -1432,7 +_,15 @@ } public void startFallFlying() { -- this.setSharedFlag(Entity.FLAG_FALL_FLYING, true); + // CraftBukkit start + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callToggleGlideEvent(this, true).isCancelled()) { -+ this.setSharedFlag(Entity.FLAG_FALL_FLYING, true); + this.setSharedFlag(Entity.FLAG_FALL_FLYING, true); + } else { + // SPIGOT-5542: must toggle like below + this.setSharedFlag(Entity.FLAG_FALL_FLYING, true); @@ -436,16 +433,16 @@ } @Override -@@ -1546,7 +_,7 @@ +@@ -1530,7 +_,7 @@ - if (levels > 0 && this.experienceLevel % 5 == 0 && this.lastLevelUpTime < this.tickCount - 100.0F) { - float f = this.experienceLevel > 30 ? 1.0F : this.experienceLevel / 30.0F; -- this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_LEVELUP, this.getSoundSource(), f * 0.75F, 1.0F); -+ Player.sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_LEVELUP, this.getSoundSource(), f * 0.75F, 1.0F); // Paper - send while respecting visibility + if (amount > 0 && this.experienceLevel % 5 == 0 && this.lastLevelUpTime < this.tickCount - 100.0F) { + float vol = this.experienceLevel > 30 ? 1.0F : this.experienceLevel / 30.0F; +- this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_LEVELUP, this.getSoundSource(), vol * 0.75F, 1.0F); ++ Player.sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_LEVELUP, this.getSoundSource(), vol * 0.75F, 1.0F); // Paper - send while respecting visibility this.lastLevelUpTime = this.tickCount; } } -@@ -1554,15 +_,35 @@ +@@ -1538,15 +_,35 @@ public int getXpNeededForNextLevel() { if (this.experienceLevel >= 30) { return 112 + (this.experienceLevel - 30) * 9; @@ -464,18 +461,18 @@ + } + // Paper end - send while respecting visibility + - public void causeFoodExhaustion(float exhaustion) { + public void causeFoodExhaustion(final float amount) { + // CraftBukkit start -+ this.causeFoodExhaustion(exhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.UNKNOWN); ++ this.causeFoodExhaustion(amount, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.UNKNOWN); + } + -+ public void causeFoodExhaustion(float exhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason reason) { ++ public void causeFoodExhaustion(final float amount, final org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason reason) { + // CraftBukkit end if (!this.abilities.invulnerable) { if (!this.level().isClientSide()) { -- this.foodData.addExhaustion(exhaustion); +- this.foodData.addExhaustion(amount); + // CraftBukkit start -+ org.bukkit.event.entity.EntityExhaustionEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerExhaustionEvent(this, reason, exhaustion); ++ org.bukkit.event.entity.EntityExhaustionEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerExhaustionEvent(this, reason, amount); + if (!event.isCancelled()) { + this.foodData.addExhaustion(event.getExhaustion()); + } @@ -483,7 +480,7 @@ } } } -@@ -1821,10 +_,31 @@ +@@ -1808,10 +_,31 @@ @Override public void onAttack() { @@ -516,7 +513,7 @@ public void resetOnlyAttackStrengthTicker() { this.attackStrengthTicker = 0; } -@@ -1856,17 +_,32 @@ +@@ -1843,17 +_,32 @@ return ImmutableList.of(Pose.STANDING, Pose.CROUCHING, Pose.SWIMMING); } @@ -535,23 +532,23 @@ + // Paper end - PlayerReadyArrowEvent + @Override - public ItemStack getProjectile(ItemStack shootable) { - if (!(shootable.getItem() instanceof ProjectileWeaponItem)) { + public ItemStack getProjectile(final ItemStack heldWeapon) { + if (!(heldWeapon.getItem() instanceof ProjectileWeaponItem)) { return ItemStack.EMPTY; } else { -- Predicate supportedHeldProjectiles = ((ProjectileWeaponItem)shootable.getItem()).getSupportedHeldProjectiles(); +- Predicate supportedProjectiles = ((ProjectileWeaponItem)heldWeapon.getItem()).getSupportedHeldProjectiles(); + final org.apache.commons.lang3.mutable.MutableBoolean anyEventCancelled = new org.apache.commons.lang3.mutable.MutableBoolean(); // Paper - PlayerReadyArrowEvent -+ Predicate supportedHeldProjectiles = ((ProjectileWeaponItem)shootable.getItem()).getSupportedHeldProjectiles().and(item -> this.tryReadyArrow(shootable, item, anyEventCancelled)); // Paper - PlayerReadyArrowEvent - ItemStack heldProjectile = ProjectileWeaponItem.getHeldProjectile(this, supportedHeldProjectiles); ++ Predicate supportedProjectiles = ((ProjectileWeaponItem)heldWeapon.getItem()).getSupportedHeldProjectiles().and(item -> this.tryReadyArrow(heldWeapon, item, anyEventCancelled)); // Paper - PlayerReadyArrowEvent + ItemStack heldProjectile = ProjectileWeaponItem.getHeldProjectile(this, supportedProjectiles); if (!heldProjectile.isEmpty()) { return heldProjectile; } else { -- supportedHeldProjectiles = ((ProjectileWeaponItem)shootable.getItem()).getAllSupportedProjectiles(); -+ supportedHeldProjectiles = ((ProjectileWeaponItem)shootable.getItem()).getAllSupportedProjectiles().and(item -> this.tryReadyArrow(shootable, item, anyEventCancelled)); // Paper - PlayerReadyArrowEvent +- supportedProjectiles = ((ProjectileWeaponItem)heldWeapon.getItem()).getAllSupportedProjectiles(); ++ supportedProjectiles = ((ProjectileWeaponItem)heldWeapon.getItem()).getAllSupportedProjectiles().and(item -> this.tryReadyArrow(heldWeapon, item, anyEventCancelled)); // Paper - PlayerReadyArrowEvent for (int i = 0; i < this.inventory.getContainerSize(); i++) { - ItemStack item = this.inventory.getItem(i); -@@ -1875,6 +_,7 @@ + ItemStack itemStack = this.inventory.getItem(i); +@@ -1862,6 +_,7 @@ } } @@ -559,7 +556,7 @@ return this.hasInfiniteMaterials() ? new ItemStack(Items.ARROW) : ItemStack.EMPTY; } } -@@ -2055,5 +_,6 @@ +@@ -2025,5 +_,6 @@ public static final Player.BedSleepingProblem OBSTRUCTED = new Player.BedSleepingProblem(Component.translatable("block.minecraft.bed.obstructed")); public static final Player.BedSleepingProblem OTHER_PROBLEM = new Player.BedSleepingProblem(null); public static final Player.BedSleepingProblem NOT_SAFE = new Player.BedSleepingProblem(Component.translatable("block.minecraft.bed.not_safe")); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/player/ProfilePublicKey.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/player/ProfilePublicKey.java.patch index 1290b7263d12..88d2fdbc6020 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/player/ProfilePublicKey.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/player/ProfilePublicKey.java.patch @@ -2,8 +2,8 @@ +++ b/net/minecraft/world/entity/player/ProfilePublicKey.java @@ -24,7 +_,7 @@ - public static ProfilePublicKey createValidated(SignatureValidator signatureValidator, UUID profileId, ProfilePublicKey.Data data) throws ProfilePublicKey.ValidationException { - if (!data.validateSignature(signatureValidator, profileId)) { + public static ProfilePublicKey createValidated(final SignatureValidator validator, final UUID profileId, final ProfilePublicKey.Data data) throws ProfilePublicKey.ValidationException { + if (!data.validateSignature(validator, profileId)) { - throw new ProfilePublicKey.ValidationException(INVALID_SIGNATURE); + throw new ProfilePublicKey.ValidationException(INVALID_SIGNATURE, org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PUBLIC_KEY_SIGNATURE); // Paper - kick event causes } else { @@ -16,7 +16,7 @@ + public final org.bukkit.event.player.PlayerKickEvent.Cause kickCause; // Paper + + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - public ValidationException(Component component) { + public ValidationException(final Component component) { + // Paper start + this(component, org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); + } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/EvokerFangs.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/EvokerFangs.java.patch index 81c63120251b..0be361d50e24 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/EvokerFangs.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/EvokerFangs.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/projectile/EvokerFangs.java +++ b/net/minecraft/world/entity/projectile/EvokerFangs.java -@@ -96,7 +_,7 @@ +@@ -99,7 +_,7 @@ } if (--this.lifeTicks < 0) { @@ -9,12 +9,12 @@ } } } -@@ -105,7 +_,7 @@ - LivingEntity owner = this.getOwner(); - if (target.isAlive() && !target.isInvulnerable() && target != owner) { - if (owner == null) { -- target.hurt(this.damageSources().magic(), 6.0F); -+ target.hurt(this.damageSources().magic().eventEntityDamager(this), 6.0F); // CraftBukkit +@@ -108,7 +_,7 @@ + LivingEntity currentOwner = this.getOwner(); + if (entity.isAlive() && !entity.isInvulnerable() && entity != currentOwner) { + if (currentOwner == null) { +- entity.hurt(this.damageSources().magic(), 6.0F); ++ entity.hurt(this.damageSources().magic().eventEntityDamager(this), 6.0F); // CraftBukkit } else { - if (owner.isAlliedTo(target)) { + if (currentOwner.isAlliedTo(entity)) { return; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/EyeOfEnder.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/EyeOfEnder.java.patch index 060c0ec1b92e..0c9a562f8db8 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/EyeOfEnder.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/EyeOfEnder.java.patch @@ -3,24 +3,24 @@ @@ -72,6 +_,12 @@ } - public void signalTo(Vec3 pos) { -+ // Paper start - Change EnderEye target without changing other things -+ this.signalTo(pos, true); + public void signalTo(final Vec3 target) { ++ // Paper start - Change EyeOfEnder target without changing other things ++ this.signalTo(target, true); + } + -+ public void signalTo(Vec3 pos, boolean update) { -+ // Paper end - Change EnderEye target without changing other things - Vec3 vec3 = pos.subtract(this.position()); - double d = vec3.horizontalDistance(); - if (d > 12.0) { ++ public void signalTo(final Vec3 target, final boolean update) { ++ // Paper end - Change EyeOfEnder target without changing other things + Vec3 delta = target.subtract(this.position()); + double horizontalDistance = delta.horizontalDistance(); + if (horizontalDistance > 12.0) { @@ -80,8 +_,10 @@ - this.target = pos; + this.target = target; } -+ if (update) { // Paper - Change EnderEye target without changing other things ++ if (update) { // Paper - Change EyeOfEnder target without changing other things this.life = 0; this.surviveAfterDeath = this.random.nextInt(5) > 0; -+ } // Paper - Change EnderEye target without changing other things ++ } // Paper - Change EyeOfEnder target without changing other things } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch index 15e4b962ee8e..99599a64ff97 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch @@ -6,25 +6,25 @@ public @Nullable LivingEntity attachedToEntity; + public java.util.@Nullable UUID spawningEntity; // Paper - public FireworkRocketEntity(EntityType type, Level level) { + public FireworkRocketEntity(final EntityType type, final Level level) { super(type, level); @@ -163,7 +_,7 @@ } - if (!this.noPhysics && this.isAlive() && hitResultOnMoveVector.getType() != HitResult.Type.MISS) { -- this.hitTargetOrDeflectSelf(hitResultOnMoveVector); -+ this.preHitTargetOrDeflectSelf(hitResultOnMoveVector); // CraftBukkit - projectile hit event + if (!this.noPhysics && this.isAlive() && hitResult.getType() != HitResult.Type.MISS) { +- this.hitTargetOrDeflectSelf(hitResult); ++ this.preHitTargetOrDeflectSelf(hitResult); // CraftBukkit - projectile hit event this.needsSync = true; } @@ -187,7 +_,11 @@ } - if (this.life > this.lifetime && this.level() instanceof ServerLevel serverLevel) { -- this.explode(serverLevel); + if (this.life > this.lifetime && this.level() instanceof ServerLevel level) { +- this.explode(level); + // Paper start - Call FireworkExplodeEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this)) { -+ this.explode(serverLevel); ++ this.explode(level); + } + // Paper end - Call FireworkExplodeEvent } @@ -39,31 +39,31 @@ } @Override - protected void onHitEntity(EntityHitResult result) { - super.onHitEntity(result); - if (this.level() instanceof ServerLevel serverLevel) { -- this.explode(serverLevel); + protected void onHitEntity(final EntityHitResult hitResult) { + super.onHitEntity(hitResult); + if (this.level() instanceof ServerLevel level) { +- this.explode(level); + // Paper start - Call FireworkExplodeEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this)) { -+ this.explode(serverLevel); ++ this.explode(level); + } + // Paper end - Call FireworkExplodeEvent } } @@ -211,7 +_,11 @@ - BlockPos blockPos = new BlockPos(result.getBlockPos()); - this.level().getBlockState(blockPos).entityInside(this.level(), blockPos, this, InsideBlockEffectApplier.NOOP, true); - if (this.level() instanceof ServerLevel serverLevel && this.hasExplosion()) { -- this.explode(serverLevel); + BlockPos pos = new BlockPos(hitResult.getBlockPos()); + this.level().getBlockState(pos).entityInside(this.level(), pos, this, InsideBlockEffectApplier.NOOP, true); + if (this.level() instanceof ServerLevel level && this.hasExplosion()) { +- this.explode(level); + // Paper start - Call FireworkExplodeEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this)) { -+ this.explode(serverLevel); ++ this.explode(level); + } + // Paper end - Call FireworkExplodeEvent } - super.onHitBlock(result); + super.onHitBlock(hitResult); @@ -283,6 +_,7 @@ output.putInt("LifeTime", this.lifetime); output.store("FireworksItem", ItemStack.CODEC, this.getItem()); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/FishingHook.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/FishingHook.java.patch index ebdb125b7d61..9632b2c537da 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/FishingHook.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/FishingHook.java.patch @@ -16,7 +16,7 @@ + public boolean skyInfluenced = true; + // CraftBukkit end + - private FishingHook(EntityType type, Level level, int luck, int lureSpeed) { + private FishingHook(final EntityType type, final Level level, final int luck, final int lureSpeed) { super(type, level); this.luck = Math.max(0, luck); this.lureSpeed = Math.max(0, lureSpeed); @@ -26,14 +26,14 @@ + // Paper end - Configurable fishing time ranges } - public FishingHook(EntityType type, Level level) { + public FishingHook(final EntityType type, final Level level) { @@ -152,12 +_,12 @@ super.tick(); - Player playerOwner = this.getPlayerOwner(); - if (playerOwner == null) { + Player owner = this.getPlayerOwner(); + if (owner == null) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause - } else if (this.level().isClientSide() || !this.shouldStopFishing(playerOwner)) { + } else if (this.level().isClientSide() || !this.shouldStopFishing(owner)) { if (this.onGround()) { this.life++; if (this.life >= 1200) { @@ -51,7 +51,7 @@ return; } - if (flag) { + if (isInWater) { this.setDeltaMovement(this.getDeltaMovement().multiply(0.3, 0.2, 0.3)); + new io.papermc.paper.event.entity.FishHookStateChangeEvent((org.bukkit.entity.FishHook) getBukkitEntity(), org.bukkit.entity.FishHook.HookState.BOBBING).callEvent(); // Paper - Add FishHookStateChangeEvent. #BOBBING this.currentState = FishingHook.FishHookState.BOBBING; @@ -75,24 +75,24 @@ } private void checkCollision() { - HitResult hitResultOnMoveVector = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity); -- this.hitTargetOrDeflectSelf(hitResultOnMoveVector); -+ this.preHitTargetOrDeflectSelf(hitResultOnMoveVector); + HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity); +- this.hitTargetOrDeflectSelf(hitResult); ++ this.preHitTargetOrDeflectSelf(hitResult); } @Override @@ -298,11 +_,11 @@ ServerLevel serverLevel = (ServerLevel)this.level(); - int i = 1; - BlockPos blockPos = pos.above(); -- if (this.random.nextFloat() < 0.25F && this.level().isRainingAt(blockPos)) { -+ if (this.rainInfluenced && this.random.nextFloat() < 0.25F && this.level().isRainingAt(blockPos)) { // CraftBukkit - i++; + int fishingSpeed = 1; + BlockPos above = blockPos.above(); +- if (this.random.nextFloat() < 0.25F && this.level().isRainingAt(above)) { ++ if (this.rainInfluenced && this.random.nextFloat() < 0.25F && this.level().isRainingAt(above)) { // CraftBukkit + fishingSpeed++; } -- if (this.random.nextFloat() < 0.5F && !this.level().canSeeSky(blockPos)) { -+ if (this.skyInfluenced && this.random.nextFloat() < 0.5F && !this.level().canSeeSky(blockPos)) { // CraftBukkit - i--; +- if (this.random.nextFloat() < 0.5F && !this.level().canSeeSky(above)) { ++ if (this.skyInfluenced && this.random.nextFloat() < 0.5F && !this.level().canSeeSky(above)) { // CraftBukkit + fishingSpeed--; } @@ -312,6 +_,10 @@ @@ -105,9 +105,9 @@ + // CraftBukkit end } } else if (this.timeUntilHooked > 0) { - this.timeUntilHooked -= i; + this.timeUntilHooked -= fishingSpeed; @@ -335,6 +_,12 @@ - serverLevel.sendParticles(ParticleTypes.FISHING, d, d1, d2, 0, -f2, 0.01, f1, 1.0); + serverLevel.sendParticles(ParticleTypes.FISHING, fishX, fishY, fishZ, 0, -particleZMovement, 0.01, particleXMovement, 1.0); } } else { + // CraftBukkit start @@ -117,9 +117,9 @@ + } + // CraftBukkit end this.playSound(SoundEvents.FISHING_BOBBER_SPLASH, 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); - double d3 = this.getY() + 0.5; + double y = this.getY() + 0.5; serverLevel.sendParticles( -@@ -386,14 +_,31 @@ +@@ -378,14 +_,31 @@ } if (this.timeUntilLured <= 0) { @@ -153,89 +153,84 @@ + } + // Paper end - more projectile api - extract time until lured reset logic - public boolean calculateOpenWater(BlockPos pos) { - FishingHook.OpenWaterType openWaterType = FishingHook.OpenWaterType.INVALID; -@@ -452,15 +_,31 @@ - protected void readAdditionalSaveData(ValueInput input) { + public boolean calculateOpenWater(final BlockPos blockPos) { + FishingHook.OpenWaterType previousLayer = FishingHook.OpenWaterType.INVALID; +@@ -444,15 +_,30 @@ + protected void readAdditionalSaveData(final ValueInput input) { } -+ + // Paper start - Add hand parameter to PlayerFishEvent + @Deprecated @io.papermc.paper.annotation.DoNotUse - public int retrieve(ItemStack stack) { -+ return this.retrieve(stack, net.minecraft.world.InteractionHand.MAIN_HAND); + public int retrieve(final ItemStack rod) { ++ return this.retrieve(rod, net.minecraft.world.InteractionHand.MAIN_HAND); + } + -+ public int retrieve(ItemStack stack, net.minecraft.world.InteractionHand hand) { ++ public int retrieve(final ItemStack rod, final net.minecraft.world.InteractionHand hand) { + // Paper end - Add hand parameter to PlayerFishEvent - Player playerOwner = this.getPlayerOwner(); - if (!this.level().isClientSide() && playerOwner != null && !this.shouldStopFishing(playerOwner)) { - int i = 0; + Player owner = this.getPlayerOwner(); + if (!this.level().isClientSide() && owner != null && !this.shouldStopFishing(owner)) { + int dmg = 0; if (this.hookedIn != null) { + // CraftBukkit start -+ org.bukkit.event.player.PlayerFishEvent playerFishEvent = new org.bukkit.event.player.PlayerFishEvent((org.bukkit.entity.Player) playerOwner.getBukkitEntity(), this.hookedIn.getBukkitEntity(), (org.bukkit.entity.FishHook) this.getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), org.bukkit.event.player.PlayerFishEvent.State.CAUGHT_ENTITY); // Paper - Add hand parameter to PlayerFishEvent ++ org.bukkit.event.player.PlayerFishEvent playerFishEvent = new org.bukkit.event.player.PlayerFishEvent((org.bukkit.entity.Player) owner.getBukkitEntity(), this.hookedIn.getBukkitEntity(), (org.bukkit.entity.FishHook) this.getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), org.bukkit.event.player.PlayerFishEvent.State.CAUGHT_ENTITY); // Paper - Add hand parameter to PlayerFishEvent + if (!playerFishEvent.callEvent()) { + return 0; + } + if (this.hookedIn != null) { // Paper - re-check to see if there is a hooked entity + // CraftBukkit end this.pullEntity(this.hookedIn); - CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer)playerOwner, stack, this, Collections.emptyList()); + CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer)owner, rod, this, Collections.emptyList()); this.level().broadcastEntityEvent(this, EntityEvent.FISHING_ROD_REEL_IN); - i = this.hookedIn instanceof ItemEntity ? 3 : 5; + dmg = this.hookedIn instanceof ItemEntity ? 3 : 5; + } // Paper - re-check to see if there is a hooked entity } else if (this.nibble > 0) { - LootParams lootParams = new LootParams.Builder((ServerLevel)this.level()) + LootParams params = new LootParams.Builder((ServerLevel)this.level()) .withParameter(LootContextParams.ORIGIN, this.position()) -@@ -474,18 +_,27 @@ +@@ -466,14 +_,27 @@ - for (ItemStack itemStack : randomItems) { - ItemEntity itemEntity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemStack); + for (ItemStack itemStack : items) { + ItemEntity entity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemStack); + // CraftBukkit start -+ org.bukkit.event.player.PlayerFishEvent playerFishEvent = new org.bukkit.event.player.PlayerFishEvent((org.bukkit.entity.Player) playerOwner.getBukkitEntity(), itemEntity.getBukkitEntity(), (org.bukkit.entity.FishHook) this.getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), org.bukkit.event.player.PlayerFishEvent.State.CAUGHT_FISH); // Paper - itemEntity may be null // Paper - Add hand parameter to PlayerFishEvent ++ org.bukkit.event.player.PlayerFishEvent playerFishEvent = new org.bukkit.event.player.PlayerFishEvent((org.bukkit.entity.Player) owner.getBukkitEntity(), entity.getBukkitEntity(), (org.bukkit.entity.FishHook) this.getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), org.bukkit.event.player.PlayerFishEvent.State.CAUGHT_FISH); // Paper - itemEntity may be null // Paper - Add hand parameter to PlayerFishEvent + playerFishEvent.setExpToDrop(this.random.nextInt(6) + 1); + if (!playerFishEvent.callEvent()) { + return 0; + } + // CraftBukkit end - double d = playerOwner.getX() - this.getX(); - double d1 = playerOwner.getY() - this.getY(); - double d2 = playerOwner.getZ() - this.getZ(); - double d3 = 0.1; - itemEntity.setDeltaMovement(d * 0.1, d1 * 0.1 + Math.sqrt(Math.sqrt(d * d + d1 * d1 + d2 * d2)) * 0.08, d2 * 0.1); - this.level().addFreshEntity(itemEntity); -- playerOwner.level() -- .addFreshEntity( -- new ExperienceOrb( -- playerOwner.level(), playerOwner.getX(), playerOwner.getY() + 0.5, playerOwner.getZ() + 0.5, this.random.nextInt(6) + 1 -- ) -- ); + double xa = owner.getX() - this.getX(); + double ya = owner.getY() - this.getY(); + double za = owner.getZ() - this.getZ(); + double speed = 0.1; + entity.setDeltaMovement(xa * 0.1, ya * 0.1 + Math.sqrt(Math.sqrt(xa * xa + ya * ya + za * za)) * 0.08, za * 0.1); + this.level().addFreshEntity(entity); +- owner.level() +- .addFreshEntity(new ExperienceOrb(owner.level(), owner.getX(), owner.getY() + 0.5, owner.getZ() + 0.5, this.random.nextInt(6) + 1)); + if (playerFishEvent.getExpToDrop() > 0) { // CraftBukkit - custom exp -+ playerOwner.level() ++ owner.level() + .addFreshEntity( + new ExperienceOrb( -+ playerOwner.level(), new net.minecraft.world.phys.Vec3(playerOwner.getX(), playerOwner.getY() + 0.5, playerOwner.getZ() + 0.5), net.minecraft.world.phys.Vec3.ZERO, playerFishEvent.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.FISHING, this.getPlayerOwner(), this // Paper ++ owner.level(), new net.minecraft.world.phys.Vec3(owner.getX(), owner.getY() + 0.5, owner.getZ() + 0.5), net.minecraft.world.phys.Vec3.ZERO, playerFishEvent.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.FISHING, this.getPlayerOwner(), this // Paper + ) + ); + } if (itemStack.is(ItemTags.FISHES)) { - playerOwner.awardStat(Stats.FISH_CAUGHT, 1); + owner.awardStat(Stats.FISH_CAUGHT, 1); } -@@ -495,10 +_,24 @@ +@@ -483,10 +_,24 @@ } if (this.onGround()) { + // CraftBukkit start -+ org.bukkit.event.player.PlayerFishEvent playerFishEvent = new org.bukkit.event.player.PlayerFishEvent((org.bukkit.entity.Player) playerOwner.getBukkitEntity(), null, (org.bukkit.entity.FishHook) this.getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), org.bukkit.event.player.PlayerFishEvent.State.IN_GROUND); // Paper - Add hand parameter to PlayerFishEvent ++ org.bukkit.event.player.PlayerFishEvent playerFishEvent = new org.bukkit.event.player.PlayerFishEvent((org.bukkit.entity.Player) owner.getBukkitEntity(), null, (org.bukkit.entity.FishHook) this.getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), org.bukkit.event.player.PlayerFishEvent.State.IN_GROUND); // Paper - Add hand parameter to PlayerFishEvent + if (!playerFishEvent.callEvent()) { + return 0; + } + // CraftBukkit end - i = 2; + dmg = 2; } + // CraftBukkit start -+ if (i == 0) { -+ org.bukkit.event.player.PlayerFishEvent playerFishEvent = new org.bukkit.event.player.PlayerFishEvent((org.bukkit.entity.Player) playerOwner.getBukkitEntity(), null, (org.bukkit.entity.FishHook) this.getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), org.bukkit.event.player.PlayerFishEvent.State.REEL_IN); // Paper - Add hand parameter to PlayerFishEvent ++ if (dmg == 0) { ++ org.bukkit.event.player.PlayerFishEvent playerFishEvent = new org.bukkit.event.player.PlayerFishEvent((org.bukkit.entity.Player) owner.getBukkitEntity(), null, (org.bukkit.entity.FishHook) this.getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), org.bukkit.event.player.PlayerFishEvent.State.REEL_IN); // Paper - Add hand parameter to PlayerFishEvent + if (!playerFishEvent.callEvent()) { + return 0; + } @@ -244,25 +239,25 @@ - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause - return i; + return dmg; } else { return 0; -@@ -528,9 +_,9 @@ +@@ -516,9 +_,9 @@ } @Override -- public void remove(Entity.RemovalReason reason) { -+ public void remove(Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause +- public void remove(final Entity.RemovalReason reason) { ++ public void remove(final Entity.RemovalReason reason, final org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause this.updateOwnerInfo(null); - super.remove(reason); + super.remove(reason, cause); // CraftBukkit - add Bukkit remove cause } @Override -@@ -576,7 +_,7 @@ +@@ -564,7 +_,7 @@ if (this.getPlayerOwner() == null) { - int data = packet.getData(); - LOGGER.error("Failed to recreate fishing hook on client. {} (id: {}) is not a valid owner.", this.level().getEntity(data), data); + int ownerId = packet.getData(); + LOGGER.error("Failed to recreate fishing hook on client. {} (id: {}) is not a valid owner.", this.level().getEntity(ownerId), ownerId); - this.discard(); + this.discard(null); // CraftBukkit - add Bukkit remove cause } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/LlamaSpit.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/LlamaSpit.java.patch index 81a54ac237bf..311ea6aa01f7 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/LlamaSpit.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/LlamaSpit.java.patch @@ -2,15 +2,15 @@ +++ b/net/minecraft/world/entity/projectile/LlamaSpit.java @@ -43,16 +_,16 @@ super.tick(); - Vec3 deltaMovement = this.getDeltaMovement(); - HitResult hitResultOnMoveVector = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity); -- this.hitTargetOrDeflectSelf(hitResultOnMoveVector); -+ this.preHitTargetOrDeflectSelf(hitResultOnMoveVector); // CraftBukkit - projectile hit event - double d = this.getX() + deltaMovement.x; - double d1 = this.getY() + deltaMovement.y; - double d2 = this.getZ() + deltaMovement.z; + Vec3 movement = this.getDeltaMovement(); + HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity); +- this.hitTargetOrDeflectSelf(hitResult); ++ this.preHitTargetOrDeflectSelf(hitResult); // CraftBukkit - projectile hit event + double x = this.getX() + movement.x; + double y = this.getY() + movement.y; + double z = this.getZ() + movement.z; this.updateRotation(); - float f = 0.99F; + float inertia = 0.99F; if (this.level().getBlockStates(this.getBoundingBox()).noneMatch(BlockBehaviour.BlockStateBase::isAir)) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause @@ -18,11 +18,11 @@ - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause } else { - this.setDeltaMovement(deltaMovement.scale(0.99F)); + this.setDeltaMovement(movement.scale(0.99F)); this.applyGravity(); @@ -76,7 +_,7 @@ - protected void onHitBlock(BlockHitResult result) { - super.onHitBlock(result); + protected void onHitBlock(final BlockHitResult hitResult) { + super.onHitBlock(hitResult); if (!this.level().isClientSide()) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/Projectile.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/Projectile.java.patch index bbddee96ba82..ffa721197df7 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/Projectile.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/Projectile.java.patch @@ -6,102 +6,94 @@ private @Nullable Entity lastDeflectedBy; + protected boolean hitCancelled = false; // CraftBukkit - protected Projectile(EntityType type, Level level) { + protected Projectile(final EntityType type, final Level level) { super(type, level); -@@ -51,12 +_,25 @@ - - protected void setOwner(@Nullable EntityReference owner) { - this.owner = owner; -+ this.refreshProjectileSource(false); // Paper - } - - public void setOwner(@Nullable Entity owner) { - this.setOwner(EntityReference.of(owner)); - } - -+ // Paper start - Refresh ProjectileSource for projectiles -+ public void refreshProjectileSource(boolean fillCache) { -+ if (fillCache) { -+ this.getOwner(); -+ } -+ Entity owner = this.getOwner(); -+ if (owner != null && this.projectileSource == null && owner.getBukkitEntity() instanceof org.bukkit.projectiles.ProjectileSource projSource) { -+ this.projectileSource = projSource; -+ } -+ } -+ // Paper end - Refresh ProjectileSource for projectiles -+ - @Override - public @Nullable Entity getOwner() { - return EntityReference.getEntity(this.owner, this.level()); @@ -83,6 +_,7 @@ @Override - protected void readAdditionalSaveData(ValueInput input) { + protected void readAdditionalSaveData(final ValueInput input) { this.setOwner(EntityReference.read(input, "Owner")); + if (this instanceof net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownEnderpearl && this.level().paperConfig().fixes.disableUnloadedChunkEnderpearlExploit && this.level().paperConfig().misc.legacyEnderPearlBehavior) { this.owner = null; } // Paper - Reset pearls when they stop being ticked; Don't store shooter name for pearls to block enderpearl travel exploit this.leftOwner = input.getBooleanOr("LeftOwner", false); this.hasBeenShot = input.getBooleanOr("HasBeenShot", false); } @@ -155,7 +_,14 @@ - float f2 = Mth.cos(y * (float) (Math.PI / 180.0)) * Mth.cos(x * (float) (Math.PI / 180.0)); - this.shoot(f, f1, f2, velocity, inaccuracy); - Vec3 knownMovement = shooter.getKnownMovement(); + float zd = Mth.cos(yRot * Mth.DEG_TO_RAD) * Mth.cos(xRot * Mth.DEG_TO_RAD); + this.shoot(xd, yd, zd, pow, uncertainty); + Vec3 sourceMovement = source.getKnownMovement(); + // Paper start - allow disabling relative velocity -+ if (Double.isNaN(knownMovement.x) || Double.isNaN(knownMovement.y) || Double.isNaN(knownMovement.z)) { -+ knownMovement = new Vec3(0, 0, 0); ++ if (!sourceMovement.isFinite()) { ++ sourceMovement = Vec3.ZERO; + } -+ if (!shooter.level().paperConfig().misc.disableRelativeProjectileVelocity) { - this.setDeltaMovement(this.getDeltaMovement().add(knownMovement.x, shooter.onGround() ? 0.0 : knownMovement.y, knownMovement.z)); ++ if (!source.level().paperConfig().misc.disableRelativeProjectileVelocity) { + this.setDeltaMovement(this.getDeltaMovement().add(sourceMovement.x, source.onGround() ? 0.0 : sourceMovement.y, sourceMovement.z)); + } + // Paper end - allow disabling relative velocity } @Override -@@ -175,7 +_,12 @@ - public static T spawnProjectileFromRotation( - Projectile.ProjectileFactory factory, ServerLevel level, ItemStack spawnedFrom, LivingEntity owner, float z, float velocity, float inaccuracy +@@ -181,7 +_,20 @@ + final float pow, + final float uncertainty ) { - return spawnProjectile( + // Paper start - PlayerLaunchProjectileEvent -+ return spawnProjectileFromRotationDelayed(factory, level, spawnedFrom, owner, z, velocity, inaccuracy).spawn(); ++ return spawnProjectileFromRotationDelayed(creator, serverLevel, itemStack, source, yOffset, pow, uncertainty).spawn(); + } -+ public static Delayed spawnProjectileFromRotationDelayed(Projectile.ProjectileFactory factory, ServerLevel level, ItemStack spawnedFrom, LivingEntity owner, float z, float velocity, float inaccuracy) { ++ public static Delayed spawnProjectileFromRotationDelayed( ++ final Projectile.ProjectileFactory creator, ++ final ServerLevel serverLevel, ++ final ItemStack itemStack, ++ final LivingEntity source, ++ final float yOffset, ++ final float pow, ++ final float uncertainty ++ ) { + return spawnProjectileDelayed( + // Paper end - PlayerLaunchProjectileEvent - factory.create(level, owner, spawnedFrom), - level, - spawnedFrom, -@@ -200,7 +_,13 @@ - public static T spawnProjectileUsingShoot( - T projectile, ServerLevel level, ItemStack spawnedFrom, double x, double y, double z, float velocity, float inaccuracy + creator.create(serverLevel, source, itemStack), + serverLevel, + itemStack, +@@ -215,7 +_,22 @@ + final float pow, + final float uncertainty ) { -- return spawnProjectile(projectile, level, spawnedFrom, projectile1 -> projectile.shoot(x, y, z, velocity, inaccuracy)); +- return spawnProjectile(projectile, serverLevel, itemStack, i -> projectile.shoot(targetX, targetY, targetZ, pow, uncertainty)); + // Paper start - fixes and addition to spawn reason API -+ return spawnProjectileUsingShootDelayed(projectile, level, spawnedFrom, x, y, z, velocity, inaccuracy).spawn(); ++ return spawnProjectileUsingShootDelayed(projectile, serverLevel, itemStack, targetX, targetY, targetZ, pow, uncertainty).spawn(); + } + -+ public static Delayed spawnProjectileUsingShootDelayed(T projectile, ServerLevel level, ItemStack spawnedFrom, double x, double y, double z, float velocity, float inaccuracy) { -+ return spawnProjectileDelayed(projectile, level, spawnedFrom, projectile1 -> projectile.shoot(x, y, z, velocity, inaccuracy)); ++ public static Delayed spawnProjectileUsingShootDelayed( ++ final T projectile, ++ final ServerLevel serverLevel, ++ final ItemStack itemStack, ++ final double targetX, ++ final double targetY, ++ final double targetZ, ++ final float pow, ++ final float uncertainty ++ ) { ++ return spawnProjectileDelayed(projectile, serverLevel, itemStack, i -> projectile.shoot(targetX, targetY, targetZ, pow, uncertainty)); + // Paper end - fixes and addition to spawn reason API } - public static T spawnProjectile(T projectile, ServerLevel level, ItemStack spawnedFrom) { -@@ -208,11 +_,46 @@ - } - - public static T spawnProjectile(T projectile, ServerLevel level, ItemStack stack, Consumer adapter) { + public static T spawnProjectile(final T projectile, final ServerLevel serverLevel, final ItemStack itemStack) { +@@ -225,11 +_,46 @@ + public static T spawnProjectile( + final T projectile, final ServerLevel serverLevel, final ItemStack itemStack, final Consumer shootFunction + ) { +- shootFunction.accept(projectile); +- serverLevel.addFreshEntity(projectile); +- projectile.applyOnProjectileSpawned(serverLevel, itemStack); +- return projectile; +- } + // Paper start - delayed projectile spawning -+ return spawnProjectileDelayed(projectile, level, stack, adapter).spawn(); ++ return spawnProjectileDelayed(projectile, serverLevel, itemStack, shootFunction).spawn(); + } + -+ public static Delayed spawnProjectileDelayed(T projectile, ServerLevel level, ItemStack stack, Consumer adapter) { ++ public static Delayed spawnProjectileDelayed(T projectile, ServerLevel serverLevel, ItemStack itemStack, Consumer adapter) { + // Paper end - delayed projectile spawning - adapter.accept(projectile); -- level.addFreshEntity(projectile); -- projectile.applyOnProjectileSpawned(level, stack); -- return projectile; -- } -+ return new Delayed<>(projectile, level, stack); // Paper - delayed projectile spawning ++ adapter.accept(projectile); ++ return new Delayed<>(projectile, serverLevel, itemStack); // Paper - delayed projectile spawning + } + + // Paper start - delayed projectile spawning @@ -135,9 +127,9 @@ + } + // Paper end - delayed projectile spawning - public void applyOnProjectileSpawned(ServerLevel level, ItemStack spawnedFrom) { - EnchantmentHelper.onProjectileSpawned(level, spawnedFrom, this, item -> {}); -@@ -224,6 +_,17 @@ + public void applyOnProjectileSpawned(final ServerLevel serverLevel, final ItemStack pickupItemStack) { + EnchantmentHelper.onProjectileSpawned(serverLevel, pickupItemStack, this, item -> {}); +@@ -241,6 +_,17 @@ } } @@ -152,20 +144,20 @@ + } + // CraftBukkit end + - protected ProjectileDeflection hitTargetOrDeflectSelf(HitResult hitResult) { + protected ProjectileDeflection hitTargetOrDeflectSelf(final HitResult hitResult) { if (hitResult.getType() == HitResult.Type.ENTITY) { EntityHitResult entityHitResult = (EntityHitResult)hitResult; -@@ -291,15 +_,35 @@ +@@ -313,15 +_,35 @@ } - protected void onHitBlock(BlockHitResult result) { + protected void onHitBlock(final BlockHitResult hitResult) { + // CraftBukkit start - cancellable hit event + if (this.hitCancelled) { + return; + } + // CraftBukkit end - BlockState blockState = this.level().getBlockState(result.getBlockPos()); - blockState.onProjectileHit(this.level(), blockState, result, this); + BlockState state = this.level().getBlockState(hitResult.getBlockPos()); + state.onProjectileHit(this.level(), state, hitResult, this); } + // Paper start @@ -174,35 +166,35 @@ + } + // Paper end + - protected boolean canHitEntity(Entity target) { - if (!target.canBeHitByProjectile()) { + protected boolean canHitEntity(final Entity entity) { + if (!entity.canBeHitByProjectile()) { return false; } else { Entity owner = this.getOwner(); + // Paper start - Cancel hit for vanished entities + if (owner instanceof net.minecraft.server.level.ServerPlayer) { -+ org.bukkit.entity.Entity collided = target.getBukkitEntity(); ++ org.bukkit.entity.Entity collided = entity.getBukkitEntity(); + org.bukkit.entity.Player shooter = (org.bukkit.entity.Player) owner.getBukkitEntity(); + if (!shooter.canSee(collided)) { + return false; + } + } + // Paper end - Cancel hit for vanished entities - return owner == null || this.leftOwner || !owner.isPassengerOfSameVehicle(target); + return owner == null || this.leftOwner || !owner.isPassengerOfSameVehicle(entity); } } -@@ -312,13 +_,7 @@ +@@ -334,13 +_,7 @@ } - protected static float lerpRotation(float currentRotation, float targetRotation) { -- while (targetRotation - currentRotation < -180.0F) { -- currentRotation -= 360.0F; + protected static float lerpRotation(float rotO, final float rot) { +- while (rot - rotO < -180.0F) { +- rotO -= 360.0F; - } - -- while (targetRotation - currentRotation >= 180.0F) { -- currentRotation += 360.0F; +- while (rot - rotO >= 180.0F) { +- rotO += 360.0F; - } -+ currentRotation += Math.round((targetRotation - currentRotation) / 360.0F) * 360.0F; // Paper - stop large look changes from crashing the server ++ rotO += Math.round((rot - rotO) / 360.0F) * 360.0F; // Paper - stop large look changes from crashing the server - return Mth.lerp(0.2F, currentRotation, targetRotation); + return Mth.lerp(0.2F, rotO, rot); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/ShulkerBullet.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/ShulkerBullet.java.patch index c7c063146a24..a60b569a11de 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/ShulkerBullet.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/ShulkerBullet.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/projectile/ShulkerBullet.java +++ b/net/minecraft/world/entity/projectile/ShulkerBullet.java @@ -55,7 +_,21 @@ - this.finalTarget = EntityReference.of(finalTarget); + this.finalTarget = EntityReference.of(target); this.currentMoveDirection = Direction.UP; - this.selectNextMoveDirection(axis, finalTarget); + this.selectNextMoveDirection(invalidStartAxis, target); - } -+ this.projectileSource = shooter.getBukkitLivingEntity(); // CraftBukkit ++ this.projectileSource = owner.getBukkitLivingEntity(); // CraftBukkit + } + + // CraftBukkit start @@ -32,7 +32,7 @@ } } -@@ -220,7 +_,7 @@ +@@ -218,7 +_,7 @@ } if (hitResult != null && this.isAlive() && hitResult.getType() != HitResult.Type.MISS) { @@ -41,16 +41,16 @@ } ProjectileUtil.rotateTowardsMovement(this, 0.5F); -@@ -293,7 +_,7 @@ +@@ -288,7 +_,7 @@ } - if (entity instanceof LivingEntity livingEntity1) { -- livingEntity1.addEffect(new MobEffectInstance(MobEffects.LEVITATION, 200), MoreObjects.firstNonNull(owner, this)); -+ livingEntity1.addEffect(new MobEffectInstance(MobEffects.LEVITATION, 200), MoreObjects.firstNonNull(owner, this), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + if (target instanceof LivingEntity livingTarget) { +- livingTarget.addEffect(new MobEffectInstance(MobEffects.LEVITATION, 200), MoreObjects.firstNonNull(owner, this)); ++ livingTarget.addEffect(new MobEffectInstance(MobEffects.LEVITATION, 200), MoreObjects.firstNonNull(owner, this), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit } } } -@@ -306,14 +_,20 @@ +@@ -301,14 +_,20 @@ } private void destroy() { @@ -66,19 +66,19 @@ } @Override - protected void onHit(HitResult result) { - super.onHit(result); + protected void onHit(final HitResult hitResult) { + super.onHit(hitResult); - this.destroy(); + this.destroy(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause } @Override -@@ -328,9 +_,14 @@ +@@ -323,9 +_,14 @@ @Override - public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { + public boolean hurtServer(final ServerLevel level, final DamageSource source, final float damage) { + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damageSource, amount, false)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, damage, false)) { + return false; + } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrowableProjectile.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrowableProjectile.java.patch index c46b5f9bdd1f..afa84e5943d2 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrowableProjectile.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrowableProjectile.java.patch @@ -3,9 +3,9 @@ @@ -60,7 +_,7 @@ this.applyEffectsFromBlocks(); super.tick(); - if (hitResultOnMoveVector.getType() != HitResult.Type.MISS && this.isAlive()) { -- this.hitTargetOrDeflectSelf(hitResultOnMoveVector); -+ this.preHitTargetOrDeflectSelf(hitResultOnMoveVector); // CraftBukkit - projectile hit event + if (result.getType() != HitResult.Type.MISS && this.isAlive()) { +- this.hitTargetOrDeflectSelf(result); ++ this.preHitTargetOrDeflectSelf(result); // CraftBukkit - projectile hit event } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java.patch index bb72fda9f12a..61080a99da0f 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java +++ b/net/minecraft/world/entity/projectile/arrow/AbstractArrow.java -@@ -86,7 +_,14 @@ - protected AbstractArrow( - EntityType type, double x, double y, double z, Level level, ItemStack pickupItemStack, @Nullable ItemStack firedFromWeapon +@@ -92,7 +_,14 @@ + final ItemStack pickupItemStack, + final @Nullable ItemStack firedFromWeapon ) { + // CraftBukkit start - handle the owner before the rest of things + this(type, x, y, z, level, pickupItemStack, firedFromWeapon, null); @@ -14,21 +14,21 @@ + // CraftBukkit end this.pickupItemStack = pickupItemStack.copy(); this.applyComponentsFromItemStack(pickupItemStack); - Unit unit = pickupItemStack.remove(DataComponents.INTANGIBLE_PROJECTILE); -@@ -111,8 +_,8 @@ - protected AbstractArrow( - EntityType type, LivingEntity owner, Level level, ItemStack pickupItemStack, @Nullable ItemStack firedFromWeapon + Unit intangible = pickupItemStack.remove(DataComponents.INTANGIBLE_PROJECTILE); +@@ -121,8 +_,8 @@ + final ItemStack pickupItemStack, + final @Nullable ItemStack firedFromWeapon ) { -- this(type, owner.getX(), owner.getEyeY() - 0.1F, owner.getZ(), level, pickupItemStack, firedFromWeapon); -- this.setOwner(owner); -+ this(type, owner.getX(), owner.getEyeY() - 0.1F, owner.getZ(), level, pickupItemStack, firedFromWeapon, owner); // CraftBukkit -+ // this.setOwner(owner); // SPIGOT-7744 - Moved to the above constructor +- this(type, mob.getX(), mob.getEyeY() - 0.1F, mob.getZ(), level, pickupItemStack, firedFromWeapon); +- this.setOwner(mob); ++ this(type, mob.getX(), mob.getEyeY() - 0.1F, mob.getZ(), level, pickupItemStack, firedFromWeapon, mob); // CraftBukkit ++ // this.setOwner(mob); // SPIGOT-7744 - Moved to the above constructor } - public void setSoundEvent(SoundEvent soundEvent) { -@@ -146,7 +_,11 @@ + public void setSoundEvent(final SoundEvent soundEvent) { +@@ -156,7 +_,11 @@ @Override - public void lerpMotion(Vec3 movement) { + public void lerpMotion(final Vec3 movement) { super.lerpMotion(movement); - this.life = 0; + // Paper start - Fix loophole for arrow immunity @@ -39,7 +39,7 @@ if (this.isInGround() && movement.lengthSqr() > 0.0) { this.setInGround(false); } -@@ -207,6 +_,10 @@ +@@ -217,6 +_,10 @@ this.setSharedFlagOnFire(this.getRemainingFireTicks() > 0); } } else { @@ -48,27 +48,27 @@ + if (maxArrowDespawnInvulnerability.enabled() && this.tickCount > maxArrowDespawnInvulnerability.intValue()) this.tickDespawn(); + // Paper end - tick life regardless after X seconds this.inGroundTime = 0; - Vec3 vec31 = this.position(); + Vec3 originalPosition = this.position(); if (this.isInWater()) { -@@ -276,7 +_,7 @@ +@@ -288,7 +_,7 @@ - if (list.isEmpty()) { - if (this.isAlive() && hitResult.getType() != HitResult.Type.MISS) { -- this.hitTargetOrDeflectSelf(hitResult); -+ this.preHitTargetOrDeflectSelf(hitResult); // CraftBukkit - projectile hit event + if (entitiesHit.isEmpty()) { + if (this.isAlive() && blockHitResult.getType() != HitResult.Type.MISS) { +- this.hitTargetOrDeflectSelf(blockHitResult); ++ this.preHitTargetOrDeflectSelf(blockHitResult); // CraftBukkit - projectile hit event this.needsSync = true; } break; -@@ -293,7 +_,7 @@ - - private ProjectileDeflection hitTargetsOrDeflectSelf(Collection hitResults) { - for (EntityHitResult entityHitResult : hitResults) { -- ProjectileDeflection projectileDeflection = this.hitTargetOrDeflectSelf(entityHitResult); -+ ProjectileDeflection projectileDeflection = this.preHitTargetOrDeflectSelf(entityHitResult); // CraftBukkit - projectile hit event - if (!this.isAlive() || projectileDeflection != ProjectileDeflection.NONE) { - return projectileDeflection; +@@ -305,7 +_,7 @@ + + private ProjectileDeflection hitTargetsOrDeflectSelf(final Collection entityHitResults) { + for (EntityHitResult e : entityHitResults) { +- ProjectileDeflection deflection = this.hitTargetOrDeflectSelf(e); ++ ProjectileDeflection deflection = this.preHitTargetOrDeflectSelf(e); // CraftBukkit - projectile hit event + if (!this.isAlive() || deflection != ProjectileDeflection.NONE) { + return deflection; } -@@ -325,20 +_,37 @@ +@@ -337,20 +_,37 @@ } } @@ -108,7 +108,7 @@ } public boolean isInGround() { -@@ -364,8 +_,8 @@ +@@ -376,8 +_,8 @@ protected void tickDespawn() { this.life++; @@ -119,19 +119,19 @@ } } -@@ -399,9 +_,9 @@ +@@ -411,9 +_,9 @@ } @Override -- public void push(double x, double y, double z) { -+ public void push(double x, double y, double z, @Nullable Entity pushingEntity) { // Paper - add push source entity param +- public void push(final double xa, final double ya, final double za) { ++ public void push(final double xa, final double ya, final double za, final @Nullable Entity pushingEntity) { // Paper - add push source entity param if (!this.isInGround()) { -- super.push(x, y, z); -+ super.push(x, y, z, pushingEntity); // Paper - add push source entity param +- super.push(xa, ya, za); ++ super.push(xa, ya, za, pushingEntity); // Paper - add push source entity param } } -@@ -428,7 +_,7 @@ +@@ -440,7 +_,7 @@ } if (this.piercingIgnoreEntityIds.size() >= this.getPierceLevel() + 1) { @@ -140,14 +140,14 @@ return; } -@@ -444,10 +_,16 @@ - livingEntity.setLastHurtMob(entity); +@@ -456,10 +_,16 @@ + livingOwner.setLastHurtMob(entity); } + if (this.isCritArrow()) damageSource = damageSource.critical(); // Paper - add critical damage API - boolean flag = entity.getType() == EntityType.ENDERMAN; + boolean isEnderman = entity.is(EntityType.ENDERMAN); int remainingFireTicks = entity.getRemainingFireTicks(); - if (this.isOnFire() && !flag) { + if (this.isOnFire() && !isEnderman) { - entity.igniteForSeconds(5.0F); + // CraftBukkit start + org.bukkit.event.entity.EntityCombustByEntityEvent combustEvent = new org.bukkit.event.entity.EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 5.0F); @@ -157,8 +157,8 @@ + // CraftBukkit end } - if (entity.hurtOrSimulate(damageSource, ceil)) { -@@ -485,7 +_,7 @@ + if (entity.hurtOrSimulate(damageSource, damage)) { +@@ -497,7 +_,7 @@ this.playSound(this.soundEvent, 1.0F, 1.2F / (this.random.nextFloat() * 0.2F + 0.9F)); if (this.getPierceLevel() <= 0) { @@ -167,8 +167,8 @@ } } else { entity.setRemainingFireTicks(remainingFireTicks); -@@ -496,7 +_,7 @@ - this.spawnAtLocation(serverLevel2, this.getPickupItem(), 0.1F); +@@ -508,7 +_,7 @@ + this.spawnAtLocation(level, this.getPickupItem(), 0.1F); } - this.discard(); @@ -176,40 +176,40 @@ } } } -@@ -509,7 +_,7 @@ - double max = Math.max(0.0, 1.0 - entity.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE)); - Vec3 vec3 = this.getDeltaMovement().multiply(1.0, 0.0, 1.0).normalize().scale(d * 0.6 * max); - if (vec3.lengthSqr() > 0.0) { -- entity.push(vec3.x, 0.1, vec3.z); -+ entity.push(vec3.x, 0.1, vec3.z, this); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent +@@ -521,7 +_,7 @@ + double knockbackResistance = Math.max(0.0, 1.0 - mob.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE)); + Vec3 movement = this.getDeltaMovement().multiply(1.0, 0.0, 1.0).normalize().scale(knockback * 0.6 * knockbackResistance); + if (movement.lengthSqr() > 0.0) { +- mob.push(movement.x, 0.1, movement.z); ++ mob.push(movement.x, 0.1, movement.z, this); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent } } } -@@ -620,7 +_,14 @@ +@@ -632,7 +_,14 @@ @Override - public void setOwner(@Nullable Entity entity) { + public void setOwner(final @Nullable Entity owner) { + // Paper start - Fix PickupStatus getting reset -+ this.setOwner(entity, true); ++ this.setOwner(owner, true); + } + -+ public void setOwner(@Nullable Entity entity, boolean resetPickup) { ++ public void setOwner(final @Nullable Entity owner, final boolean resetPickup) { + // Paper end - Fix PickupStatus getting reset - super.setOwner(entity); + super.setOwner(owner); + if (!resetPickup) return; // Paper - Fix PickupStatus getting reset - this.pickup = switch (entity) { - case Player player when this.pickup == AbstractArrow.Pickup.DISALLOWED -> AbstractArrow.Pickup.ALLOWED; -@@ -632,9 +_,22 @@ + this.pickup = switch (owner) { + case Player ignored when this.pickup == AbstractArrow.Pickup.DISALLOWED -> AbstractArrow.Pickup.ALLOWED; +@@ -644,9 +_,22 @@ @Override - public void playerTouch(Player entity) { + public void playerTouch(final Player player) { if (!this.level().isClientSide() && (this.isInGround() || this.isNoPhysics()) && this.shakeTime <= 0) { -- if (this.tryPickup(entity)) { +- if (this.tryPickup(player)) { + // CraftBukkit start + ItemStack itemstack = this.getPickupItem(); -+ if (this.pickup == Pickup.ALLOWED && !itemstack.isEmpty() && entity.getInventory().canHold(itemstack) > 0) { ++ if (this.pickup == Pickup.ALLOWED && !itemstack.isEmpty() && player.getInventory().canHold(itemstack) > 0) { + net.minecraft.world.entity.item.ItemEntity item = new net.minecraft.world.entity.item.ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemstack); -+ org.bukkit.event.player.PlayerPickupArrowEvent event = new org.bukkit.event.player.PlayerPickupArrowEvent((org.bukkit.entity.Player) entity.getBukkitEntity(), (org.bukkit.entity.Item) item.getBukkitEntity(), (org.bukkit.entity.AbstractArrow) this.getBukkitEntity()); ++ org.bukkit.event.player.PlayerPickupArrowEvent event = new org.bukkit.event.player.PlayerPickupArrowEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.Item) item.getBukkitEntity(), (org.bukkit.entity.AbstractArrow) this.getBukkitEntity()); + // event.setCancelled(!entityhuman.canPickUpLoot); TODO + if (!event.callEvent()) { + return; @@ -217,9 +217,9 @@ + itemstack = item.getItem(); + } + -+ if ((this.pickup == AbstractArrow.Pickup.ALLOWED && entity.getInventory().add(itemstack)) || (this.pickup == AbstractArrow.Pickup.CREATIVE_ONLY && entity.getAbilities().instabuild)) { ++ if ((this.pickup == AbstractArrow.Pickup.ALLOWED && player.getInventory().add(itemstack)) || (this.pickup == AbstractArrow.Pickup.CREATIVE_ONLY && player.getAbilities().instabuild)) { + // CraftBukkit end - entity.take(this, 1); + player.take(this, 1); - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/Arrow.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/Arrow.java.patch index 55d81a23186f..e1c73cb88a51 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/Arrow.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/Arrow.java.patch @@ -3,9 +3,9 @@ @@ -115,7 +_,7 @@ Entity effectSource = this.getEffectSource(); PotionContents potionContents = this.getPotionContents(); - float potionDurationScale = this.getPotionDurationScale(); -- potionContents.forEachEffect(mobEffectInstance -> target.addEffect(mobEffectInstance, effectSource), potionDurationScale); -+ potionContents.forEachEffect(mobEffectInstance -> target.addEffect(mobEffectInstance, effectSource, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ARROW), potionDurationScale); // CraftBukkit + float durationScale = this.getPotionDurationScale(); +- potionContents.forEachEffect(effect -> mob.addEffect(effect, effectSource), durationScale); ++ potionContents.forEachEffect(effect -> mob.addEffect(effect, effectSource, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ARROW), durationScale); // CraftBukkit } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/SpectralArrow.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/SpectralArrow.java.patch index a44b58670aa4..706a0a310a9b 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/SpectralArrow.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/SpectralArrow.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/projectile/arrow/SpectralArrow.java +++ b/net/minecraft/world/entity/projectile/arrow/SpectralArrow.java -@@ -41,7 +_,7 @@ - protected void doPostHurtEffects(LivingEntity target) { - super.doPostHurtEffects(target); - MobEffectInstance mobEffectInstance = new MobEffectInstance(MobEffects.GLOWING, this.duration, 0); -- target.addEffect(mobEffectInstance, this.getEffectSource()); -+ target.addEffect(mobEffectInstance, this.getEffectSource(), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ARROW); // CraftBukkit +@@ -43,7 +_,7 @@ + protected void doPostHurtEffects(final LivingEntity mob) { + super.doPostHurtEffects(mob); + MobEffectInstance effect = new MobEffectInstance(MobEffects.GLOWING, this.duration, 0); +- mob.addEffect(effect, this.getEffectSource()); ++ mob.addEffect(effect, this.getEffectSource(), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ARROW); // CraftBukkit } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/ThrownTrident.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/ThrownTrident.java.patch index 0e745702adcb..684f6afb5484 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/ThrownTrident.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/arrow/ThrownTrident.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/entity/projectile/arrow/ThrownTrident.java +++ b/net/minecraft/world/entity/projectile/arrow/ThrownTrident.java @@ -51,6 +_,12 @@ - this.entityData.set(ID_FOIL, pickupItemStack.hasFoil()); + this.entityData.set(ID_FOIL, tridentItem.hasFoil()); } + // Paper start - Allow trident custom damage @@ -11,16 +11,16 @@ + // Paper end - Allow trident custom damage + @Override - protected void defineSynchedData(SynchedEntityData.Builder builder) { - super.defineSynchedData(builder); + protected void defineSynchedData(final SynchedEntityData.Builder entityData) { + super.defineSynchedData(entityData); @@ -72,10 +_,10 @@ - this.spawnAtLocation(serverLevel, this.getPickupItem(), 0.1F); + this.spawnAtLocation(level, this.getPickupItem(), 0.1F); } - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause } else { - if (!(owner instanceof Player) && this.position().distanceTo(owner.getEyePosition()) < owner.getBbWidth() + 1.0) { + if (!(currentOwner instanceof Player) && this.position().distanceTo(currentOwner.getEyePosition()) < currentOwner.getBbWidth() + 1.0) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause return; @@ -45,14 +45,14 @@ + // Paper end + @Override - protected @Nullable EntityHitResult findHitEntity(Vec3 startVec, Vec3 endVec) { - return this.dealtDamage ? null : super.findHitEntity(startVec, endVec); + protected @Nullable EntityHitResult findHitEntity(final Vec3 from, final Vec3 to) { + return this.dealtDamage ? null : super.findHitEntity(from, to); @@ -118,7 +_,7 @@ @Override - protected void onHitEntity(EntityHitResult result) { - Entity entity = result.getEntity(); -- float f = 8.0F; -+ float f = (float) this.baseDamage; // Paper - Allow trident custom damage - Entity owner = this.getOwner(); - DamageSource damageSource = this.damageSources().trident(this, (Entity)(owner == null ? this : owner)); + protected void onHitEntity(final EntityHitResult hitResult) { + Entity entity = hitResult.getEntity(); +- float dmg = 8.0F; ++ float dmg = (float) this.baseDamage; // Paper - Allow trident custom damage + Entity currentOwner = this.getOwner(); + DamageSource damageSource = this.damageSources().trident(this, (Entity)(currentOwner == null ? this : currentOwner)); if (this.level() instanceof ServerLevel serverLevel) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectile.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectile.java.patch index 9eb8af597e53..9f59be1d928a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectile.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/AbstractHurtingProjectile.java.patch @@ -7,14 +7,14 @@ + public float bukkitYield = 1; // CraftBukkit + public boolean isIncendiary = true; // CraftBukkit - protected AbstractHurtingProjectile(EntityType type, Level level) { + protected AbstractHurtingProjectile(final EntityType type, final Level level) { super(type, level); -@@ -86,12 +_,12 @@ +@@ -92,12 +_,12 @@ } - if (hitResultOnMoveVector.getType() != HitResult.Type.MISS && this.isAlive()) { -- this.hitTargetOrDeflectSelf(hitResultOnMoveVector); -+ this.preHitTargetOrDeflectSelf(hitResultOnMoveVector); // CraftBukkit - projectile hit event + if (hitResult.getType() != HitResult.Type.MISS && this.isAlive()) { +- this.hitTargetOrDeflectSelf(hitResult); ++ this.preHitTargetOrDeflectSelf(hitResult); // CraftBukkit - projectile hit event } this.createParticleTrail(); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/DragonFireball.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/DragonFireball.java.patch index 0f3dffb6cd05..e13954640c9b 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/DragonFireball.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/DragonFireball.java.patch @@ -4,12 +4,12 @@ } } -+ if (new com.destroystokyo.paper.event.entity.EnderDragonFireballHitEvent((org.bukkit.entity.DragonFireball) this.getBukkitEntity(), entitiesOfClass.stream().map(LivingEntity::getBukkitLivingEntity).collect(java.util.stream.Collectors.toList()), (org.bukkit.entity.AreaEffectCloud) areaEffectCloud.getBukkitEntity()).callEvent()) { // Paper - EnderDragon Events ++ if (new com.destroystokyo.paper.event.entity.EnderDragonFireballHitEvent((org.bukkit.entity.DragonFireball) this.getBukkitEntity(), entitiesOfClass.stream().map(LivingEntity::getBukkitLivingEntity).collect(java.util.stream.Collectors.toList()), (org.bukkit.entity.AreaEffectCloud) cloud.getBukkitEntity()).callEvent()) { // Paper - EnderDragon Events this.level().levelEvent(LevelEvent.PARTICLES_DRAGON_FIREBALL_SPLASH, this.blockPosition(), this.isSilent() ? -1 : 1); -- this.level().addFreshEntity(areaEffectCloud); +- this.level().addFreshEntity(cloud); - this.discard(); -+ this.level().addFreshEntity(areaEffectCloud, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EXPLOSION); // Paper - use correct spawn reason -+ } else areaEffectCloud.discard(null); // Paper - EnderDragon Events ++ this.level().addFreshEntity(cloud, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EXPLOSION); // Paper - use correct spawn reason ++ } else cloud.discard(null); // Paper - EnderDragon Events + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/LargeFireball.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/LargeFireball.java.patch index 68379e517009..084ee5d2c013 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/LargeFireball.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/LargeFireball.java.patch @@ -2,25 +2,25 @@ +++ b/net/minecraft/world/entity/projectile/hurtingprojectile/LargeFireball.java @@ -20,20 +_,27 @@ - public LargeFireball(EntityType type, Level level) { + public LargeFireball(final EntityType type, final Level level) { super(type, level); + this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING); // CraftBukkit } - public LargeFireball(Level level, LivingEntity owner, Vec3 movement, int explosionPower) { - super(EntityType.FIREBALL, owner, movement, level); + public LargeFireball(final Level level, final LivingEntity mob, final Vec3 direction, final int explosionPower) { + super(EntityType.FIREBALL, mob, direction, level); this.explosionPower = explosionPower; + this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING); // CraftBukkit } @Override - protected void onHit(HitResult result) { - super.onHit(result); + protected void onHit(final HitResult hitResult) { + super.onHit(hitResult); if (this.level() instanceof ServerLevel serverLevel) { -- boolean flag = serverLevel.getGameRules().get(GameRules.MOB_GRIEFING); -- this.level().explode(this, this.getX(), this.getY(), this.getZ(), this.explosionPower, flag, Level.ExplosionInteraction.MOB); +- boolean grief = serverLevel.getGameRules().get(GameRules.MOB_GRIEFING); +- this.level().explode(this, this.getX(), this.getY(), this.getZ(), this.explosionPower, grief, Level.ExplosionInteraction.MOB); - this.discard(); -+ // boolean flag = serverLevel.getGameRules().get(GameRules.MOB_GRIEFING); // CraftBukkit - baked into fields (see constructor) ++ // boolean grief = serverLevel.getGameRules().get(GameRules.MOB_GRIEFING); // CraftBukkit - baked into fields (see constructor) + // CraftBukkit start - fire ExplosionPrimeEvent + org.bukkit.event.entity.ExplosionPrimeEvent event = new org.bukkit.event.entity.ExplosionPrimeEvent((org.bukkit.entity.Explosive) this.getBukkitEntity()); + if (event.callEvent()) { @@ -33,7 +33,7 @@ @@ -58,6 +_,6 @@ @Override - protected void readAdditionalSaveData(ValueInput input) { + protected void readAdditionalSaveData(final ValueInput input) { super.readAdditionalSaveData(input); - this.explosionPower = input.getByteOr("ExplosionPower", (byte)1); + this.bukkitYield = this.explosionPower = input.getByteOr("ExplosionPower", (byte)1); // CraftBukkit - set bukkitYield when setting explosionPower diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/SmallFireball.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/SmallFireball.java.patch index 59a16e4ae357..0d214aca2064 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/SmallFireball.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/SmallFireball.java.patch @@ -2,8 +2,8 @@ +++ b/net/minecraft/world/entity/projectile/hurtingprojectile/SmallFireball.java @@ -23,6 +_,11 @@ - public SmallFireball(Level level, LivingEntity owner, Vec3 movement) { - super(EntityType.SMALL_FIREBALL, owner, movement, level); + public SmallFireball(final Level level, final LivingEntity mob, final Vec3 direction) { + super(EntityType.SMALL_FIREBALL, mob, direction, level); + // CraftBukkit start + if (this.getOwner() != null && this.getOwner() instanceof Mob) { + this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING); @@ -11,9 +11,9 @@ + // CraftBukkit end } - public SmallFireball(Level level, double x, double y, double z, Vec3 movement) { + public SmallFireball(final Level level, final double x, final double y, final double z, final Vec3 direction) { @@ -36,7 +_,14 @@ - Entity var7 = result.getEntity(); + Entity var7 = hitResult.getEntity(); Entity owner = this.getOwner(); int remainingFireTicks = var7.getRemainingFireTicks(); - var7.igniteForSeconds(5.0F); @@ -29,20 +29,20 @@ if (!var7.hurtServer(serverLevel, damageSource, 5.0F)) { var7.setRemainingFireTicks(remainingFireTicks); @@ -51,9 +_,9 @@ - super.onHitBlock(result); + super.onHitBlock(hitResult); if (this.level() instanceof ServerLevel serverLevel) { Entity owner = this.getOwner(); - if (!(owner instanceof Mob) || serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) { + if (this.isIncendiary) { // CraftBukkit - BlockPos blockPos = result.getBlockPos().relative(result.getDirection()); -- if (this.level().isEmptyBlock(blockPos)) { -+ if (this.level().isEmptyBlock(blockPos) && !org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level(), blockPos, this).isCancelled()) { // CraftBukkit - this.level().setBlockAndUpdate(blockPos, BaseFireBlock.getState(this.level(), blockPos)); + BlockPos pos = hitResult.getBlockPos().relative(hitResult.getDirection()); +- if (this.level().isEmptyBlock(pos)) { ++ if (this.level().isEmptyBlock(pos) && !org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level(), pos, this).isCancelled()) { // CraftBukkit + this.level().setBlockAndUpdate(pos, BaseFireBlock.getState(this.level(), pos)); } } @@ -64,7 +_,7 @@ - protected void onHit(HitResult result) { - super.onHit(result); + protected void onHit(final HitResult hitResult) { + super.onHit(hitResult); if (!this.level().isClientSide()) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java.patch index 427e628f7980..ee9ee10a94b3 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/WitherSkull.java.patch @@ -4,28 +4,28 @@ if (var8.isAlive()) { EnchantmentHelper.doPostAttackEffects(serverLevel, var8, damageSource); } else { -- livingEntity.heal(5.0F); -+ livingEntity.heal(5.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.WITHER); // CraftBukkit +- livingOwner.heal(5.0F); ++ livingOwner.heal(5.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.WITHER); // CraftBukkit } } } else { -- flag = var8.hurtServer(serverLevel, this.damageSources().magic(), 5.0F); -+ flag = var8.hurtServer(serverLevel, this.damageSources().magic().eventEntityDamager(this), 5.0F); // Paper - Fire EntityDamageByEntityEvent for unowned wither skulls // Paper - fix DamageSource API +- wasHurt = var8.hurtServer(serverLevel, this.damageSources().magic(), 5.0F); ++ wasHurt = var8.hurtServer(serverLevel, this.damageSources().magic().eventEntityDamager(this), 5.0F); // Paper - Fire EntityDamageByEntityEvent for unowned wither skulls // Paper - fix DamageSource API } - if (flag && var8 instanceof LivingEntity livingEntityx) { + if (wasHurt && var8 instanceof LivingEntity livingEntity) { @@ -83,7 +_,7 @@ } - if (i > 0) { -- livingEntityx.addEffect(new MobEffectInstance(MobEffects.WITHER, 20 * i, 1), this.getEffectSource()); -+ livingEntityx.addEffect(new MobEffectInstance(MobEffects.WITHER, 20 * i, 1), this.getEffectSource(), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + if (witherSeconds > 0) { +- livingEntity.addEffect(new MobEffectInstance(MobEffects.WITHER, 20 * witherSeconds, 1), this.getEffectSource()); ++ livingEntity.addEffect(new MobEffectInstance(MobEffects.WITHER, 20 * witherSeconds, 1), this.getEffectSource(), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit } } } @@ -93,8 +_,13 @@ - protected void onHit(HitResult result) { - super.onHit(result); + protected void onHit(final HitResult hitResult) { + super.onHit(hitResult); if (!this.level().isClientSide()) { - this.level().explode(this, this.getX(), this.getY(), this.getZ(), 1.0F, false, Level.ExplosionInteraction.MOB); - this.discard(); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/AbstractWindCharge.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/AbstractWindCharge.java.patch index 503b3602f153..8b49c138e55b 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/AbstractWindCharge.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/AbstractWindCharge.java.patch @@ -1,33 +1,33 @@ --- a/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/AbstractWindCharge.java +++ b/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/AbstractWindCharge.java -@@ -85,7 +_,7 @@ +@@ -89,7 +_,7 @@ } @Override -- public void push(double x, double y, double z) { -+ public void push(double x, double y, double z, @Nullable Entity pushingEntity) { // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent +- public void push(final double xa, final double ya, final double za) { ++ public void push(final double xa, final double ya, final double za, @Nullable Entity pushingEntity) { // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent } - public abstract void explode(Vec3 pos); -@@ -98,7 +_,7 @@ - Vec3 vec3 = Vec3.atLowerCornerOf(unitVec3i).multiply(0.25, 0.25, 0.25); - Vec3 vec31 = result.getLocation().add(vec3); - this.explode(vec31); + public abstract void explode(final Vec3 position); +@@ -102,7 +_,7 @@ + Vec3 scaledNormal = Vec3.atLowerCornerOf(collisionNormal).multiply(0.25, 0.25, 0.25); + Vec3 explosionPos = hitResult.getLocation().add(scaledNormal); + this.explode(explosionPos); - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause } } -@@ -106,7 +_,7 @@ - protected void onHit(HitResult result) { - super.onHit(result); +@@ -110,7 +_,7 @@ + protected void onHit(final HitResult hitResult) { + super.onHit(hitResult); if (!this.level().isClientSide()) { - this.discard(); + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause } } -@@ -139,7 +_,7 @@ +@@ -143,7 +_,7 @@ public void tick() { if (!this.level().isClientSide() && this.getBlockY() > this.level().getMaxY() + 30) { this.explode(this.position()); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/BreezeWindCharge.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/BreezeWindCharge.java.patch index c2905d1cb136..1cc4e7797769 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/BreezeWindCharge.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/BreezeWindCharge.java.patch @@ -3,7 +3,7 @@ @@ -21,6 +_,12 @@ @Override - public void explode(Vec3 pos) { + public void explode(final Vec3 position) { + // Paper start - Fire event for WindCharge explosions + org.bukkit.event.entity.ExplosionPrimeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExplosionPrimeEvent(this, RADIUS, false); + if (event.isCancelled()) { @@ -14,9 +14,9 @@ .explode( this, @@ -29,8 +_,8 @@ - pos.x(), - pos.y(), - pos.z(), + position.x(), + position.y(), + position.z(), - 3.0F, - false, + event.getRadius(), // Paper - Fire event for WindCharge explosions diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/WindCharge.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/WindCharge.java.patch index a4a45fc9312a..9248bff35d6a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/WindCharge.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/WindCharge.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/WindCharge.java +++ b/net/minecraft/world/entity/projectile/hurtingprojectile/windcharge/WindCharge.java -@@ -54,6 +_,12 @@ +@@ -59,6 +_,12 @@ @Override - public void explode(Vec3 pos) { + public void explode(final Vec3 position) { + // Paper start - Fire event for WindCharge explosions + org.bukkit.event.entity.ExplosionPrimeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExplosionPrimeEvent(this, RADIUS, false); + if (event.isCancelled()) { @@ -13,10 +13,10 @@ this.level() .explode( this, -@@ -62,8 +_,8 @@ - pos.x(), - pos.y(), - pos.z(), +@@ -67,8 +_,8 @@ + position.x(), + position.y(), + position.z(), - 1.2F, - false, + event.getRadius(), // Paper - Fire event for WindCharge explosions diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/AbstractThrownPotion.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/AbstractThrownPotion.java.patch index 69d4e6502d60..d6ec47d1482a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/AbstractThrownPotion.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/AbstractThrownPotion.java.patch @@ -1,65 +1,65 @@ --- a/net/minecraft/world/entity/projectile/throwableitemprojectile/AbstractThrownPotion.java +++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/AbstractThrownPotion.java -@@ -68,56 +_,97 @@ +@@ -70,56 +_,97 @@ @Override - protected void onHit(HitResult result) { - super.onHit(result); + protected void onHit(final HitResult hitResult) { + super.onHit(hitResult); + // Paper start - More projectile API -+ this.splash(result); ++ this.splash(hitResult); + } + -+ public void splash(HitResult result) { ++ public void splash(final HitResult hitResult) { + // Paper end - More projectile API - if (this.level() instanceof ServerLevel serverLevel) { - ItemStack item = this.getItem(); - PotionContents potionContents = item.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); + if (this.level() instanceof ServerLevel level) { + ItemStack potionItemStack = this.getItem(); + PotionContents potion = potionItemStack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); + boolean showParticles = true; // Paper - Fix potions splash events - if (potionContents.is(Potions.WATER)) { -- this.onHitAsWater(serverLevel); -- } else if (potionContents.hasEffects()) { -- this.onHitAsPotion(serverLevel, item, result); -+ showParticles = this.onHitAsWater(serverLevel, result); // Paper - Fix potions splash events -+ } else if (true || potionContents.hasEffects()) { // CraftBukkit - Call event even if no effects to apply -+ showParticles = this.onHitAsPotion(serverLevel, item, result); // Paper - pass HitResult + if (potion.is(Potions.WATER)) { +- this.onHitAsWater(level); +- } else if (potion.hasEffects()) { +- this.onHitAsPotion(level, potionItemStack, hitResult); ++ showParticles = this.onHitAsWater(level, hitResult); // Paper - Fix potions splash events ++ } else if (true || potion.hasEffects()) { // CraftBukkit - Call event even if no effects to apply ++ showParticles = this.onHitAsPotion(level, potionItemStack, hitResult); // Paper - pass HitResult } + if (showParticles) { // Paper - Fix potions splash events - int i = potionContents.potion().isPresent() && potionContents.potion().get().value().hasInstantEffects() + int type = potion.potion().isPresent() && potion.potion().get().value().hasInstantEffects() ? LevelEvent.PARTICLES_INSTANT_POTION_SPLASH : LevelEvent.PARTICLES_SPELL_POTION_SPLASH; - serverLevel.levelEvent(i, this.blockPosition(), potionContents.getColor()); + level.levelEvent(type, this.blockPosition(), potion.getColor()); - this.discard(); + } // Paper - Fix potions splash events + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause } } -- private void onHitAsWater(ServerLevel level) { +- private void onHitAsWater(final ServerLevel level) { + private static final Predicate APPLY_WATER_GET_ENTITIES_PREDICATE = AbstractThrownPotion.WATER_SENSITIVE_OR_ON_FIRE.or(Axolotl.class::isInstance); // Paper - Fix potions splash events + -+ private boolean onHitAsWater(ServerLevel level, HitResult result) { // Paper - Fix potions splash events ++ private boolean onHitAsWater(final ServerLevel level, final HitResult hitResult) { // Paper - Fix potions splash events AABB aabb = this.getBoundingBox().inflate(4.0, 2.0, 4.0); -- for (LivingEntity livingEntity : this.level().getEntitiesOfClass(LivingEntity.class, aabb, WATER_SENSITIVE_OR_ON_FIRE)) { +- for (LivingEntity entity : this.level().getEntitiesOfClass(LivingEntity.class, aabb, WATER_SENSITIVE_OR_ON_FIRE)) { + // Paper start - Fix potions splash events + java.util.Map affected = new java.util.HashMap<>(); + java.util.Set rehydrate = new java.util.HashSet<>(); + java.util.Set extinguish = new java.util.HashSet<>(); -+ for (LivingEntity livingEntity : this.level().getEntitiesOfClass(LivingEntity.class, aabb, APPLY_WATER_GET_ENTITIES_PREDICATE)) { -+ if (livingEntity instanceof Axolotl axolotl) { ++ for (LivingEntity entity : this.level().getEntitiesOfClass(LivingEntity.class, aabb, APPLY_WATER_GET_ENTITIES_PREDICATE)) { ++ if (entity instanceof Axolotl axolotl) { + rehydrate.add(((org.bukkit.entity.Axolotl) axolotl.getBukkitEntity())); + } + // Paper end - Fix potions splash events - double d = this.distanceToSqr(livingEntity); - if (d < 16.0) { - if (livingEntity.isSensitiveToWater()) { -- livingEntity.hurtServer(level, this.damageSources().indirectMagic(this, this.getOwner()), 1.0F); -+ affected.put(livingEntity.getBukkitLivingEntity(), 1.0); // Paper - Fix potions splash events + double dist = this.distanceToSqr(entity); + if (dist < 16.0) { + if (entity.isSensitiveToWater()) { +- entity.hurtServer(level, this.damageSources().indirectMagic(this, this.getOwner()), 1.0F); ++ affected.put(entity.getBukkitLivingEntity(), 1.0); // Paper - Fix potions splash events } - if (livingEntity.isOnFire() && livingEntity.isAlive()) { -- livingEntity.extinguishFire(); -+ extinguish.add(livingEntity.getBukkitLivingEntity()); // Paper - Fix potions splash events + if (entity.isOnFire() && entity.isAlive()) { +- entity.extinguishFire(); ++ extinguish.add(entity.getBukkitLivingEntity()); // Paper - Fix potions splash events } } } @@ -68,7 +68,7 @@ - axolotl.rehydrate(); + // Paper start - Fix potions splash events + io.papermc.paper.event.entity.WaterBottleSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callWaterBottleSplashEvent( -+ this, result, affected, rehydrate, extinguish ++ this, hitResult, affected, rehydrate, extinguish + ); + if (!event.isCancelled()) { + for (org.bukkit.entity.LivingEntity affectedEntity : event.getToDamage()) { @@ -87,10 +87,10 @@ + return !event.isCancelled(); // Paper - Fix potions splash events } -- protected abstract void onHitAsPotion(ServerLevel level, ItemStack stack, HitResult hitResult); -+ protected abstract boolean onHitAsPotion(ServerLevel level, ItemStack stack, HitResult hitResult); // Paper - Fix potions splash events & More Projectile API +- protected abstract void onHitAsPotion(ServerLevel level, ItemStack potionItem, HitResult hitResult); ++ protected abstract boolean onHitAsPotion(ServerLevel level, ItemStack potionItem, HitResult hitResult); // Paper - Fix potions splash events & More Projectile API - private void dowseFire(BlockPos pos) { + private void dowseFire(final BlockPos pos) { BlockState blockState = this.level().getBlockState(pos); if (blockState.is(BlockTags.FIRE)) { + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, pos, blockState.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/Snowball.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/Snowball.java.patch index 5ce544f89f5f..ece99bf0170f 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/Snowball.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/Snowball.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/entity/projectile/throwableitemprojectile/Snowball.java +++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/Snowball.java -@@ -62,7 +_,7 @@ - super.onHit(result); +@@ -65,7 +_,7 @@ + super.onHit(hitResult); if (!this.level().isClientSide()) { this.level().broadcastEntityEvent(this, EntityEvent.DEATH); - this.discard(); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEgg.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEgg.java.patch index 1c7b472ff8d8..8969dcd4c4c3 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEgg.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEgg.java.patch @@ -1,44 +1,44 @@ --- a/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEgg.java +++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEgg.java -@@ -62,31 +_,66 @@ - protected void onHit(HitResult result) { - super.onHit(result); +@@ -66,29 +_,64 @@ + protected void onHit(final HitResult hitResult) { + super.onHit(hitResult); if (!this.level().isClientSide()) { - if (this.random.nextInt(8) == 0) { + // CraftBukkit start + boolean hatching = this.random.nextInt(8) == 0; + if (true) { + // CraftBukkit end - int i = 1; + int count = 1; if (this.random.nextInt(32) == 0) { - i = 4; + count = 4; } + // CraftBukkit start + if (!hatching) { -+ i = 0; ++ count = 0; + } + + net.minecraft.world.entity.Entity shooter = this.getOwner(); + org.bukkit.entity.EntityType hatchingType = org.bukkit.entity.EntityType.CHICKEN; + if (shooter instanceof net.minecraft.server.level.ServerPlayer) { -+ org.bukkit.event.player.PlayerEggThrowEvent event = new org.bukkit.event.player.PlayerEggThrowEvent((org.bukkit.entity.Player) shooter.getBukkitEntity(), (org.bukkit.entity.Egg) this.getBukkitEntity(), hatching, (byte) i, hatchingType); ++ org.bukkit.event.player.PlayerEggThrowEvent event = new org.bukkit.event.player.PlayerEggThrowEvent((org.bukkit.entity.Player) shooter.getBukkitEntity(), (org.bukkit.entity.Egg) this.getBukkitEntity(), hatching, (byte) count, hatchingType); + event.callEvent(); + + hatching = event.isHatching(); -+ i = hatching ? event.getNumHatches() : 0; // If hatching is set to false, ensure child count is 0 ++ count = hatching ? event.getNumHatches() : 0; // If hatching is set to false, ensure child count is 0 + hatchingType = event.getHatchingType(); + } + // CraftBukkit end + // Paper start - Add ThrownEggHatchEvent -+ com.destroystokyo.paper.event.entity.ThrownEggHatchEvent event = new com.destroystokyo.paper.event.entity.ThrownEggHatchEvent((org.bukkit.entity.Egg) getBukkitEntity(), hatching, (byte) i, hatchingType); ++ com.destroystokyo.paper.event.entity.ThrownEggHatchEvent event = new com.destroystokyo.paper.event.entity.ThrownEggHatchEvent((org.bukkit.entity.Egg) getBukkitEntity(), hatching, (byte) count, hatchingType); + event.callEvent(); + hatching = event.isHatching(); -+ i = hatching ? event.getNumHatches() : 0; // If hatching is set to false, ensure child count is 0 ++ count = hatching ? event.getNumHatches() : 0; // If hatching is set to false, ensure child count is 0 + hatchingType = event.getHatchingType(); + EntityType newEntityType = org.bukkit.craftbukkit.entity.CraftEntityType.bukkitToMinecraft(hatchingType); + // Paper end - Add ThrownEggHatchEvent - for (int i1 = 0; i1 < i; i1++) { + for (int i = 0; i < count; i++) { - Chicken chicken = EntityType.CHICKEN.create(this.level(), EntitySpawnReason.TRIGGERED); + net.minecraft.world.entity.Entity chicken = newEntityType.create(this.level(), net.minecraft.world.entity.EntitySpawnReason.TRIGGERED); // CraftBukkit if (chicken != null) { @@ -49,14 +49,10 @@ + } + // CraftBukkit end chicken.snapTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F); -- Optional.ofNullable(this.getItem().get(DataComponents.CHICKEN_VARIANT)) -- .flatMap(eitherHolder -> eitherHolder.unwrap(this.registryAccess())) -- .ifPresent(chicken::setVariant); +- Optional.ofNullable(this.getItem().get(DataComponents.CHICKEN_VARIANT)).ifPresent(chicken::setVariant); + // CraftBukkit start + if (chicken instanceof Chicken realChicken) { -+ Optional.ofNullable(this.getItem().get(DataComponents.CHICKEN_VARIANT)) -+ .flatMap(eitherHolder -> eitherHolder.unwrap(this.registryAccess())) -+ .ifPresent(realChicken::setVariant); ++ Optional.ofNullable(this.getItem().get(DataComponents.CHICKEN_VARIANT)).ifPresent(realChicken::setVariant); + } + // CraftBukkit end if (!chicken.fudgePositionAfterSizeChange(ZERO_SIZED_DIMENSIONS)) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java.patch index 5baf743b58a9..6b6532ae31bf 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java.patch @@ -1,26 +1,27 @@ --- a/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java +++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownEnderpearl.java -@@ -102,11 +_,21 @@ - Vec3 vec3 = this.oldPosition(); - if (owner instanceof ServerPlayer serverPlayer) { - if (serverPlayer.connection.isAcceptingMessages()) { +@@ -102,11 +_,22 @@ + Vec3 teleportPos = this.oldPosition(); + if (owner instanceof ServerPlayer player) { + if (player.connection.isAcceptingMessages()) { + // CraftBukkit start + // Store pre teleportation position as the teleport has been moved up. -+ final double preTeleportX = serverPlayer.getX(), preTeleportY = serverPlayer.getY(), preTeleportZ = serverPlayer.getZ(); -+ final float preTeleportYRot = serverPlayer.getYRot(), preTeleportXRot = serverPlayer.getXRot(); -+ ServerPlayer serverPlayer1 = serverPlayer.teleport(new TeleportTransition(serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL)); -+ if (serverPlayer1 == null) { ++ final double preTeleportX = player.getX(), preTeleportY = player.getY(), preTeleportZ = player.getZ(); ++ final float preTeleportYRot = player.getYRot(), preTeleportXRot = player.getXRot(); ++ ServerPlayer newOwner = player.teleport(new TeleportTransition( ++ level, teleportPos, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.ENDER_PEARL)); ++ if (newOwner == null) { + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.HIT); + return; + } + // CraftBukkit end - if (this.random.nextFloat() < 0.05F && serverLevel.isSpawningMonsters()) { - Endermite endermite = EntityType.ENDERMITE.create(serverLevel, EntitySpawnReason.TRIGGERED); + if (this.random.nextFloat() < 0.05F && level.isSpawningMonsters()) { + Endermite endermite = EntityType.ENDERMITE.create(level, EntitySpawnReason.TRIGGERED); if (endermite != null) { - endermite.snapTo(owner.getX(), owner.getY(), owner.getZ(), owner.getYRot(), owner.getXRot()); -- serverLevel.addFreshEntity(endermite); +- level.addFreshEntity(endermite); + endermite.snapTo(preTeleportX, preTeleportY, preTeleportZ, preTeleportYRot, preTeleportXRot); // Paper - spawn endermite at pre teleport position as teleport has been moved up -+ serverLevel.addFreshEntity(endermite, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENDER_PEARL); // Paper - add reason ++ level.addFreshEntity(endermite, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENDER_PEARL); // Paper - add reason } } @@ -28,28 +29,28 @@ owner.setPortalCooldown(); } -- ServerPlayer serverPlayer1 = serverPlayer.teleport( +- ServerPlayer newOwner = player.teleport( - new TeleportTransition( -- serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING +- level, teleportPos, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING - ) - ); + // CraftBukkit start - moved up -+ // ServerPlayer serverPlayer1 = serverPlayer.teleport( ++ // ServerPlayer newOwner = player.teleport( + // new TeleportTransition( -+ // serverLevel, vec3, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING ++ // level, teleportPos, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.ROTATION, Relative.DELTA), TeleportTransition.DO_NOTHING + // ) + // ); + // CraftBukkit end - moved up - if (serverPlayer1 != null) { - serverPlayer1.resetFallDistance(); - serverPlayer1.resetCurrentImpulseContext(); -- serverPlayer1.hurtServer(serverPlayer.level(), this.damageSources().enderPearl(), 5.0F); -+ serverPlayer1.hurtServer(serverPlayer.level(), this.damageSources().enderPearl().eventEntityDamager(this), 5.0F); // CraftBukkit // Paper - fix DamageSource API + if (newOwner != null) { + newOwner.resetFallDistance(); + newOwner.resetCurrentImpulseContext(); +- newOwner.hurtServer(player.level(), this.damageSources().enderPearl(), 5.0F); ++ newOwner.hurtServer(player.level(), this.damageSources().enderPearl().eventEntityDamager(this), 5.0F); // CraftBukkit // Paper - fix DamageSource API } - this.playSound(serverLevel, vec3); -@@ -138,9 +_,9 @@ - this.playSound(serverLevel, vec3); + this.playSound(level, teleportPos); +@@ -142,9 +_,9 @@ + this.playSound(level, teleportPos); } - this.discard(); @@ -60,8 +61,8 @@ } } } -@@ -163,7 +_,7 @@ - && !entity.isAlive() +@@ -167,7 +_,7 @@ + && !owner.isAlive() && !serverPlayer.wonGame && serverPlayer.level().getGameRules().get(GameRules.ENDER_PEARLS_VANISH_ON_DEATH)) { - this.discard(); @@ -69,21 +70,21 @@ } else { super.tick(); } -@@ -192,7 +_,7 @@ - public @Nullable Entity teleport(TeleportTransition teleportTransition) { - Entity entity = super.teleport(teleportTransition); - if (entity != null) { -- entity.placePortalTicket(BlockPos.containing(entity.position())); -+ if (!this.level().paperConfig().misc.legacyEnderPearlBehavior) entity.placePortalTicket(BlockPos.containing(entity.position())); // Paper - Allow using old ender pearl behavior +@@ -196,7 +_,7 @@ + public @Nullable Entity teleport(final TeleportTransition transition) { + Entity newEntity = super.teleport(transition); + if (newEntity != null) { +- newEntity.placePortalTicket(BlockPos.containing(newEntity.position())); ++ if (!this.level().paperConfig().misc.legacyEnderPearlBehavior) newEntity.placePortalTicket(BlockPos.containing(newEntity.position())); // Paper - Allow using old ender pearl behavior } - return entity; -@@ -200,7 +_,7 @@ + return newEntity; +@@ -204,7 +_,7 @@ @Override - public boolean canTeleport(Level fromLevel, Level toLevel) { -- return fromLevel.dimension() == Level.END && toLevel.dimension() == Level.OVERWORLD && this.getOwner() instanceof ServerPlayer serverPlayer -+ return fromLevel.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.END && toLevel.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.OVERWORLD && this.getOwner() instanceof ServerPlayer serverPlayer // CraftBukkit - ? super.canTeleport(fromLevel, toLevel) && serverPlayer.seenCredits - : super.canTeleport(fromLevel, toLevel); + public boolean canTeleport(final Level from, final Level to) { +- return from.dimension() == Level.END && to.dimension() == Level.OVERWORLD && this.getOwner() instanceof ServerPlayer player ++ return from.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.END && to.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.OVERWORLD && this.getOwner() instanceof ServerPlayer player // CraftBukkit + ? super.canTeleport(from, to) && player.seenCredits + : super.canTeleport(from, to); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownExperienceBottle.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownExperienceBottle.java.patch index b4cb035bba5c..15d79a480337 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownExperienceBottle.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownExperienceBottle.java.patch @@ -1,28 +1,28 @@ --- a/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownExperienceBottle.java +++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownExperienceBottle.java @@ -40,16 +_,25 @@ - protected void onHit(HitResult result) { - super.onHit(result); - if (this.level() instanceof ServerLevel serverLevel) { -- serverLevel.levelEvent(LevelEvent.PARTICLES_SPELL_POTION_SPLASH, this.blockPosition(), -13083194); + protected void onHit(final HitResult hitResult) { + super.onHit(hitResult); + if (this.level() instanceof ServerLevel level) { +- level.levelEvent(LevelEvent.PARTICLES_SPELL_POTION_SPLASH, this.blockPosition(), -13083194); + // CraftBukkit - moved to after event - int i = 3 + serverLevel.random.nextInt(5) + serverLevel.random.nextInt(5); + int xpCount = 3 + this.random.nextInt(5) + this.random.nextInt(5); + // Paper start - exp bottle event -+ org.bukkit.event.entity.ExpBottleEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExpBottleEvent(this, result, i); -+ i = event.getExperience(); ++ org.bukkit.event.entity.ExpBottleEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExpBottleEvent(this, hitResult, xpCount); ++ xpCount = event.getExperience(); + // Paper end - exp bottle event - if (result instanceof BlockHitResult blockHitResult) { - Vec3 unitVec3 = blockHitResult.getDirection().getUnitVec3(); -- ExperienceOrb.awardWithDirection(serverLevel, result.getLocation(), unitVec3, i); -+ ExperienceOrb.awardWithDirection(serverLevel, result.getLocation(), unitVec3, i, org.bukkit.entity.ExperienceOrb.SpawnReason.EXP_BOTTLE, this.getOwner(), this); // Paper + if (hitResult instanceof BlockHitResult blockHitResult) { + Vec3 blockNormalHit = blockHitResult.getDirection().getUnitVec3(); +- ExperienceOrb.awardWithDirection(level, hitResult.getLocation(), blockNormalHit, xpCount); ++ ExperienceOrb.awardWithDirection(level, hitResult.getLocation(), blockNormalHit, xpCount, org.bukkit.entity.ExperienceOrb.SpawnReason.EXP_BOTTLE, this.getOwner(), this); // Paper } else { -- ExperienceOrb.awardWithDirection(serverLevel, result.getLocation(), this.getDeltaMovement().scale(-1.0), i); +- ExperienceOrb.awardWithDirection(level, hitResult.getLocation(), this.getDeltaMovement().scale(-1.0), xpCount); - } -+ ExperienceOrb.awardWithDirection(serverLevel, result.getLocation(), this.getDeltaMovement().scale(-1.0), i, org.bukkit.entity.ExperienceOrb.SpawnReason.EXP_BOTTLE, this.getOwner(), this); // Paper ++ ExperienceOrb.awardWithDirection(level, hitResult.getLocation(), this.getDeltaMovement().scale(-1.0), xpCount, org.bukkit.entity.ExperienceOrb.SpawnReason.EXP_BOTTLE, this.getOwner(), this); // Paper + } + // Paper start - exp bottle event + if (event.getShowEffect()) { -+ this.level().levelEvent(LevelEvent.PARTICLES_SPELL_POTION_SPLASH, this.blockPosition(), net.minecraft.world.item.alchemy.PotionContents.BASE_POTION_COLOR); ++ level.levelEvent(LevelEvent.PARTICLES_SPELL_POTION_SPLASH, this.blockPosition(), net.minecraft.world.item.alchemy.PotionContents.BASE_POTION_COLOR); + } + // Paper end - exp bottle event diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownLingeringPotion.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownLingeringPotion.java.patch index e41c58a29c49..3b32be385ff1 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownLingeringPotion.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownLingeringPotion.java.patch @@ -4,22 +4,22 @@ } @Override -- public void onHitAsPotion(ServerLevel level, ItemStack stack, HitResult hitResult) { -+ public boolean onHitAsPotion(ServerLevel level, ItemStack stack, HitResult hitResult) { // Paper - More projectile API - AreaEffectCloud areaEffectCloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ()); - if (this.getOwner() instanceof LivingEntity livingEntity) { - areaEffectCloud.setOwner(livingEntity); +- public void onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) { ++ public boolean onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) { // Paper - More projectile API + AreaEffectCloud cloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ()); + if (this.getOwner() instanceof LivingEntity owner) { + cloud.setOwner(owner); @@ -41,6 +_,15 @@ - areaEffectCloud.setWaitTime(10); - areaEffectCloud.setRadiusPerTick(-areaEffectCloud.getRadius() / areaEffectCloud.getDuration()); - areaEffectCloud.applyComponentsFromItemStack(stack); + cloud.setWaitTime(10); + cloud.setRadiusPerTick(-cloud.getRadius() / cloud.getDuration()); + cloud.applyComponentsFromItemStack(potionItem); + boolean noEffects = this.getItem().getOrDefault(net.minecraft.core.component.DataComponents.POTION_CONTENTS, net.minecraft.world.item.alchemy.PotionContents.EMPTY).hasEffects(); // Paper - Fix potions splash events + // CraftBukkit start -+ org.bukkit.event.entity.LingeringPotionSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callLingeringPotionSplashEvent(this, hitResult, areaEffectCloud); -+ if (!(event.isCancelled() || areaEffectCloud.isRemoved() || (!event.allowsEmptyCreation() && (noEffects && !areaEffectCloud.potionContents.hasEffects())))) { // Paper - don't spawn area effect cloud if the effects were empty and not changed during the event handling - level.addFreshEntity(areaEffectCloud); ++ org.bukkit.event.entity.LingeringPotionSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callLingeringPotionSplashEvent(this, hitResult, cloud); ++ if (!(event.isCancelled() || cloud.isRemoved() || (!event.allowsEmptyCreation() && (noEffects && !cloud.potionContents.hasEffects())))) { // Paper - don't spawn area effect cloud if the effects were empty and not changed during the event handling + level.addFreshEntity(cloud); + } else { -+ areaEffectCloud.discard(null); ++ cloud.discard(null); + } + // CraftBukkit end + return !event.isCancelled(); // Paper - Fix potions splash events diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownSplashPotion.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownSplashPotion.java.patch index 5a1801fc2f15..8337f2feb8d1 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownSplashPotion.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/throwableitemprojectile/ThrownSplashPotion.java.patch @@ -4,52 +4,52 @@ } @Override -- public void onHitAsPotion(ServerLevel level, ItemStack stack, HitResult hitResult) { -+ public boolean onHitAsPotion(ServerLevel level, ItemStack stack, HitResult hitResult) { // Paper - More projectile API - PotionContents potionContents = stack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); - float orDefault = stack.getOrDefault(DataComponents.POTION_DURATION_SCALE, 1.0F); - Iterable allEffects = potionContents.getAllEffects(); - AABB aabb = this.getBoundingBox().move(hitResult.getLocation().subtract(this.position())); - AABB aabb1 = aabb.inflate(4.0, 2.0, 4.0); - List entitiesOfClass = this.level().getEntitiesOfClass(LivingEntity.class, aabb1); +- public void onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) { ++ public boolean onHitAsPotion(final ServerLevel level, final ItemStack potionItem, final HitResult hitResult) { // Paper - More projectile API + PotionContents contents = potionItem.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); + float durationScale = potionItem.getOrDefault(DataComponents.POTION_DURATION_SCALE, 1.0F); + Iterable mobEffects = contents.getAllEffects(); + AABB potionAabb = this.getBoundingBox().move(hitResult.getLocation().subtract(this.position())); + AABB effectAabb = potionAabb.inflate(4.0, 2.0, 4.0); + List entities = this.level().getEntitiesOfClass(LivingEntity.class, effectAabb); + java.util.Map affected = new java.util.HashMap<>(); // CraftBukkit - float f = ProjectileUtil.computeMargin(this); - if (!entitiesOfClass.isEmpty()) { + float margin = ProjectileUtil.computeMargin(this); + if (!entities.isEmpty()) { Entity effectSource = this.getEffectSource(); @@ -52,8 +_,25 @@ - if (livingEntity.isAffectedByPotions()) { - double d = aabb.distanceToSqr(livingEntity.getBoundingBox().inflate(f)); - if (d < 16.0) { -- double d1 = 1.0 - Math.sqrt(d) / 4.0; + if (entity.isAffectedByPotions()) { + double dist = potionAabb.distanceToSqr(entity.getBoundingBox().inflate(margin)); + if (dist < 16.0) { +- double scale = 1.0 - Math.sqrt(dist) / 4.0; - -+ double d1 = 1.0 - Math.sqrt(d) / 4.0; // Paper - diff on change, used when calling the splash event for water splash potions ++ double scale = 1.0 - Math.sqrt(dist) / 4.0; // Paper - diff on change, used when calling the splash event for water splash potions + // CraftBukkit start -+ affected.put(livingEntity.getBukkitLivingEntity(), d1); ++ affected.put(entity.getBukkitLivingEntity(), scale); + } + } + } + } + org.bukkit.event.entity.PotionSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPotionSplashEvent(this, hitResult, affected); -+ if (!event.isCancelled() && !entitiesOfClass.isEmpty()) { // do not process effects if there are no effects to process ++ if (!event.isCancelled() && !entities.isEmpty()) { // do not process effects if there are no effects to process + Entity effectSource = this.getEffectSource(); + for (org.bukkit.entity.LivingEntity victim : event.getAffectedEntities()) { + if (!(victim instanceof org.bukkit.craftbukkit.entity.CraftLivingEntity craftLivingEntity)) { + continue; + } -+ LivingEntity livingEntity = craftLivingEntity.getHandle(); -+ double d1 = event.getIntensity(victim); ++ LivingEntity entity = craftLivingEntity.getHandle(); ++ double scale = event.getIntensity(victim); + { + { -+ // CraftBukkit end - for (MobEffectInstance mobEffectInstance : allEffects) { - Holder effect = mobEffectInstance.getEffect(); ++ // CraftBukkit end + for (MobEffectInstance effectInstance : mobEffects) { + Holder effect = effectInstance.getEffect(); if (effect.value().isInstantenous()) { @@ -64,7 +_,7 @@ - effect, i, mobEffectInstance.getAmplifier(), mobEffectInstance.isAmbient(), mobEffectInstance.isVisible() + effect, duration, effectInstance.getAmplifier(), effectInstance.isAmbient(), effectInstance.isVisible() ); - if (!mobEffectInstance1.endsWithin(20)) { -- livingEntity.addEffect(mobEffectInstance1, effectSource); -+ livingEntity.addEffect(mobEffectInstance1, effectSource, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.POTION_SPLASH); // CraftBukkit + if (!newEffect.endsWithin(20)) { +- entity.addEffect(newEffect, effectSource); ++ entity.addEffect(newEffect, effectSource, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.POTION_SPLASH); // CraftBukkit } } } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/raid/Raid.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/raid/Raid.java.patch index b5e70b6efac7..0258d237ed0b 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/raid/Raid.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/raid/Raid.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/raid/Raid.java +++ b/net/minecraft/world/entity/raid/Raid.java -@@ -61,6 +_,12 @@ +@@ -63,6 +_,12 @@ import org.jspecify.annotations.Nullable; public class Raid { @@ -12,17 +12,17 @@ + // Paper end public static final SpawnPlacementType RAVAGER_SPAWN_PLACEMENT_TYPE = SpawnPlacements.getPlacementType(EntityType.RAVAGER); public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group( -@@ -76,6 +_,8 @@ - Raid.RaidStatus.CODEC.fieldOf("status").forGetter(raid -> raid.status), - BlockPos.CODEC.fieldOf("center").forGetter(raid -> raid.center), - UUIDUtil.CODEC_SET.fieldOf("heroes_of_the_village").forGetter(raid -> raid.heroesOfTheVillage) + i -> i.group( +@@ -78,6 +_,8 @@ + Raid.RaidStatus.CODEC.fieldOf("status").forGetter(r -> r.status), + BlockPos.CODEC.fieldOf("center").forGetter(r -> r.center), + UUIDUtil.CODEC_SET.fieldOf("heroes_of_the_village").forGetter(r -> r.heroesOfTheVillage) + , org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer.createCodec(PDC_TYPE_REGISTRY).lenientOptionalFieldOf(PDC_NBT_KEY) // Paper - add persistent data container + .forGetter(raid -> raid.persistentDataContainer.isEmpty() ? java.util.Optional.empty() : java.util.Optional.of(raid.persistentDataContainer)) // Paper - add persistent data container ) - .apply(instance, Raid::new) + .apply(i, Raid::new) ); -@@ -127,6 +_,7 @@ +@@ -131,6 +_,7 @@ this.center = center; this.numGroups = this.getNumGroups(difficulty); this.status = Raid.RaidStatus.ONGOING; @@ -30,15 +30,15 @@ } private Raid( -@@ -142,6 +_,7 @@ - Raid.RaidStatus status, - BlockPos center, - Set heroesOfTheVillage +@@ -146,6 +_,7 @@ + final Raid.RaidStatus status, + final BlockPos center, + final Set heroesOfTheVillage + , final Optional persistentDataContainer // Paper - add persistent data container ) { this.started = started; this.active = active; -@@ -155,6 +_,7 @@ +@@ -159,6 +_,7 @@ this.numGroups = numGroups; this.status = status; this.heroesOfTheVillage.addAll(heroesOfTheVillage); @@ -46,7 +46,7 @@ } public boolean isOver() { -@@ -181,6 +_,12 @@ +@@ -185,6 +_,12 @@ return this.status == Raid.RaidStatus.LOSS; } @@ -59,15 +59,15 @@ public float getTotalHealth() { return this.totalHealth; } -@@ -267,6 +_,7 @@ - boolean flag = this.active; +@@ -271,6 +_,7 @@ + boolean oldActive = this.active; this.active = level.hasChunkAt(this.center); if (level.getDifficulty() == Difficulty.PEACEFUL) { + org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(level, this, org.bukkit.event.raid.RaidStopEvent.Reason.PEACE); // CraftBukkit this.stop(); return; } -@@ -286,13 +_,16 @@ +@@ -290,13 +_,16 @@ if (!level.isVillage(this.center)) { if (this.groupsSpawned > 0) { this.status = Raid.RaidStatus.LOSS; @@ -84,27 +84,27 @@ this.stop(); return; } -@@ -381,6 +_,7 @@ +@@ -385,6 +_,7 @@ } - if (i > 5) { + if (attempt > 5) { + org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(level, this, org.bukkit.event.raid.RaidStopEvent.Reason.UNSPAWNABLE); // CraftBukkit this.stop(); break; } -@@ -392,6 +_,7 @@ +@@ -396,6 +_,7 @@ } else { this.status = Raid.RaidStatus.VICTORY; + List winners = new java.util.ArrayList<>(); // CraftBukkit - for (UUID uuid : this.heroesOfTheVillage) { - Entity entity = level.getEntity(uuid); - if (entity instanceof LivingEntity livingEntity && !entity.isSpectator()) { -@@ -399,9 +_,11 @@ - if (livingEntity instanceof ServerPlayer serverPlayer) { - serverPlayer.awardStat(Stats.RAID_WIN); - CriteriaTriggers.RAID_WIN.trigger(serverPlayer); -+ winners.add(serverPlayer.getBukkitEntity()); // CraftBukkit + for (UUID heroUUID : this.heroesOfTheVillage) { + Entity entity = level.getEntity(heroUUID); + if (entity instanceof LivingEntity hero && !entity.isSpectator()) { +@@ -403,9 +_,11 @@ + if (hero instanceof ServerPlayer playerHero) { + playerHero.awardStat(Stats.RAID_WIN); + CriteriaTriggers.RAID_WIN.trigger(playerHero); ++ winners.add(playerHero.getBukkitEntity()); // CraftBukkit } } } @@ -112,7 +112,7 @@ } } -@@ -409,6 +_,7 @@ +@@ -413,6 +_,7 @@ } else if (this.isOver()) { this.celebrationTicks++; if (this.celebrationTicks >= 600) { @@ -120,35 +120,26 @@ this.stop(); return; } -@@ -513,7 +_,7 @@ - - private void spawnGroup(ServerLevel level, BlockPos pos) { - boolean flag = false; -- int i = this.groupsSpawned + 1; -+ int i = this.groupsSpawned + 1; final int wave = i; // Paper - OBFHELPER - this.totalHealth = 0.0F; - DifficultyInstance currentDifficultyAt = level.getCurrentDifficultyAt(pos); - boolean shouldSpawnBonusGroup = this.shouldSpawnBonusGroup(); -@@ -562,6 +_,7 @@ +@@ -568,6 +_,7 @@ this.groupsSpawned++; this.updateBossbar(); this.setDirty(level); -+ org.bukkit.craftbukkit.event.CraftEventFactory.callRaidSpawnWaveEvent(level, this, java.util.Objects.requireNonNull(this.getLeader(wave)), this.groupRaiderMap.get(wave)); // CraftBukkit ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRaidSpawnWaveEvent(level, this, java.util.Objects.requireNonNull(this.getLeader(groupNumber)), this.groupRaiderMap.get(groupNumber)); // CraftBukkit } - public void joinRaid(ServerLevel level, int wave, Raider raider, @Nullable BlockPos pos, boolean isRecruited) { -@@ -576,7 +_,7 @@ + public void joinRaid(final ServerLevel level, final int groupNumber, final Raider raider, final @Nullable BlockPos pos, final boolean exists) { +@@ -582,7 +_,7 @@ raider.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), EntitySpawnReason.EVENT, null); - raider.applyRaidBuffs(level, wave, false); + raider.applyRaidBuffs(level, groupNumber, false); raider.setOnGround(true); - level.addFreshEntityWithPassengers(raider); + level.addFreshEntityWithPassengers(raider, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.RAID); // CraftBukkit } } } -@@ -793,6 +_,12 @@ - public void addHeroOfTheVillage(Entity player) { - this.heroesOfTheVillage.add(player.getUUID()); +@@ -809,6 +_,12 @@ + public void addHeroOfTheVillage(final Entity killer) { + this.heroesOfTheVillage.add(killer.getUUID()); } + + // CraftBukkit start - a method to get all raiders @@ -157,5 +148,5 @@ + } + // CraftBukkit end - static enum RaidStatus implements StringRepresentable { + private static enum RaidStatus implements StringRepresentable { ONGOING("ongoing"), diff --git a/paper-server/patches/sources/net/minecraft/world/entity/raid/Raider.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/raid/Raider.java.patch index e71cc3849ea2..fc089bfbd33d 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/raid/Raider.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/raid/Raider.java.patch @@ -1,50 +1,50 @@ --- a/net/minecraft/world/entity/raid/Raider.java +++ b/net/minecraft/world/entity/raid/Raider.java -@@ -212,17 +_,24 @@ +@@ -213,17 +_,24 @@ if (this.hasActiveRaid() - && !flag - && ItemStack.matches(item, Raid.getOminousBannerInstance(this.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)))) { + && !hasRaidLeader + && ItemStack.matches(itemStack, Raid.getOminousBannerInstance(this.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)))) { + // Paper start - EntityPickupItemEvent fixes + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(this, entity, 0).isCancelled()) { + return; + } + // Paper end - EntityPickupItemEvent fixes - EquipmentSlot equipmentSlot = EquipmentSlot.HEAD; - ItemStack itemBySlot = this.getItemBySlot(equipmentSlot); - double d = this.getDropChances().byEquipment(equipmentSlot); - if (!itemBySlot.isEmpty() && Math.max(this.random.nextFloat() - 0.1F, 0.0F) < d) { + EquipmentSlot slot = EquipmentSlot.HEAD; + ItemStack current = this.getItemBySlot(slot); + double dropChance = this.getDropChances().byEquipment(slot); + if (!current.isEmpty() && Math.max(this.random.nextFloat() - 0.1F, 0.0F) < dropChance) { + this.forceDrops = true; // Paper - Add missing forceDrop toggles - this.spawnAtLocation(level, itemBySlot); + this.spawnAtLocation(level, current); + this.forceDrops = false; // Paper - Add missing forceDrop toggles } this.onItemPickup(entity); - this.setItemSlot(equipmentSlot, item); - this.take(entity, item.getCount()); + this.setItemSlot(slot, itemStack); + this.take(entity, itemStack.getCount()); - entity.discard(); + entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause this.getCurrentRaid().setLeader(this.getWave(), this); this.setPatrolLeader(true); } else { -@@ -295,7 +_,7 @@ +@@ -296,7 +_,7 @@ - for (Raider raider : getServerLevel(this.mob) + for (Raider entity : getServerLevel(this.mob) .getNearbyEntities(Raider.class, this.shoutTargeting, this.mob, this.mob.getBoundingBox().inflate(8.0, 8.0, 8.0))) { -- raider.setTarget(this.mob.getTarget()); -+ raider.setTarget(this.mob.getTarget(), org.bukkit.event.entity.EntityTargetEvent.TargetReason.FOLLOW_LEADER); // CraftBukkit +- entity.setTarget(this.mob.getTarget()); ++ entity.setTarget(this.mob.getTarget(), org.bukkit.event.entity.EntityTargetEvent.TargetReason.FOLLOW_LEADER); // CraftBukkit } } -@@ -306,7 +_,7 @@ +@@ -307,7 +_,7 @@ if (target != null) { - for (Raider raider : getServerLevel(this.mob) + for (Raider entity : getServerLevel(this.mob) .getNearbyEntities(Raider.class, this.shoutTargeting, this.mob, this.mob.getBoundingBox().inflate(8.0, 8.0, 8.0))) { -- raider.setTarget(target); -+ raider.setTarget(target, org.bukkit.event.entity.EntityTargetEvent.TargetReason.FOLLOW_LEADER); // CraftBukkit - raider.setAggressive(true); +- entity.setTarget(target); ++ entity.setTarget(target, org.bukkit.event.entity.EntityTargetEvent.TargetReason.FOLLOW_LEADER); // CraftBukkit + entity.setAggressive(true); } -@@ -389,6 +_,7 @@ +@@ -393,6 +_,7 @@ } private boolean cannotPickUpBanner() { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/raid/Raids.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/raid/Raids.java.patch index 0efe66607e7e..64840baa8750 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/raid/Raids.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/raid/Raids.java.patch @@ -1,17 +1,17 @@ --- a/net/minecraft/world/entity/raid/Raids.java +++ b/net/minecraft/world/entity/raid/Raids.java -@@ -59,6 +_,7 @@ - private Raids(List raids, int nextId, int tick) { - for (Raids.RaidWithId raidWithId : raids) { - this.raidMap.put(raidWithId.id, raidWithId.raid); -+ raidWithId.raid.idOrNegativeOne = raidWithId.id; // Paper - expose id of raids while method is kept around as deprecated for removal +@@ -52,6 +_,7 @@ + private Raids(final List raids, final int nextId, final int tick) { + for (Raids.RaidWithId raid : raids) { + this.raidMap.put(raid.id, raid.raid); ++ raid.raid.idOrNegativeOne = raid.id; // Paper - expose id of raids while method is kept around as deprecated for removal } this.nextId = nextId; -@@ -137,11 +_,23 @@ +@@ -130,11 +_,23 @@ } - Raid raid = this.getOrCreateRaid(serverLevel, blockPos); + Raid raid = this.getOrCreateRaid(level, raidCenterPos); - if (!raid.isStarted() && !this.raidMap.containsValue(raid)) { - this.raidMap.put(this.getUniqueId(), raid); - } @@ -24,7 +24,7 @@ + + if (!raid.isStarted() || (raid.isInProgress() && raid.getRaidOmenLevel() < raid.getMaxRaidOmenLevel())) { // CraftBukkit - fixed a bug with raid: players could add up Bad Omen level even when the raid had finished + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callRaidTriggerEvent(serverLevel, raid, player)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callRaidTriggerEvent(level, raid, player)) { + player.removeEffect(net.minecraft.world.effect.MobEffects.RAID_OMEN); + return null; + } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/variant/StructureCheck.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/variant/StructureCheck.java.patch index fea326d418df..e48b77c0e873 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/variant/StructureCheck.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/variant/StructureCheck.java.patch @@ -3,7 +3,7 @@ @@ -15,7 +_,7 @@ @Override - public boolean test(SpawnContext context) { + public boolean test(final SpawnContext context) { - return context.level().getLevel().structureManager().getStructureWithPieceAt(context.pos(), this.requiredStructures).isValid(); + return context.level().getLevel().structureManager().getStructureWithPieceAt(context.pos(), this.requiredStructures, context.level()).isValid(); // Paper - Fix swamp hut cat generation deadlock } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/ContainerEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/ContainerEntity.java.patch index ceb95d1af909..4ec5f3460b32 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/ContainerEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/ContainerEntity.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/entity/vehicle/ContainerEntity.java +++ b/net/minecraft/world/entity/vehicle/ContainerEntity.java @@ -59,12 +_,12 @@ - default void addChestVehicleSaveData(ValueOutput output) { + default void addChestVehicleSaveData(final ValueOutput output) { if (this.getContainerLootTable() != null) { output.putString("LootTable", this.getContainerLootTable().identifier().toString()); + this.lootableData().saveNbt(output); // Paper @@ -14,25 +14,25 @@ + ContainerHelper.saveAllItems(output, this.getItemStacks()); // Paper - always save the items, table may still remain } - default void readChestVehicleSaveData(ValueInput input) { + default void readChestVehicleSaveData(final ValueInput input) { @@ -72,7 +_,12 @@ - ResourceKey resourceKey = input.read("LootTable", LootTable.KEY_CODEC).orElse(null); - this.setContainerLootTable(resourceKey); + ResourceKey lootTable = input.read("LootTable", LootTable.KEY_CODEC).orElse(null); + this.setContainerLootTable(lootTable); this.setContainerLootTableSeed(input.getLongOr("LootTableSeed", 0L)); -- if (resourceKey == null) { +- if (lootTable == null) { + // Paper start - LootTable API + if (this.getContainerLootTable() != null) { + this.lootableData().loadNbt(input); + } + // Paper end - LootTable API -+ if (true || resourceKey == null) { // Paper - always read the items, table may still remain ++ if (true || lootTable == null) { // Paper - always read the items, table may still remain ContainerHelper.loadAllItems(input, this.getItemStacks()); } } -@@ -88,19 +_,27 @@ +@@ -87,19 +_,27 @@ } - default InteractionResult interactWithContainerVehicle(Player player) { + default InteractionResult interactWithContainerVehicle(final Player player) { - player.openMenu(this); + // Paper start - Fix InventoryOpenEvent cancellation + if (player.openMenu(this).isEmpty()) { @@ -42,7 +42,7 @@ return InteractionResult.SUCCESS; } - default void unpackChestVehicleLootTable(@Nullable Player player) { + default void unpackChestVehicleLootTable(final @Nullable Player player) { MinecraftServer server = this.level().getServer(); - if (this.getContainerLootTable() != null && server != null) { + if (server != null && this.lootableData().shouldReplenish(this, com.destroystokyo.paper.loottable.PaperLootableInventoryData.ENTITY, player)) { // Paper - LootTable API @@ -60,8 +60,8 @@ LootParams.Builder builder = new LootParams.Builder((ServerLevel)this.level()).withParameter(LootContextParams.ORIGIN, this.position()); if (player != null) { builder.withLuck(player.getLuck()).withParameter(LootContextParams.THIS_ENTITY, player); -@@ -170,4 +_,14 @@ - default boolean isChestVehicleStillValid(Player player) { +@@ -173,4 +_,14 @@ + default boolean isChestVehicleStillValid(final Player player) { return !this.isRemoved() && player.isWithinEntityInteractionRange(this.getBoundingBox(), 4.0); } + diff --git a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/VehicleEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/VehicleEntity.java.patch index 0020b09f5192..70c89728a761 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/VehicleEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/VehicleEntity.java.patch @@ -1,24 +1,31 @@ --- a/net/minecraft/world/entity/vehicle/VehicleEntity.java +++ b/net/minecraft/world/entity/vehicle/VehicleEntity.java -@@ -38,6 +_,14 @@ - } else if (this.isInvulnerableToBase(damageSource)) { +@@ -32,12 +_,20 @@ + } + + @Override +- public boolean hurtServer(final ServerLevel level, final DamageSource source, final float damage) { ++ public boolean hurtServer(final ServerLevel level, final DamageSource source, float damage) { // Paper - unfinal damage + if (this.isRemoved()) { + return true; + } else if (this.isInvulnerableToBase(source)) { return false; } else { + // CraftBukkit start + org.bukkit.entity.Vehicle vehicle = (org.bukkit.entity.Vehicle) this.getBukkitEntity(); -+ org.bukkit.entity.Entity attacker = (damageSource.getEntity() == null) ? null : damageSource.getEntity().getBukkitEntity(); ++ org.bukkit.entity.Entity attacker = (source.getEntity() == null) ? null : source.getEntity().getBukkitEntity(); + -+ org.bukkit.event.vehicle.VehicleDamageEvent event = new org.bukkit.event.vehicle.VehicleDamageEvent(vehicle, attacker, amount); ++ org.bukkit.event.vehicle.VehicleDamageEvent event = new org.bukkit.event.vehicle.VehicleDamageEvent(vehicle, attacker, damage); + if (!event.callEvent()) return false; -+ amount = (float) event.getDamage(); ++ damage = (float) event.getDamage(); + // CraftBukkit end this.setHurtDir(-this.getHurtDir()); this.setHurtTime(10); this.markHurt(); @@ -46,9 +_,23 @@ - boolean flag = damageSource.getEntity() instanceof Player player && player.getAbilities().instabuild; - if ((flag || !(this.getDamage() > 40.0F)) && !this.shouldSourceDestroy(damageSource)) { - if (flag) { + boolean creativePlayer = source.getEntity() instanceof Player player && player.getAbilities().instabuild; + if ((creativePlayer || !(this.getDamage() > 40.0F)) && !this.shouldSourceDestroy(source)) { + if (creativePlayer) { - this.discard(); + // CraftBukkit start + org.bukkit.event.vehicle.VehicleDestroyEvent destroyEvent = new org.bukkit.event.vehicle.VehicleDestroyEvent(vehicle, attacker); @@ -37,6 +44,6 @@ + return true; + } + // CraftBukkit end - this.destroy(level, damageSource); + this.destroy(level, source); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java.patch index c26b465f5514..29cdd8a7965c 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/boat/AbstractBoat.java.patch @@ -13,7 +13,7 @@ + private org.bukkit.Location lastLocation; + // CraftBukkit end + - public AbstractBoat(EntityType type, Level level, Supplier dropItem) { + public AbstractBoat(final EntityType type, final Level level, final Supplier dropItem) { super(type, level); this.dropItem = dropItem; @@ -120,7 +_,7 @@ @@ -28,7 +28,7 @@ @@ -175,11 +_,30 @@ @Override - public void push(Entity entity) { + public void push(final Entity entity) { + if (!this.level().paperConfig().collisions.allowVehicleCollisions && this.level().paperConfig().collisions.onlyPlayersCollide && !(entity instanceof Player)) return; // Paper - Collision option for requiring a player participant if (entity instanceof AbstractBoat) { if (entity.getBoundingBox().minY < this.getBoundingBox().maxY) { @@ -75,21 +75,21 @@ this.applyEffectsFromBlocks(); this.applyEffectsFromBlocks(); this.tickBubbleColumn(); -@@ -545,7 +_,7 @@ +@@ -543,7 +_,7 @@ this.waterLevel = this.getY(1.0); - double d2 = this.getWaterLevelAbove() - this.getBbHeight() + 0.101; - if (this.level().noCollision(this, this.getBoundingBox().move(0.0, d2 - this.getY(), 0.0))) { -- this.setPos(this.getX(), d2, this.getZ()); -+ this.move(MoverType.SELF, new Vec3(0.0D, d2 - this.getY(), 0.0D)); // Paper - Fix some exploit with boats // TODO Still needed? + double targetY = this.getWaterLevelAbove() - this.getBbHeight() + 0.101; + if (this.level().noCollision(this, this.getBoundingBox().move(0.0, targetY - this.getY(), 0.0))) { +- this.setPos(this.getX(), targetY, this.getZ()); ++ this.move(MoverType.SELF, new Vec3(0.0, targetY - this.getY(), 0.0)); // Paper - Fix some exploit with boats // TODO Still needed? this.setDeltaMovement(this.getDeltaMovement().multiply(1.0, 0.0, 1.0)); this.lastYd = 0.0; } -@@ -708,12 +_,12 @@ +@@ -704,12 +_,12 @@ } @Override -- public void remove(Entity.RemovalReason reason) { -+ public void remove(Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause eventCause) { // CraftBukkit - add Bukkit remove cause +- public void remove(final Entity.RemovalReason reason) { ++ public void remove(final Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause eventCause) { // CraftBukkit - add Bukkit remove cause if (!this.level().isClientSide() && reason.shouldDestroy() && this.isLeashed()) { this.dropLeash(); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/boat/AbstractChestBoat.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/boat/AbstractChestBoat.java.patch index fe519a27e621..67ecbf491eed 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/boat/AbstractChestBoat.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/boat/AbstractChestBoat.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/vehicle/boat/AbstractChestBoat.java +++ b/net/minecraft/world/entity/vehicle/boat/AbstractChestBoat.java -@@ -67,12 +_,12 @@ +@@ -68,12 +_,12 @@ } @Override -- public void remove(Entity.RemovalReason reason) { -+ public void remove(Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause +- public void remove(final Entity.RemovalReason reason) { ++ public void remove(final Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause if (!this.level().isClientSide() && reason.shouldDestroy()) { Containers.dropContents(this.level(), this, this); } @@ -15,29 +15,29 @@ } @Override -@@ -95,8 +_,8 @@ +@@ -96,8 +_,8 @@ @Override - public void openCustomInventoryScreen(Player player) { + public void openCustomInventoryScreen(final Player player) { - player.openMenu(this); -- if (player.level() instanceof ServerLevel serverLevel) { +- if (player.level() instanceof ServerLevel level) { + // Paper - fix inventory open cancel - moved into below if -+ if (player.level() instanceof ServerLevel serverLevel && player.openMenu(this).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation ++ if (player.level() instanceof ServerLevel level && player.openMenu(this).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation this.gameEvent(GameEvent.CONTAINER_OPEN, player); - PiglinAi.angerNearbyPiglins(serverLevel, player, true); + PiglinAi.angerNearbyPiglins(level, player, true); } -@@ -148,7 +_,7 @@ +@@ -149,7 +_,7 @@ @Override - public @Nullable AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) { + public @Nullable AbstractContainerMenu createMenu(final int containerId, final Inventory inventory, final Player player) { - if (this.lootTable != null && player.isSpectator()) { + if (this.lootTable != null && player.isSpectator()) { // Paper - LootTable API (TODO spectators can open chests that aren't ready to be re-generated but this doesn't support that) return null; } else { - this.unpackLootTable(playerInventory.player); -@@ -194,4 +_,58 @@ - public void stopOpen(ContainerUser user) { - this.level().gameEvent(GameEvent.CONTAINER_CLOSE, this.position(), GameEvent.Context.of(user.getLivingEntity())); + this.unpackLootTable(inventory.player); +@@ -195,4 +_,58 @@ + public void stopOpen(final ContainerUser containerUser) { + this.level().gameEvent(GameEvent.CONTAINER_CLOSE, this.position(), GameEvent.Context.of(containerUser.getLivingEntity())); } + + // Paper start - LootTable API diff --git a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java.patch index 3e918e368e71..3cb5b2ff19bc 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/AbstractMinecart.java.patch @@ -16,12 +16,12 @@ + public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API + // CraftBukkit end - protected AbstractMinecart(EntityType type, Level level) { + protected AbstractMinecart(final EntityType type, final Level level) { super(type, level); -@@ -154,11 +_,19 @@ +@@ -161,11 +_,19 @@ @Override - public boolean canCollideWith(Entity entity) { + public boolean canCollideWith(final Entity entity) { - return AbstractBoat.canVehicleCollide(this, entity); + // Paper start - fix VehicleEntityCollisionEvent not called when colliding with player + boolean collides = AbstractBoat.canVehicleCollide(this, entity); @@ -40,7 +40,7 @@ return true; } -@@ -259,6 +_,14 @@ +@@ -270,6 +_,14 @@ @Override public void tick() { @@ -55,7 +55,7 @@ if (this.getHurtTime() > 0) { this.setHurtTime(this.getHurtTime() - 1); } -@@ -269,8 +_,20 @@ +@@ -280,8 +_,20 @@ this.checkBelowWorld(); this.computeSpeed(); @@ -74,12 +74,12 @@ + new org.bukkit.event.vehicle.VehicleMoveEvent(vehicle, from, to).callEvent(); + } + // CraftBukkit end - this.updateInWaterStateAndDoFluidPushing(); + this.updateFluidInteraction(); if (this.isInLava()) { this.lavaIgnite(); -@@ -358,12 +_,16 @@ - Vec3 deltaMovement = this.getDeltaMovement(); - this.setDeltaMovement(Mth.clamp(deltaMovement.x, -maxSpeed, maxSpeed), deltaMovement.y, Mth.clamp(deltaMovement.z, -maxSpeed, maxSpeed)); +@@ -371,12 +_,16 @@ + Vec3 movement = this.getDeltaMovement(); + this.setDeltaMovement(Mth.clamp(movement.x, -maxSpeed, maxSpeed), movement.y, Mth.clamp(movement.z, -maxSpeed, maxSpeed)); if (this.onGround()) { - this.setDeltaMovement(this.getDeltaMovement().scale(0.5)); + // CraftBukkit start - replace magic numbers with our variables @@ -96,7 +96,7 @@ } } -@@ -465,6 +_,15 @@ +@@ -478,6 +_,15 @@ this.setDisplayOffset(input.getIntOr("DisplayOffset", this.getDefaultDisplayOffset())); this.flipped = input.getBooleanOr("FlippedRotation", false); this.firstTick = input.getBooleanOr("HasTicked", false); @@ -112,7 +112,7 @@ } @Override -@@ -477,13 +_,26 @@ +@@ -490,13 +_,26 @@ output.putBoolean("FlippedRotation", this.flipped); output.putBoolean("HasTicked", this.firstTick); @@ -124,7 +124,7 @@ } @Override - public void push(Entity entity) { + public void push(final Entity entity) { if (!this.level().isClientSide()) { if (!entity.noPhysics && !this.noPhysics) { + if (!this.level().paperConfig().collisions.allowVehicleCollisions && this.level().paperConfig().collisions.onlyPlayersCollide && !(entity instanceof Player)) return; // Paper - Collision option for requiring a player participant @@ -136,10 +136,10 @@ + ); + if (!collisionEvent.callEvent()) return; + // CraftBukkit end - double d = entity.getX() - this.getX(); - double d1 = entity.getZ() - this.getZ(); - double d2 = d * d + d1 * d1; -@@ -592,4 +_,26 @@ + double xa = entity.getX() - this.getX(); + double za = entity.getZ() - this.getZ(); + double dd = xa * xa + za * za; +@@ -605,4 +_,26 @@ public boolean isFurnace() { return false; } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainer.java.patch index aef9f3d9e99e..ad9fdfdd241a 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainer.java +++ b/net/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainer.java -@@ -23,9 +_,10 @@ +@@ -24,9 +_,10 @@ import org.jspecify.annotations.Nullable; public abstract class AbstractMinecartContainer extends AbstractMinecart implements ContainerEntity { @@ -10,14 +10,14 @@ public long lootTableSeed; + private final com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData = new com.destroystokyo.paper.loottable.PaperLootableInventoryData(); // Paper - LootTable API - protected AbstractMinecartContainer(EntityType type, Level level) { + protected AbstractMinecartContainer(final EntityType type, final Level level) { super(type, level); -@@ -72,12 +_,12 @@ +@@ -73,12 +_,12 @@ } @Override -- public void remove(Entity.RemovalReason reason) { -+ public void remove(Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause +- public void remove(final Entity.RemovalReason reason) { ++ public void remove(final Entity.RemovalReason reason, org.bukkit.event.entity.EntityRemoveEvent.@Nullable Cause cause) { // CraftBukkit - add Bukkit remove cause if (!this.level().isClientSide() && reason.shouldDestroy()) { Containers.dropContents(this.level(), this, this); } @@ -27,7 +27,7 @@ } @Override -@@ -163,4 +_,56 @@ +@@ -164,4 +_,56 @@ public void clearItemStacks() { this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/MinecartCommandBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/MinecartCommandBlock.java.patch index 0eaa33118f97..b90a4b0a86c3 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/MinecartCommandBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/MinecartCommandBlock.java.patch @@ -1,15 +1,15 @@ --- a/net/minecraft/world/entity/vehicle/minecart/MinecartCommandBlock.java +++ b/net/minecraft/world/entity/vehicle/minecart/MinecartCommandBlock.java -@@ -84,7 +_,7 @@ +@@ -88,7 +_,7 @@ @Override - public InteractionResult interact(Player player, InteractionHand hand) { + public InteractionResult interact(final Player player, final InteractionHand hand, final Vec3 location) { - if (!player.canUseGameMasterBlocks()) { + if (!player.canUseGameMasterBlocks() && (!player.isCreative() || !player.getBukkitEntity().hasPermission("minecraft.commandblock"))) { // Paper - command block permission return InteractionResult.PASS; } else { if (player.level().isClientSide()) { -@@ -122,7 +_,7 @@ +@@ -131,7 +_,7 @@ MinecartCommandBlock.this.position(), MinecartCommandBlock.this.getRotationVector(), level, @@ -18,7 +18,7 @@ this.getName().getString(), MinecartCommandBlock.this.getDisplayName(), level.getServer(), -@@ -134,5 +_,17 @@ +@@ -143,5 +_,17 @@ public boolean isValid() { return !MinecartCommandBlock.this.isRemoved(); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/MinecartTNT.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/MinecartTNT.java.patch index a0cc24069497..228857bf91fd 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/MinecartTNT.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/MinecartTNT.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/entity/vehicle/minecart/MinecartTNT.java +++ b/net/minecraft/world/entity/vehicle/minecart/MinecartTNT.java -@@ -39,6 +_,7 @@ +@@ -38,6 +_,7 @@ public int fuse = -1; public float explosionPowerBase = 4.0F; public float explosionSpeedFactor = 1.0F; + public boolean isIncendiary = false; // CraftBukkit - public MinecartTNT(EntityType type, Level level) { + public MinecartTNT(final EntityType type, final Level level) { super(type, level); -@@ -53,6 +_,12 @@ +@@ -52,6 +_,12 @@ public void tick() { super.tick(); if (this.fuse > 0) { @@ -21,14 +21,14 @@ this.fuse--; this.level().addParticle(ParticleTypes.SMOKE, this.getX(), this.getY() + 0.5, this.getZ(), 0.0, 0.0, 0.0); } else if (this.fuse == 0) { -@@ -104,6 +_,17 @@ - if (this.level() instanceof ServerLevel serverLevel) { - if (serverLevel.getGameRules().get(GameRules.TNT_EXPLODES)) { - double min = Math.min(Math.sqrt(radiusModifier), 5.0); +@@ -103,6 +_,17 @@ + if (this.level() instanceof ServerLevel level) { + if (level.getGameRules().get(GameRules.TNT_EXPLODES)) { + double speed = Math.min(Math.sqrt(speedSqr), 5.0); + // CraftBukkit start + org.bukkit.event.entity.ExplosionPrimeEvent event = new org.bukkit.event.entity.ExplosionPrimeEvent( + this.getBukkitEntity(), -+ (float) (this.explosionPowerBase + this.explosionSpeedFactor * this.random.nextDouble() * 1.5 * min), ++ (float) (this.explosionPowerBase + this.explosionSpeedFactor * this.random.nextDouble() * 1.5 * speed), + this.isIncendiary + ); + if (!event.callEvent()) { @@ -36,14 +36,14 @@ + return; + } + // CraftBukkit end - serverLevel.explode( + level.explode( this, damageSource, -@@ -111,13 +_,13 @@ +@@ -110,13 +_,13 @@ this.getX(), this.getY(), this.getZ(), -- (float)(this.explosionPowerBase + this.explosionSpeedFactor * this.random.nextDouble() * 1.5 * min), +- (float)(this.explosionPowerBase + this.explosionSpeedFactor * this.random.nextDouble() * 1.5 * speed), - false, + event.getRadius(), // CraftBukkit - explosion prime event + event.getFire(), // CraftBukkit - explosion prime event diff --git a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/NewMinecartBehavior.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/NewMinecartBehavior.java.patch index d07d507f6177..bbf4cca13b12 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/NewMinecartBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/NewMinecartBehavior.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/entity/vehicle/minecart/NewMinecartBehavior.java +++ b/net/minecraft/world/entity/vehicle/minecart/NewMinecartBehavior.java -@@ -479,6 +_,12 @@ +@@ -482,6 +_,12 @@ @Override - public double getMaxSpeed(ServerLevel level) { + public double getMaxSpeed(final ServerLevel level) { + // CraftBukkit start + Double maxSpeed = this.minecart.maxSpeed; + if (maxSpeed != null) { -+ return this.minecart.isInWater() ? maxSpeed / 2.0D : maxSpeed; ++ return this.minecart.isInWater() ? maxSpeed / 2.0 : maxSpeed; + } + // CraftBukkit end return level.getGameRules().get(GameRules.MAX_MINECART_SPEED).intValue() * (this.minecart.isInWater() ? 0.5 : 1.0) / 20.0; } -@@ -494,7 +_,8 @@ +@@ -497,7 +_,8 @@ @Override public double getSlowdownFactor() { @@ -23,7 +23,7 @@ } @Override -@@ -518,6 +_,13 @@ +@@ -521,6 +_,13 @@ && !(entity instanceof AbstractMinecart) && !this.minecart.isVehicle() && !entity.isPassenger()) { @@ -34,10 +34,10 @@ + ); + if (!collisionEvent.callEvent()) continue; + // CraftBukkit end - boolean flag = entity.startRiding(this.minecart); - if (flag) { + boolean pickedUp = entity.startRiding(this.minecart); + if (pickedUp) { return true; -@@ -541,6 +_,17 @@ +@@ -544,6 +_,17 @@ || entity instanceof AbstractMinecart || this.minecart.isVehicle() || entity.isPassenger()) { @@ -53,21 +53,21 @@ + } + // CraftBukkit end entity.push(this.minecart); - flag = true; + pushed = true; } -@@ -549,6 +_,15 @@ +@@ -552,6 +_,15 @@ } else { - for (Entity entity1 : this.level().getEntities(this.minecart, box)) { - if (!this.minecart.hasPassenger(entity1) && entity1.isPushable() && entity1 instanceof AbstractMinecart) { + for (Entity entityx : this.level().getEntities(this.minecart, hitbox)) { + if (!this.minecart.hasPassenger(entityx) && entityx.isPushable() && entityx instanceof AbstractMinecart) { + // CraftBukkit start + org.bukkit.event.vehicle.VehicleEntityCollisionEvent collisionEvent = new org.bukkit.event.vehicle.VehicleEntityCollisionEvent( + (org.bukkit.entity.Vehicle) this.minecart.getBukkitEntity(), -+ entity1.getBukkitEntity() ++ entityx.getBukkitEntity() + ); + if (!collisionEvent.callEvent()) { + continue; + } + // CraftBukkit end - entity1.push(this.minecart); - flag = true; + entityx.push(this.minecart); + pushed = true; } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/OldMinecartBehavior.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/OldMinecartBehavior.java.patch index e4369eabbed7..891b315c2eb6 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/OldMinecartBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/vehicle/minecart/OldMinecartBehavior.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/vehicle/minecart/OldMinecartBehavior.java +++ b/net/minecraft/world/entity/vehicle/minecart/OldMinecartBehavior.java -@@ -376,8 +_,22 @@ +@@ -375,8 +_,22 @@ && !(entity instanceof AbstractMinecart) && !this.minecart.isVehicle() && !entity.isPassenger()) { @@ -23,27 +23,27 @@ entity.push(this.minecart); } } -@@ -385,6 +_,12 @@ +@@ -384,6 +_,12 @@ } else { - for (Entity entity1 : this.level().getEntities(this.minecart, aabb)) { - if (!this.minecart.hasPassenger(entity1) && entity1.isPushable() && entity1 instanceof AbstractMinecart) { + for (Entity entityx : this.level().getEntities(this.minecart, hitbox)) { + if (!this.minecart.hasPassenger(entityx) && entityx.isPushable() && entityx instanceof AbstractMinecart) { + // CraftBukkit start + org.bukkit.event.vehicle.VehicleEntityCollisionEvent collisionEvent = new org.bukkit.event.vehicle.VehicleEntityCollisionEvent( -+ (org.bukkit.entity.Vehicle) this.minecart.getBukkitEntity(), entity1.getBukkitEntity() ++ (org.bukkit.entity.Vehicle) this.minecart.getBukkitEntity(), entityx.getBukkitEntity() + ); + if (!collisionEvent.callEvent()) continue; + // CraftBukkit end - entity1.push(this.minecart); + entityx.push(this.minecart); } } -@@ -407,11 +_,18 @@ +@@ -406,11 +_,18 @@ @Override - public double getMaxSpeed(ServerLevel level) { + public double getMaxSpeed(final ServerLevel level) { + // CraftBukkit start + Double maxSpeed = this.minecart.maxSpeed; + if (maxSpeed != null) { -+ return this.minecart.isInWater() ? maxSpeed / 2.0D : maxSpeed; ++ return this.minecart.isInWater() ? maxSpeed / 2.0 : maxSpeed; + } + // CraftBukkit end return this.minecart.isInWater() ? 0.2 : 0.4; diff --git a/paper-server/patches/sources/net/minecraft/world/food/FoodData.java.patch b/paper-server/patches/sources/net/minecraft/world/food/FoodData.java.patch index 4b595cfd1806..1612f8473deb 100644 --- a/paper-server/patches/sources/net/minecraft/world/food/FoodData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/food/FoodData.java.patch @@ -1,17 +1,17 @@ --- a/net/minecraft/world/food/FoodData.java +++ b/net/minecraft/world/food/FoodData.java @@ -15,6 +_,11 @@ - public float saturationLevel = 5.0F; + public float saturationLevel = FoodConstants.START_SATURATION; public float exhaustionLevel; private int tickTimer; + // CraftBukkit start -+ public int saturatedRegenRate = 10; -+ public int unsaturatedRegenRate = 80; -+ public int starvationRate = 80; ++ public int saturatedRegenRate = FoodConstants.HEALTH_TICK_COUNT_SATURATED; ++ public int unsaturatedRegenRate = FoodConstants.HEALTH_TICK_COUNT; ++ public int starvationRate = FoodConstants.HEALTH_TICK_COUNT; + // CraftBukkit end - private void add(int nutrition, float saturationLevel) { - this.foodLevel = Mth.clamp(nutrition + this.foodLevel, 0, 20); + private void add(final int food, final float saturation) { + this.foodLevel = Mth.clamp(food + this.foodLevel, 0, 20); @@ -29,6 +_,17 @@ this.add(foodProperties.nutrition(), foodProperties.saturation()); } @@ -27,9 +27,9 @@ + } + // CraftBukkit end + - public void tick(ServerPlayer player) { - ServerLevel serverLevel = player.level(); - Difficulty difficulty = serverLevel.getDifficulty(); + public void tick(final ServerPlayer player) { + ServerLevel level = player.level(); + Difficulty difficulty = level.getDifficulty(); @@ -37,29 +_,39 @@ if (this.saturationLevel > 0.0F) { this.saturationLevel = Math.max(this.saturationLevel - 1.0F, 0.0F); @@ -47,34 +47,34 @@ } } - boolean flag = serverLevel.getGameRules().get(GameRules.NATURAL_HEALTH_REGENERATION); - if (flag && this.saturationLevel > 0.0F && player.isHurt() && this.foodLevel >= 20) { + boolean naturalRegen = level.getGameRules().get(GameRules.NATURAL_HEALTH_REGENERATION); + if (naturalRegen && this.saturationLevel > 0.0F && player.isHurt() && this.foodLevel >= FoodConstants.MAX_FOOD) { this.tickTimer++; -- if (this.tickTimer >= 10) { +- if (this.tickTimer >= FoodConstants.HEALTH_TICK_COUNT_SATURATED) { + if (this.tickTimer >= this.saturatedRegenRate) { // CraftBukkit - float min = Math.min(this.saturationLevel, 6.0F); -- player.heal(min / 6.0F); -- this.addExhaustion(min); -+ player.heal(min / 6.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.SATIATED, true); // CraftBukkit - added RegainReason // Paper - This is fast regen -+ // this.addExhaustion(min); CraftBukkit - EntityExhaustionEvent -+ player.causeFoodExhaustion(min, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.REGEN); // CraftBukkit - EntityExhaustionEvent + float saturationSpent = Math.min(this.saturationLevel, 6.0F); +- player.heal(saturationSpent / 6.0F); +- this.addExhaustion(saturationSpent); ++ player.heal(saturationSpent / 6.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.SATIATED, true); // CraftBukkit - added RegainReason // Paper - This is fast regen ++ // this.addExhaustion(saturationSpent); // CraftBukkit - EntityExhaustionEvent ++ player.causeFoodExhaustion(saturationSpent, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.REGEN); // CraftBukkit - EntityExhaustionEvent this.tickTimer = 0; } - } else if (flag && this.foodLevel >= 18 && player.isHurt()) { + } else if (naturalRegen && this.foodLevel >= FoodConstants.HEAL_LEVEL && player.isHurt()) { this.tickTimer++; -- if (this.tickTimer >= 80) { +- if (this.tickTimer >= FoodConstants.HEALTH_TICK_COUNT) { - player.heal(1.0F); - this.addExhaustion(6.0F); + if (this.tickTimer >= this.unsaturatedRegenRate) { // CraftBukkit - add regen rate manipulation + player.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.SATIATED); // CraftBukkit - added RegainReason -+ // this.addExhaustion(6.0F); CraftBukkit - EntityExhaustionEvent ++ // this.addExhaustion(6.0F); // CraftBukkit - EntityExhaustionEvent + player.causeFoodExhaustion(player.level().spigotConfig.regenExhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.REGEN); // CraftBukkit - EntityExhaustionEvent // Spigot - Change to use configurable value this.tickTimer = 0; } } else if (this.foodLevel <= 0) { this.tickTimer++; -- if (this.tickTimer >= 80) { +- if (this.tickTimer >= FoodConstants.HEALTH_TICK_COUNT) { + if (this.tickTimer >= this.starvationRate) { // CraftBukkit - add regen rate manipulation if (player.getHealth() > 10.0F || difficulty == Difficulty.HARD || player.getHealth() > 1.0F && difficulty == Difficulty.NORMAL) { - player.hurtServer(serverLevel, player.damageSources().starve(), 1.0F); + player.hurtServer(level, player.damageSources().starve(), 1.0F); } diff --git a/paper-server/patches/sources/net/minecraft/world/food/FoodProperties.java.patch b/paper-server/patches/sources/net/minecraft/world/food/FoodProperties.java.patch index d6ade19bb431..a6b6de13f333 100644 --- a/paper-server/patches/sources/net/minecraft/world/food/FoodProperties.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/food/FoodProperties.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/food/FoodProperties.java +++ b/net/minecraft/world/food/FoodProperties.java @@ -41,7 +_,7 @@ - RandomSource random = entity.getRandom(); - level.playSound(null, entity.getX(), entity.getY(), entity.getZ(), consumable.sound().value(), SoundSource.NEUTRAL, 1.0F, random.triangle(1.0F, 0.4F)); - if (entity instanceof Player player) { + RandomSource random = user.getRandom(); + level.playSound(null, user.getX(), user.getY(), user.getZ(), consumable.sound().value(), SoundSource.NEUTRAL, 1.0F, random.triangle(1.0F, 0.4F)); + if (user instanceof Player player) { - player.getFoodData().eat(this); + player.getFoodData().eat(this, stack, (net.minecraft.server.level.ServerPlayer) player); // CraftBukkit level.playSound( diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/AbstractContainerMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/AbstractContainerMenu.java.patch index 37c974a48096..522ea97a86ce 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/AbstractContainerMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/AbstractContainerMenu.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java -@@ -63,6 +_,35 @@ +@@ -65,6 +_,35 @@ private final List containerListeners = Lists.newArrayList(); private @Nullable ContainerSynchronizer synchronizer; private boolean suppressRemoteUpdates; @@ -34,12 +34,12 @@ + public void startOpen() {} + // CraftBukkit end - protected AbstractContainerMenu(@Nullable MenuType menuType, int containerId) { + protected AbstractContainerMenu(final @Nullable MenuType menuType, final int containerId) { this.menuType = menuType; -@@ -174,8 +_,43 @@ +@@ -176,8 +_,43 @@ if (this.synchronizer != null) { - this.synchronizer.sendInitialData(this, list, carried.copy(), this.remoteDataSlots.toIntArray()); + this.synchronizer.sendInitialData(this, itemsToSend, carried.copy(), this.remoteDataSlots.toIntArray()); - } - } + this.synchronizer.sendOffHandSlotChange(); // Paper - Sync offhand slot in menus; update player's offhand since the offhand slot is not added to the slots for menus but can be changed by swapping from a menu slot @@ -80,52 +80,52 @@ + } + // CraftBukkit end - public void removeSlotListener(ContainerListener listener) { + public void removeSlotListener(final ContainerListener listener) { this.containerListeners.remove(listener); -@@ -241,7 +_,7 @@ - this.lastSlots.set(slotIndex, itemStack1); +@@ -243,7 +_,7 @@ + this.lastSlots.set(i, newItem); for (ContainerListener containerListener : this.containerListeners) { -- containerListener.slotChanged(this, slotIndex, itemStack1); -+ containerListener.slotChanged(this, slotIndex, itemStack, itemStack1); // Paper - Add PlayerInventorySlotChangeEvent +- containerListener.slotChanged(this, i, newItem); ++ containerListener.slotChanged(this, i, localExpected, newItem); // Paper - Add PlayerInventorySlotChangeEvent } } } -@@ -349,6 +_,7 @@ +@@ -351,6 +_,7 @@ this.resetQuickCraft(); } - } else if (this.quickcraftStatus == 1) { + } else if (this.quickcraftStatus == QUICKCRAFT_HEADER_CONTINUE) { + if (slotIndex < 0) return; // Paper - Add slot sanity checks to container clicks Slot slot = this.slots.get(slotIndex); - ItemStack carried = this.getCarried(); - if (canItemQuickReplace(slot, carried, true) -@@ -373,6 +_,7 @@ + ItemStack carriedItemStack = this.getCarried(); + if (canItemQuickReplace(slot, carriedItemStack, true) +@@ -375,6 +_,7 @@ } - int count = this.getCarried().getCount(); + int remaining = this.getCarried().getCount(); + it.unimi.dsi.fastutil.ints.Int2ObjectMap draggedSlots = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>(); // CraftBukkit - Store slots from drag in map (raw slot id -> new stack) - for (Slot slot1 : this.quickcraftSlots) { - ItemStack carried1 = this.getCarried(); -@@ -385,12 +_,42 @@ - int min = Math.min(itemStack.getMaxStackSize(), slot1.getMaxStackSize(itemStack)); - int min1 = Math.min(getQuickCraftPlaceCount(this.quickcraftSlots, this.quickcraftType, itemStack) + i2, min); - count -= min1 - i2; -- slot1.setByPlayer(itemStack.copyWithCount(min1)); + for (Slot slot : this.quickcraftSlots) { + ItemStack carriedItemStack = this.getCarried(); +@@ -387,12 +_,42 @@ + int maxSize = Math.min(source.getMaxStackSize(), slot.getMaxStackSize(source)); + int newCount = Math.min(getQuickCraftPlaceCount(this.quickcraftSlots.size(), this.quickcraftType, source) + carry, maxSize); + remaining -= newCount - carry; +- slot.setByPlayer(source.copyWithCount(newCount)); - } - } - -- itemStack.setCount(count); -- this.setCarried(itemStack); -+ // slot1.setByPlayer(itemStack.copyWithCount(min1)); -+ draggedSlots.put(slot1.index, itemStack.copyWithCount(min1)); // CraftBukkit - Put in map instead of setting +- source.setCount(remaining); +- this.setCarried(source); ++ // slot.setByPlayer(source.copyWithCount(newCount)); ++ draggedSlots.put(slot.index, source.copyWithCount(newCount)); // CraftBukkit - Put in map instead of setting + } + } + + // CraftBukkit start - InventoryDragEvent + org.bukkit.inventory.InventoryView view = this.getBukkitView(); -+ org.bukkit.inventory.ItemStack newCarried = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); -+ newCarried.setAmount(count); ++ org.bukkit.inventory.ItemStack newCarried = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(source); ++ newCarried.setAmount(remaining); + java.util.Map eventMap = new java.util.HashMap<>(); + for (it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry entry : draggedSlots.int2ObjectEntrySet()) { + eventMap.put(entry.getIntKey(), org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(entry.getValue())); @@ -156,8 +156,8 @@ } this.resetQuickCraft(); -@@ -404,8 +_,11 @@ - if (slotIndex == SLOT_CLICKED_OUTSIDE) { +@@ -406,8 +_,11 @@ + if (slotIndex == -999) { if (!this.getCarried().isEmpty()) { if (clickAction == ClickAction.PRIMARY) { - player.drop(this.getCarried(), true); @@ -169,13 +169,13 @@ } else { player.drop(this.getCarried().split(1), true); } -@@ -467,8 +_,18 @@ +@@ -469,8 +_,18 @@ } - slot.setChanged(); + slotx.setChanged(); + // CraftBukkit start - Make sure the client has the right slot contents -+ if (player instanceof ServerPlayer serverPlayer && slot.getMaxStackSize() != Container.MAX_STACK) { -+ serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket(this.containerId, this.incrementStateId(), slot.index, slot.getItem())); ++ if (player instanceof ServerPlayer serverPlayer && slotx.getMaxStackSize() != Container.MAX_STACK) { ++ serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket(this.containerId, this.incrementStateId(), slotx.index, slotx.getItem())); + // Updating a crafting inventory makes the client reset the result slot, have to send it again + if (this.getBukkitView().getType() == org.bukkit.event.inventory.InventoryType.WORKBENCH || this.getBukkitView().getType() == org.bukkit.event.inventory.InventoryType.CRAFTING) { + serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket(this.containerId, this.incrementStateId(), 0, this.getSlot(0).getItem())); @@ -183,25 +183,25 @@ + } + // CraftBukkit end } - } else if (clickType == ClickType.SWAP && (button >= 0 && button < 9 || button == 40)) { + } else if (containerInput == ContainerInput.SWAP && (buttonNum >= 0 && buttonNum < 9 || buttonNum == Inventory.SLOT_OFFHAND)) { + if (slotIndex < 0) return; // Paper - Add slot sanity checks to container clicks - ItemStack item = inventory.getItem(button); - Slot slot = this.slots.get(slotIndex); - ItemStack carried = slot.getItem(); -@@ -528,7 +_,11 @@ + ItemStack source = inventory.getItem(buttonNum); + Slot target = this.slots.get(slotIndex); + ItemStack targetItemStack = target.getItem(); +@@ -530,7 +_,11 @@ } - carried = slot2.safeTake(i1, Integer.MAX_VALUE, player); -- player.drop(carried, true); + itemStack = slotx.safeTake(amount, Integer.MAX_VALUE, player); +- player.drop(itemStack, true); + // CraftBukkit start - SPIGOT-8010: break loop -+ if (player.drop(carried, true) == null) { ++ if (player.drop(itemStack, true) == null) { + break; + } + // CraftBukkit end - SPIGOT-8010: break loop - player.handleCreativeModeItemDrop(carried); + player.handleCreativeModeItemDrop(itemStack); } } -@@ -588,8 +_,9 @@ +@@ -595,8 +_,9 @@ if (player instanceof ServerPlayer) { ItemStack carried = this.getCarried(); if (!carried.isEmpty()) { @@ -212,72 +212,72 @@ } } } -@@ -635,6 +_,14 @@ +@@ -642,6 +_,14 @@ public abstract boolean stillValid(Player player); - protected boolean moveItemStackTo(ItemStack stack, int startIndex, int endIndex, boolean reverseDirection) { + protected boolean moveItemStackTo(final ItemStack itemStack, final int startSlot, final int endSlot, final boolean backwards) { + // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent -+ return this.moveItemStackTo(stack, startIndex, endIndex, reverseDirection, false); ++ return this.moveItemStackTo(itemStack, startSlot, endSlot, backwards, false); + } -+ protected boolean moveItemStackTo(ItemStack stack, int startIndex, int endIndex, boolean reverseDirection, boolean isCheck) { ++ protected boolean moveItemStackTo(ItemStack itemStack, final int startSlot, final int endSlot, final boolean backwards, final boolean isCheck) { // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent - non-final itemStack + if (isCheck) { -+ stack = stack.copy(); ++ itemStack = itemStack.copy(); + } + // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent - boolean flag = false; - int i = startIndex; - if (reverseDirection) { -@@ -645,18 +_,27 @@ - while (!stack.isEmpty() && (reverseDirection ? i >= startIndex : i < endIndex)) { - Slot slot = this.slots.get(i); - ItemStack item = slot.getItem(); + boolean anythingChanged = false; + int destSlot = startSlot; + if (backwards) { +@@ -652,18 +_,27 @@ + while (!itemStack.isEmpty() && (backwards ? destSlot >= startSlot : destSlot < endSlot)) { + Slot slot = this.slots.get(destSlot); + ItemStack target = slot.getItem(); + // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent; clone if only a check + if (isCheck) { -+ item = item.copy(); ++ target = target.copy(); + } + // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent - if (!item.isEmpty() && ItemStack.isSameItemSameComponents(stack, item)) { - int i1 = item.getCount() + stack.getCount(); - int maxStackSize = slot.getMaxStackSize(item); - if (i1 <= maxStackSize) { - stack.setCount(0); - item.setCount(i1); + if (!target.isEmpty() && ItemStack.isSameItemSameComponents(itemStack, target)) { + int totalStack = target.getCount() + itemStack.getCount(); + int maxStackSize = slot.getMaxStackSize(target); + if (totalStack <= maxStackSize) { + itemStack.setCount(0); + target.setCount(totalStack); + if (!isCheck) { // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent slot.setChanged(); + } // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent - flag = true; - } else if (item.getCount() < maxStackSize) { - stack.shrink(maxStackSize - item.getCount()); - item.setCount(maxStackSize); -+ if (!isCheck) { // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent + anythingChanged = true; + } else if (target.getCount() < maxStackSize) { + itemStack.shrink(maxStackSize - target.getCount()); + target.setCount(maxStackSize); ++ if (!isCheck) { // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent slot.setChanged(); + } // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent - flag = true; + anythingChanged = true; } } -@@ -679,10 +_,21 @@ - while (reverseDirection ? i >= startIndex : i < endIndex) { - Slot slotx = this.slots.get(i); - ItemStack itemx = slotx.getItem(); +@@ -686,10 +_,21 @@ + while (backwards ? destSlot >= startSlot : destSlot < endSlot) { + Slot slotx = this.slots.get(destSlot); + ItemStack targetx = slotx.getItem(); + // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent + if (isCheck) { -+ itemx = itemx.copy(); ++ targetx = targetx.copy(); + } + // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent - if (itemx.isEmpty() && slotx.mayPlace(stack)) { - int i1 = slotx.getMaxStackSize(stack); + if (targetx.isEmpty() && slotx.mayPlace(itemStack)) { + int maxStackSize = slotx.getMaxStackSize(itemStack); + // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent + if (isCheck) { -+ stack.shrink(Math.min(stack.getCount(), i1)); ++ itemStack.shrink(Math.min(itemStack.getCount(), maxStackSize)); + } else { + // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent - slotx.setByPlayer(stack.split(Math.min(stack.getCount(), i1))); + slotx.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), maxStackSize))); slotx.setChanged(); + } // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent - flag = true; + anythingChanged = true; break; } -@@ -766,6 +_,11 @@ +@@ -773,6 +_,11 @@ } public ItemStack getCarried() { @@ -289,7 +289,7 @@ return this.carried; } -@@ -818,4 +_,15 @@ +@@ -826,4 +_,15 @@ this.stateId = this.stateId + 1 & 32767; return this.stateId; } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/AbstractCraftingMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/AbstractCraftingMenu.java.patch index 7fc235df80e8..456040a8a52b 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/AbstractCraftingMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/AbstractCraftingMenu.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/inventory/AbstractCraftingMenu.java +++ b/net/minecraft/world/inventory/AbstractCraftingMenu.java -@@ -12,14 +_,17 @@ +@@ -13,14 +_,17 @@ public abstract class AbstractCraftingMenu extends RecipeBookMenu { private final int width; private final int height; @@ -8,16 +8,16 @@ + public final TransientCraftingContainer craftSlots; // CraftBukkit public final ResultContainer resultSlots = new ResultContainer(); -- public AbstractCraftingMenu(MenuType menuType, int containerId, int width, int height) { -+ public AbstractCraftingMenu(MenuType menuType, int containerId, int width, int height, Inventory playerInventory) { // CraftBukkit +- public AbstractCraftingMenu(final MenuType menuType, final int containerId, final int width, final int height) { ++ public AbstractCraftingMenu(final MenuType menuType, final int containerId, final int width, final int height, Inventory playerInventory) { // CraftBukkit super(menuType, containerId); this.width = width; this.height = height; - this.craftSlots = new TransientCraftingContainer(this, width, height); + // CraftBukkit start + this.craftSlots = new TransientCraftingContainer(this, width, height, playerInventory.player); -+ this.craftSlots.resultInventory = this.resultSlots; // let InventoryCrafting know about its result slot ++ this.craftSlots.resultInventory = this.resultSlots; // let CraftingContainer know about its result slot + // CraftBukkit end } - protected Slot addResultSlot(Player player, int x, int y) { + protected Slot addResultSlot(final Player player, final int x, final int y) { diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/AbstractFurnaceMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/AbstractFurnaceMenu.java.patch index cfc170467c77..67a7de55c2ab 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/AbstractFurnaceMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/AbstractFurnaceMenu.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/inventory/AbstractFurnaceMenu.java +++ b/net/minecraft/world/inventory/AbstractFurnaceMenu.java -@@ -34,6 +_,21 @@ +@@ -35,6 +_,21 @@ private final RecipeType recipeType; private final RecipePropertySet acceptedInputs; private final RecipeBookType recipeBookType; + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.view.CraftFurnaceView view = null; ++ private org.bukkit.craftbukkit.inventory.view.@org.jspecify.annotations.Nullable CraftFurnaceView view = null; + private final Inventory inventory; + + @Override @@ -21,8 +21,8 @@ + // CraftBukkit end protected AbstractFurnaceMenu( - MenuType menuType, -@@ -68,6 +_,7 @@ + final MenuType menuType, +@@ -69,6 +_,7 @@ this.addSlot(new Slot(container, 0, 56, 17)); this.addSlot(new FurnaceFuelSlot(this, container, 1, 56, 53)); this.addSlot(new FurnaceResultSlot(inventory.player, container, 2, 116, 35)); @@ -30,10 +30,10 @@ this.addStandardInventorySlots(inventory, 8, 84); this.addDataSlots(data); } -@@ -85,6 +_,7 @@ +@@ -86,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return this.container.stillValid(player); } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/AbstractMountInventoryMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/AbstractMountInventoryMenu.java.patch index 55a058461158..4110ad50b994 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/AbstractMountInventoryMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/AbstractMountInventoryMenu.java.patch @@ -13,7 +13,7 @@ protected static final int INVENTORY_ROWS = 3; + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.CraftInventoryView view; ++ private org.bukkit.craftbukkit.inventory.@org.jspecify.annotations.Nullable CraftInventoryView view; + private final Inventory inventory; + + @Override @@ -26,9 +26,9 @@ + } + // CraftBukkit end + - protected AbstractMountInventoryMenu(int containerId, Inventory playerInventory, Container mountContainer, LivingEntity mount) { + protected AbstractMountInventoryMenu(final int containerId, final Inventory playerInventory, final Container mountInventory, final LivingEntity mount) { super(null, containerId); + this.inventory = playerInventory; // CraftBukkit - this.mountContainer = mountContainer; + this.mountContainer = mountInventory; this.mount = mount; - mountContainer.startOpen(playerInventory.player); + mountInventory.startOpen(playerInventory.player); diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/AnvilMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/AnvilMenu.java.patch index 73337324a4bb..ea61437e0837 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/AnvilMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/AnvilMenu.java.patch @@ -11,14 +11,14 @@ + // CraftBukkit end + public boolean bypassEnchantmentLevelRestriction = false; // Paper - bypass anvil level restrictions - public AnvilMenu(int containerId, Inventory playerInventory) { - this(containerId, playerInventory, ContainerLevelAccess.NULL); + public AnvilMenu(final int containerId, final Inventory inventory) { + this(containerId, inventory, ContainerLevelAccess.NULL); @@ -70,7 +_,7 @@ @Override - protected boolean mayPickup(Player player, boolean hasStack) { + protected boolean mayPickup(final Player player, final boolean hasItem) { - return (player.hasInfiniteMaterials() || player.experienceLevel >= this.cost.get()) && this.cost.get() > 0; -+ return (player.hasInfiniteMaterials() || player.experienceLevel >= this.cost.get()) && this.cost.get() > AnvilMenu.DEFAULT_DENIED_COST && hasStack; // CraftBukkit - allow cost 0 like a free item ++ return (player.hasInfiniteMaterials() || player.experienceLevel >= this.cost.get()) && this.cost.get() > AnvilMenu.DEFAULT_DENIED_COST && hasItem; // CraftBukkit - allow cost 0 like a free item } @Override @@ -32,26 +32,26 @@ && !StringUtil.isBlank(this.itemName) && !this.inputSlots.getItem(0).getHoverName().getString().equals(this.itemName)) { @@ -103,6 +_,16 @@ - BlockState blockState = level.getBlockState(blockPos); - if (!player.hasInfiniteMaterials() && blockState.is(BlockTags.ANVIL) && player.getRandom().nextFloat() < 0.12F) { - BlockState blockState1 = AnvilBlock.damage(blockState); + BlockState state = level.getBlockState(pos); + if (!player.hasInfiniteMaterials() && state.is(BlockTags.ANVIL) && player.getRandom().nextFloat() < 0.12F) { + BlockState newBlockState = AnvilBlock.damage(state); + // Paper start - AnvilDamageEvent -+ com.destroystokyo.paper.event.block.AnvilDamagedEvent event = new com.destroystokyo.paper.event.block.AnvilDamagedEvent(getBukkitView(), blockState1 != null ? org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(blockState1) : null); ++ com.destroystokyo.paper.event.block.AnvilDamagedEvent event = new com.destroystokyo.paper.event.block.AnvilDamagedEvent(getBukkitView(), newBlockState != null ? newBlockState.asBlockData() : null); + if (!event.callEvent()) { + return; + } else if (event.getDamageState() == com.destroystokyo.paper.event.block.AnvilDamagedEvent.DamageState.BROKEN) { -+ blockState1 = null; ++ newBlockState = null; + } else { -+ blockState1 = ((org.bukkit.craftbukkit.block.data.CraftBlockData) event.getDamageState().getMaterial().createBlockData()).getState().setValue(AnvilBlock.FACING, blockState.getValue(AnvilBlock.FACING)); ++ newBlockState = ((org.bukkit.craftbukkit.block.data.CraftBlockData) event.getDamageState().getMaterial().createBlockData()).getState().setValue(AnvilBlock.FACING, state.getValue(AnvilBlock.FACING)); + } + // Paper end - AnvilDamageEvent - if (blockState1 == null) { - level.removeBlock(blockPos, false); - level.levelEvent(LevelEvent.SOUND_ANVIL_BROKEN, blockPos, 0); + if (newBlockState == null) { + level.removeBlock(pos, false); + level.levelEvent(LevelEvent.SOUND_ANVIL_BROKEN, pos, 0); @@ -135,8 +_,8 @@ - if (itemStack.isDamageableItem() && item.isValidRepairItem(item1)) { - int min = Math.min(itemStack.getDamageValue(), itemStack.getMaxDamage() / 4); - if (min <= 0) { + if (result.isDamageableItem() && input.isValidRepairItem(addition)) { + int repairAmount = Math.min(result.getDamageValue(), result.getMaxDamage() / 4); + if (repairAmount <= 0) { - this.resultSlots.setItem(0, ItemStack.EMPTY); - this.cost.set(0); + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), ItemStack.EMPTY); // CraftBukkit @@ -60,9 +60,9 @@ } @@ -151,8 +_,8 @@ - this.repairItemCountCost = i2; + this.repairItemCountCost = count; } else { - if (!hasStoredEnchantments && (!itemStack.is(item1.getItem()) || !itemStack.isDamageableItem())) { + if (!usingBook && (!result.is(addition.getItem()) || !result.isDamageableItem())) { - this.resultSlots.setItem(0, ItemStack.EMPTY); - this.cost.set(0); + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), ItemStack.EMPTY); // CraftBukkit @@ -71,18 +71,18 @@ } @@ -198,7 +_,7 @@ - flag1 = true; + isAnyEnchantmentNotCompatible = true; } else { - flag = true; -- if (intValue > enchantment.getMaxLevel()) { -+ if (intValue > enchantment.getMaxLevel() && !this.bypassEnchantmentLevelRestriction) { // Paper - bypass anvil level restrictions - intValue = enchantment.getMaxLevel(); + isAnyEnchantmentCompatible = true; +- if (level > enchantment.getMaxLevel()) { ++ if (level > enchantment.getMaxLevel() && !this.bypassEnchantmentLevelRestriction) { // Paper - bypass anvil level restrictions + level = enchantment.getMaxLevel(); } @@ -216,8 +_,8 @@ } - if (flag1 && !flag) { + if (isAnyEnchantmentNotCompatible && !isAnyEnchantmentCompatible) { - this.resultSlots.setItem(0, ItemStack.EMPTY); - this.cost.set(0); + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), ItemStack.EMPTY); // CraftBukkit @@ -93,7 +93,7 @@ @@ -242,14 +_,16 @@ } - if (i1 == i && i1 > 0) { + if (namingCost == price && namingCost > 0) { - if (this.cost.get() >= 40) { - this.cost.set(39); + // CraftBukkit start @@ -107,15 +107,15 @@ - if (this.cost.get() >= 40 && !this.player.hasInfiniteMaterials()) { + if (this.cost.get() >= this.maximumRepairCost && !this.player.hasInfiniteMaterials()) { // CraftBukkit - itemStack = ItemStack.EMPTY; + result = ItemStack.EMPTY; } @@ -267,12 +_,13 @@ - EnchantmentHelper.setEnchantments(itemStack, mutable.toImmutable()); + EnchantmentHelper.setEnchantments(result, enchantments.toImmutable()); } -- this.resultSlots.setItem(0, itemStack); -+ org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), itemStack); // CraftBukkit +- this.resultSlots.setItem(0, result); ++ org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), result); // CraftBukkit this.broadcastChanges(); } else { - this.resultSlots.setItem(0, ItemStack.EMPTY); @@ -126,7 +126,7 @@ + this.sendAllDataToRemote(); // CraftBukkit - SPIGOT-6686, SPIGOT-7931: Always send completed inventory to stay in sync with client } - public static int calculateIncreasedRepairCost(int oldRepairCost) { + public static int calculateIncreasedRepairCost(final int baseCost) { @@ -293,6 +_,7 @@ } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/BeaconMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/BeaconMenu.java.patch index 65870483569e..06b1bf622dd2 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/BeaconMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/BeaconMenu.java.patch @@ -1,13 +1,17 @@ --- a/net/minecraft/world/inventory/BeaconMenu.java +++ b/net/minecraft/world/inventory/BeaconMenu.java -@@ -22,20 +_,14 @@ +@@ -23,24 +_,14 @@ private static final int USE_ROW_SLOT_START = 28; private static final int USE_ROW_SLOT_END = 37; private static final int NO_EFFECT = 0; - private final Container beacon = new SimpleContainer(1) { +- { +- Objects.requireNonNull(BeaconMenu.this); +- } +- - @Override -- public boolean canPlaceItem(int slot, ItemStack stack) { -- return stack.is(ItemTags.BEACON_PAYMENT_ITEMS); +- public boolean canPlaceItem(final int slot, final ItemStack itemStack) { +- return itemStack.is(ItemTags.BEACON_PAYMENT_ITEMS); - } - - @Override @@ -15,9 +19,8 @@ - return 1; - } - }; -- private final BeaconMenu.PaymentSlot paymentSlot; + private final Container beacon; // Paper - Add missing InventoryHolders Move down -+ private final PaymentSlot paymentSlot; + private final BeaconMenu.PaymentSlot paymentSlot; private final ContainerLevelAccess access; private final ContainerData beaconData; + // CraftBukkit start @@ -25,18 +28,18 @@ + private final net.minecraft.world.entity.player.Inventory inventory; + // CraftBukkit end - public BeaconMenu(int containerId, Container container) { - this(containerId, container, new SimpleContainerData(3), ContainerLevelAccess.NULL); -@@ -43,6 +_,25 @@ + public BeaconMenu(final int containerId, final Container inventory) { + this(containerId, inventory, new SimpleContainerData(3), ContainerLevelAccess.NULL); +@@ -48,6 +_,25 @@ - public BeaconMenu(int containerId, Container container, ContainerData beaconData, ContainerLevelAccess access) { + public BeaconMenu(final int containerId, final Container inventory, final ContainerData beaconData, final ContainerLevelAccess access) { super(MenuType.BEACON, containerId); -+ this.inventory = (net.minecraft.world.entity.player.Inventory) container; // CraftBukkit - TODO: check this ++ this.inventory = (net.minecraft.world.entity.player.Inventory) inventory; // CraftBukkit - TODO: check this + // Paper - Add missing InventoryHolders + this.beacon = new SimpleContainer(this.createBlockHolder(access), 1) { + @Override -+ public boolean canPlaceItem(int slot, ItemStack stack) { -+ return stack.is(ItemTags.BEACON_PAYMENT_ITEMS); ++ public boolean canPlaceItem(final int slot, final ItemStack itemStack) { ++ return itemStack.is(ItemTags.BEACON_PAYMENT_ITEMS); + } + + @Override @@ -53,15 +56,15 @@ checkContainerDataCount(beaconData, 3); this.beaconData = beaconData; this.access = access; -@@ -65,6 +_,7 @@ +@@ -70,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return stillValid(this.access, player, Blocks.BEACON); } -@@ -138,13 +_,30 @@ +@@ -143,13 +_,30 @@ public @Nullable Holder getSecondaryEffect() { return decodeEffect(this.beaconData.get(2)); } @@ -71,21 +74,22 @@ + } + // Paper end - Add PlayerChangeBeaconEffectEvent - public void updateEffects(Optional> primaryEffect, Optional> secondaryEffect) { +- public void updateEffects(final Optional> primary, final Optional> secondary) { ++ public void updateEffects(final Optional> primary, Optional> secondary) { // Paper - fix MC-174630 - make non-final + // Paper start - fix MC-174630 - validate secondary power -+ if (secondaryEffect.isPresent() && secondaryEffect.get() != net.minecraft.world.effect.MobEffects.REGENERATION && (primaryEffect.isPresent() && secondaryEffect.get() != primaryEffect.get())) { -+ secondaryEffect = Optional.empty(); ++ if (secondary.isPresent() && secondary.get() != net.minecraft.world.effect.MobEffects.REGENERATION && (primary.isPresent() && secondary.get() != primary.get())) { ++ secondary = Optional.empty(); + } + // Paper end if (this.paymentSlot.hasItem()) { -- this.beaconData.set(1, encodeEffect(primaryEffect.orElse(null))); -- this.beaconData.set(2, encodeEffect(secondaryEffect.orElse(null))); +- this.beaconData.set(1, encodeEffect(primary.orElse(null))); +- this.beaconData.set(2, encodeEffect(secondary.orElse(null))); + // Paper start - Add PlayerChangeBeaconEffectEvent -+ io.papermc.paper.event.player.PlayerChangeBeaconEffectEvent event = new io.papermc.paper.event.player.PlayerChangeBeaconEffectEvent((org.bukkit.entity.Player) this.inventory.player.getBukkitEntity(), convert(primaryEffect), convert(secondaryEffect), this.access.getLocation().getBlock()); ++ io.papermc.paper.event.player.PlayerChangeBeaconEffectEvent event = new io.papermc.paper.event.player.PlayerChangeBeaconEffectEvent((org.bukkit.entity.Player) this.inventory.player.getBukkitEntity(), convert(primary), convert(secondary), this.access.getLocation().getBlock()); + if (event.callEvent()) { + // Paper end - Add PlayerChangeBeaconEffectEvent -+ this.beaconData.set(1, BeaconMenu.encodeEffect(event.getPrimary() == null ? null : org.bukkit.craftbukkit.potion.CraftPotionEffectType.bukkitToMinecraftHolder(event.getPrimary())));// CraftBukkit - decompile error -+ this.beaconData.set(2, BeaconMenu.encodeEffect(event.getSecondary() == null ? null : org.bukkit.craftbukkit.potion.CraftPotionEffectType.bukkitToMinecraftHolder(event.getSecondary())));// CraftBukkit - decompile error ++ this.beaconData.set(1, BeaconMenu.encodeEffect(event.getPrimary() == null ? null : org.bukkit.craftbukkit.potion.CraftPotionEffectType.bukkitToMinecraftHolder(event.getPrimary()))); // CraftBukkit ++ this.beaconData.set(2, BeaconMenu.encodeEffect(event.getSecondary() == null ? null : org.bukkit.craftbukkit.potion.CraftPotionEffectType.bukkitToMinecraftHolder(event.getSecondary()))); // CraftBukkit + if (event.willConsumeItem()) { // Paper this.paymentSlot.remove(1); + } // Paper @@ -94,7 +98,7 @@ } } -@@ -167,4 +_,17 @@ +@@ -172,4 +_,17 @@ return 1; } } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/BrewingStandMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/BrewingStandMenu.java.patch index fb6d90fa6a68..16eb771d6181 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/BrewingStandMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/BrewingStandMenu.java.patch @@ -5,34 +5,34 @@ public final ContainerData brewingStandData; private final Slot ingredientSlot; + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.view.CraftBrewingStandView view = null; ++ private org.bukkit.craftbukkit.inventory.view.@org.jspecify.annotations.Nullable CraftBrewingStandView view = null; + private final Inventory inventory; + // CraftBukkit end - public BrewingStandMenu(int containerId, Inventory playerInventory) { -- this(containerId, playerInventory, new SimpleContainer(5), new SimpleContainerData(2)); -+ this(containerId, playerInventory, new SimpleContainer(5), new io.papermc.paper.inventory.BrewingSimpleContainerData()); // Paper - Add totalBrewTime + public BrewingStandMenu(final int containerId, final Inventory inventory) { +- this(containerId, inventory, new SimpleContainer(5), new SimpleContainerData(2)); ++ this(containerId, inventory, new SimpleContainer(5), new io.papermc.paper.inventory.BrewingSimpleContainerData()); // Paper - Add totalBrewTime } - public BrewingStandMenu(int containerId, Inventory playerInventory, Container brewingStandContainer, ContainerData brewingStandData) { + public BrewingStandMenu(final int containerId, final Inventory inventory, final Container brewingStand, final ContainerData brewingStandData) { super(MenuType.BREWING_STAND, containerId); -+ this.inventory = playerInventory; // CraftBukkit - checkContainerSize(brewingStandContainer, 5); ++ this.inventory = inventory; // CraftBukkit + checkContainerSize(brewingStand, 5); - checkContainerDataCount(brewingStandData, 2); + checkContainerDataCount(brewingStandData, 3); // Paper - Add recipeBrewTime - this.brewingStand = brewingStandContainer; + this.brewingStand = brewingStand; this.brewingStandData = brewingStandData; - PotionBrewing potionBrewing = playerInventory.player.level().potionBrewing(); -- this.addSlot(new BrewingStandMenu.PotionSlot(brewingStandContainer, 0, 56, 51)); -- this.addSlot(new BrewingStandMenu.PotionSlot(brewingStandContainer, 1, 79, 58)); -- this.addSlot(new BrewingStandMenu.PotionSlot(brewingStandContainer, 2, 102, 51)); + PotionBrewing potionBrewing = inventory.player.level().potionBrewing(); +- this.addSlot(new BrewingStandMenu.PotionSlot(brewingStand, 0, 56, 51)); +- this.addSlot(new BrewingStandMenu.PotionSlot(brewingStand, 1, 79, 58)); +- this.addSlot(new BrewingStandMenu.PotionSlot(brewingStand, 2, 102, 51)); + // Paper start - custom potion mixes -+ this.addSlot(new BrewingStandMenu.PotionSlot(brewingStandContainer, 0, 56, 51, potionBrewing)); -+ this.addSlot(new BrewingStandMenu.PotionSlot(brewingStandContainer, 1, 79, 58, potionBrewing)); -+ this.addSlot(new BrewingStandMenu.PotionSlot(brewingStandContainer, 2, 102, 51, potionBrewing)); ++ this.addSlot(new BrewingStandMenu.PotionSlot(brewingStand, 0, 56, 51, potionBrewing)); ++ this.addSlot(new BrewingStandMenu.PotionSlot(brewingStand, 1, 79, 58, potionBrewing)); ++ this.addSlot(new BrewingStandMenu.PotionSlot(brewingStand, 2, 102, 51, potionBrewing)); + // Paper end - custom potion mixes - this.ingredientSlot = this.addSlot(new BrewingStandMenu.IngredientsSlot(potionBrewing, brewingStandContainer, 3, 79, 17)); - this.addSlot(new BrewingStandMenu.FuelSlot(brewingStandContainer, 4, 17, 17)); + this.ingredientSlot = this.addSlot(new BrewingStandMenu.IngredientsSlot(potionBrewing, brewingStand, 3, 79, 17)); + this.addSlot(new BrewingStandMenu.FuelSlot(brewingStand, 4, 17, 17)); - this.addDataSlots(brewingStandData); + // Paper start - Add recipeBrewTime + this.addDataSlots(new SimpleContainerData(2) { @@ -48,50 +48,50 @@ + } + }); + // Paper end - Add recipeBrewTime - this.addStandardInventorySlots(playerInventory, 8, 84); + this.addStandardInventorySlots(inventory, 8, 84); } @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return this.brewingStand.stillValid(player); } @@ -75,7 +_,7 @@ - if (!this.moveItemStackTo(item, 3, 4, false)) { + if (!this.moveItemStackTo(stack, 3, 4, false)) { return ItemStack.EMPTY; } -- } else if (BrewingStandMenu.PotionSlot.mayPlaceItem(itemStack)) { -+ } else if (BrewingStandMenu.PotionSlot.mayPlaceItem(itemStack, this.inventory.player.level().potionBrewing())) { // Paper - custom potion mixes - if (!this.moveItemStackTo(item, 0, 3, false)) { +- } else if (BrewingStandMenu.PotionSlot.mayPlaceItem(clicked)) { ++ } else if (BrewingStandMenu.PotionSlot.mayPlaceItem(clicked, this.inventory.player.level().potionBrewing())) { // Paper - custom potion mixes + if (!this.moveItemStackTo(stack, 0, 3, false)) { return ItemStack.EMPTY; } @@ -157,13 +_,15 @@ } - static class PotionSlot extends Slot { -- public PotionSlot(Container container, int slot, int x, int y) { + private static class PotionSlot extends Slot { +- public PotionSlot(final Container container, final int slot, final int x, final int y) { + private final PotionBrewing potionBrewing; // Paper - custom potion mixes -+ public PotionSlot(Container container, int slot, int x, int y, PotionBrewing potionBrewing) { // Paper - custom potion mixes ++ public PotionSlot(final Container container, final int slot, final int x, final int y, final PotionBrewing potionBrewing) { // Paper - custom potion mixes super(container, slot, x, y); + this.potionBrewing = potionBrewing; // Paper - custom potion mixes } @Override - public boolean mayPlace(ItemStack stack) { -- return mayPlaceItem(stack); -+ return mayPlaceItem(stack, this.potionBrewing); // Paper - custom potion mixes + public boolean mayPlace(final ItemStack itemStack) { +- return mayPlaceItem(itemStack); ++ return mayPlaceItem(itemStack, this.potionBrewing); // Paper - custom potion mixes } @Override @@ -181,8 +_,8 @@ - super.onTake(player, stack); + super.onTake(player, carried); } -- public static boolean mayPlaceItem(ItemStack stack) { -- return stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE); -+ public static boolean mayPlaceItem(ItemStack stack, PotionBrewing potionBrewing) { // Paper - custom potion mixes -+ return stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE) || potionBrewing.isCustomInput(stack); // Paper - Custom Potion Mixes +- public static boolean mayPlaceItem(final ItemStack itemStack) { +- return itemStack.is(Items.POTION) || itemStack.is(Items.SPLASH_POTION) || itemStack.is(Items.LINGERING_POTION) || itemStack.is(Items.GLASS_BOTTLE); ++ public static boolean mayPlaceItem(final ItemStack itemStack, final PotionBrewing potionBrewing) { // Paper - custom potion mixes) { ++ return itemStack.is(Items.POTION) || itemStack.is(Items.SPLASH_POTION) || itemStack.is(Items.LINGERING_POTION) || itemStack.is(Items.GLASS_BOTTLE) || potionBrewing.isCustomInput(itemStack); // Paper - Custom Potion Mixes } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/CartographyTableMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/CartographyTableMenu.java.patch index d927bf3e9bd3..6a8a2dbc42a4 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/CartographyTableMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/CartographyTableMenu.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/inventory/CartographyTableMenu.java +++ b/net/minecraft/world/inventory/CartographyTableMenu.java -@@ -15,6 +_,21 @@ +@@ -16,6 +_,21 @@ import net.minecraft.world.level.saveddata.maps.MapItemSavedData; public class CartographyTableMenu extends AbstractContainerMenu { + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.CraftInventoryView view = null; ++ private org.bukkit.craftbukkit.inventory.@org.jspecify.annotations.Nullable CraftInventoryView view = null; + private final org.bukkit.entity.Player player; + + @Override @@ -22,11 +22,15 @@ public static final int MAP_SLOT = 0; public static final int ADDITIONAL_SLOT = 1; public static final int RESULT_SLOT = 2; -@@ -24,20 +_,8 @@ +@@ -25,28 +_,8 @@ private static final int USE_ROW_SLOT_END = 39; private final ContainerLevelAccess access; - long lastSoundTime; + private long lastSoundTime; - public final Container container = new SimpleContainer(2) { +- { +- Objects.requireNonNull(CartographyTableMenu.this); +- } +- - @Override - public void setChanged() { - CartographyTableMenu.this.slotsChanged(this); @@ -34,6 +38,10 @@ - } - }; - private final ResultContainer resultContainer = new ResultContainer() { +- { +- Objects.requireNonNull(CartographyTableMenu.this); +- } +- - @Override - public void setChanged() { - CartographyTableMenu.this.slotsChanged(this); @@ -43,11 +51,11 @@ + public final Container container; // Paper - Add missing InventoryHolders - move down + private final ResultContainer resultContainer; // Paper - Add missing InventoryHolders - move down - public CartographyTableMenu(int containerId, Inventory playerInventory) { - this(containerId, playerInventory, ContainerLevelAccess.NULL); -@@ -45,6 +_,34 @@ + public CartographyTableMenu(final int containerId, final Inventory inventory) { + this(containerId, inventory, ContainerLevelAccess.NULL); +@@ -54,6 +_,34 @@ - public CartographyTableMenu(int containerId, Inventory playerInventory, final ContainerLevelAccess access) { + public CartographyTableMenu(final int containerId, final Inventory inventory, final ContainerLevelAccess access) { super(MenuType.CARTOGRAPHY_TABLE, containerId); + // Paper start - Add missing InventoryHolders - move down + this.container = new SimpleContainer(this.createBlockHolder(access), 2) { @@ -79,25 +87,25 @@ + // Paper end - Add missing InventoryHolders - move down this.access = access; this.addSlot(new Slot(this.container, 0, 15, 15) { - @Override -@@ -80,10 +_,12 @@ + { +@@ -101,10 +_,12 @@ } }); - this.addStandardInventorySlots(playerInventory, 8, 84); -+ this.player = (org.bukkit.entity.Player) playerInventory.player.getBukkitEntity(); // CraftBukkit + this.addStandardInventorySlots(inventory, 8, 84); ++ this.player = (org.bukkit.entity.Player) inventory.player.getBukkitEntity(); // CraftBukkit } @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return stillValid(this.access, player, Blocks.CARTOGRAPHY_TABLE); } -@@ -99,6 +_,7 @@ +@@ -120,6 +_,7 @@ } else { this.resultContainer.removeItemNoUpdate(2); } + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareResultEvent(this, RESULT_SLOT); // Paper - Add PrepareResultEvent } - private void setupResultSlot(ItemStack map, ItemStack firstSlotStack, ItemStack resultOutput) { + private void setupResultSlot(final ItemStack mapStack, final ItemStack additionalStack, final ItemStack resultStack) { diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/ChestMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/ChestMenu.java.patch index b65327296886..43f25286dbed 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/ChestMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/ChestMenu.java.patch @@ -5,7 +5,7 @@ private final Container container; private final int containerRows; + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.CraftInventoryView view = null; ++ private org.bukkit.craftbukkit.inventory.@org.jspecify.annotations.Nullable CraftInventoryView view = null; + private final Inventory inventory; + + @Override @@ -33,24 +33,24 @@ + } + // CraftBukkit end - private ChestMenu(MenuType type, int containerId, Inventory playerInventory, int rows) { - this(type, containerId, playerInventory, new SimpleContainer(9 * rows), rows); + private ChestMenu(final MenuType menuType, final int containerId, final Inventory inventory, final int rows) { + this(menuType, containerId, inventory, new SimpleContainer(9 * rows), rows); @@ -51,7 +_,10 @@ checkContainerSize(container, rows * 9); this.container = container; this.containerRows = rows; -- container.startOpen(playerInventory.player); +- container.startOpen(inventory.player); + // container.startOpen(playerInventory.player); // Paper - don't startOpen until menu actually opens + // CraftBukkit start - Save player -+ this.inventory = playerInventory; ++ this.inventory = inventory; + // CraftBukkit end - int i = 18; + int chestGridTop = 18; this.addChestGrid(container, 8, 18); - int i1 = 18 + this.containerRows * 18 + 13; + int inventoryTop = 18 + this.containerRows * 18 + 13; @@ -68,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return this.container.stillValid(player); } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/ContainerLevelAccess.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/ContainerLevelAccess.java.patch index b2a7d2747c6f..2bb6536a97ce 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/ContainerLevelAccess.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/ContainerLevelAccess.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/inventory/ContainerLevelAccess.java +++ b/net/minecraft/world/inventory/ContainerLevelAccess.java @@ -12,6 +_,12 @@ - public Optional evaluate(BiFunction levelPosConsumer) { + public Optional evaluate(final BiFunction action) { return Optional.empty(); } + // Paper start - fix menus with empty level accesses @@ -14,8 +14,8 @@ static ContainerLevelAccess create(final Level level, final BlockPos pos) { @@ -20,6 +_,23 @@ - public Optional evaluate(BiFunction levelPosConsumer) { - return Optional.of(levelPosConsumer.apply(level, pos)); + public Optional evaluate(final BiFunction action) { + return Optional.of(action.apply(level, pos)); } + // CraftBukkit start + @Override @@ -59,7 +59,7 @@ + return false; + } + -+ default @javax.annotation.Nullable org.bukkit.inventory.BlockInventoryHolder createBlockHolder(AbstractContainerMenu menu) { ++ default org.bukkit.inventory.@org.jspecify.annotations.Nullable BlockInventoryHolder createBlockHolder(AbstractContainerMenu menu) { + if (!this.isBlock()) { + return null; + } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/ContainerListener.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/ContainerListener.java.patch index 0f457f3e8be2..da8d59a4b26a 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/ContainerListener.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/ContainerListener.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/inventory/ContainerListener.java +++ b/net/minecraft/world/inventory/ContainerListener.java @@ -6,4 +_,10 @@ - void slotChanged(AbstractContainerMenu containerToSend, int dataSlotIndex, ItemStack stack); + void slotChanged(AbstractContainerMenu container, int slotIndex, ItemStack itemStack); - void dataChanged(AbstractContainerMenu containerMenu, int dataSlotIndex, int value); + void dataChanged(AbstractContainerMenu container, int id, int value); + + // Paper start - Add PlayerInventorySlotChangeEvent + default void slotChanged(AbstractContainerMenu containerToSend, int dataSlotIndex, ItemStack oldStack, ItemStack stack) { diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/CrafterMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/CrafterMenu.java.patch index 02190277dc44..c5de7b0ab871 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/CrafterMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/CrafterMenu.java.patch @@ -5,7 +5,7 @@ private final Player player; private final CraftingContainer container; + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.view.CraftCrafterView view = null; ++ private org.bukkit.craftbukkit.inventory.view.@org.jspecify.annotations.Nullable CraftCrafterView view = null; + + @Override + public org.bukkit.craftbukkit.inventory.view.CraftCrafterView getBukkitView() { @@ -19,12 +19,12 @@ + } + // CraftBukkit end - public CrafterMenu(int containerId, Inventory playerInventory) { + public CrafterMenu(final int containerId, final Inventory inventory) { super(MenuType.CRAFTER_3x3, containerId); @@ -100,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return this.container.stillValid(player); } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/CraftingContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/CraftingContainer.java.patch index 2aef7bff973d..6577394c57b1 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/CraftingContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/CraftingContainer.java.patch @@ -5,11 +5,11 @@ List getItems(); + // CraftBukkit start -+ default net.minecraft.world.item.crafting.RecipeHolder getCurrentRecipe() { ++ default net.minecraft.world.item.crafting.@org.jspecify.annotations.Nullable RecipeHolder getCurrentRecipe() { + return null; + } + -+ default void setCurrentRecipe(net.minecraft.world.item.crafting.RecipeHolder recipe) { ++ default void setCurrentRecipe(net.minecraft.world.item.crafting.@org.jspecify.annotations.Nullable RecipeHolder recipe) { + } + // CraftBukkit end + diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/CraftingMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/CraftingMenu.java.patch index c2af45981276..d901921896f5 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/CraftingMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/CraftingMenu.java.patch @@ -6,20 +6,26 @@ private boolean placingRecipe; + private org.bukkit.craftbukkit.inventory.@Nullable CraftInventoryView view = null; // CraftBukkit - public CraftingMenu(int containerId, Inventory playerInventory) { - this(containerId, playerInventory, ContainerLevelAccess.NULL); + public CraftingMenu(final int containerId, final Inventory inventory) { + this(containerId, inventory, ContainerLevelAccess.NULL); } - public CraftingMenu(int containerId, Inventory playerInventory, ContainerLevelAccess access) { + public CraftingMenu(final int containerId, final Inventory inventory, final ContainerLevelAccess access) { - super(MenuType.CRAFTING, containerId, 3, 3); -+ super(MenuType.CRAFTING, containerId, 3, 3, playerInventory); // CraftBukkit - pass player ++ super(MenuType.CRAFTING, containerId, 3, 3, inventory); // CraftBukkit - pass player this.access = access; - this.player = playerInventory.player; + this.player = inventory.player; this.addResultSlot(this.player, 124, 35); -@@ -55,7 +_,32 @@ - CraftingInput craftInput = craftSlots.asCraftInput(); +@@ -50,12 +_,37 @@ + final Player player, + final CraftingContainer container, + final ResultContainer resultSlots, +- final @Nullable RecipeHolder recipeHint ++ @Nullable RecipeHolder recipeHint // Paper - Perf: Improve mass crafting; check last recipe used first + ) { + CraftingInput input = container.asCraftInput(); ServerPlayer serverPlayer = (ServerPlayer)player; - ItemStack itemStack = ItemStack.EMPTY; + ItemStack result = ItemStack.EMPTY; + // Paper start - Perf: Improve mass crafting; check last recipe used first + /* + When the server crafts all available items in CraftingMenu or InventoryMenu the game @@ -40,27 +46,27 @@ + + See also: ResultSlot class + */ -+ if (recipe == null) { -+ recipe = craftSlots.getCurrentRecipe(); ++ if (recipeHint == null) { ++ recipeHint = container.getCurrentRecipe(); + } + // Paper end - Perf: Improve mass crafting; check last recipe used first - Optional> recipeFor = level.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftInput, level, recipe); -+ craftSlots.setCurrentRecipe(recipeFor.orElse(null)); // CraftBukkit - if (recipeFor.isPresent()) { - RecipeHolder recipeHolder = recipeFor.get(); + Optional> maybeRecipe = level.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, input, level, recipeHint); ++ container.setCurrentRecipe(maybeRecipe.orElse(null)); // CraftBukkit + if (maybeRecipe.isPresent()) { + RecipeHolder recipeHolder = maybeRecipe.get(); CraftingRecipe craftingRecipe = recipeHolder.value(); @@ -66,6 +_,7 @@ } } } -+ itemStack = org.bukkit.craftbukkit.event.CraftEventFactory.callPreCraftEvent(craftSlots, resultSlots, itemStack, menu.getBukkitView(), recipeFor.map(RecipeHolder::value).orElse(null) instanceof net.minecraft.world.item.crafting.RepairItemRecipe); // CraftBukkit ++ result = org.bukkit.craftbukkit.event.CraftEventFactory.callPreCraftEvent(container, resultSlots, result, menu.getBukkitView(), maybeRecipe.map(RecipeHolder::value).orElse(null) instanceof net.minecraft.world.item.crafting.RepairItemRecipe); // CraftBukkit - resultSlots.setItem(0, itemStack); - menu.setRemoteSlot(0, itemStack); + resultSlots.setItem(0, result); + menu.setRemoteSlot(0, result); @@ -102,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return stillValid(this.access, player, Blocks.CRAFTING_TABLE); } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/DispenserMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/DispenserMenu.java.patch index 739a0a9d10b7..7e5de9a28a0a 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/DispenserMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/DispenserMenu.java.patch @@ -5,26 +5,26 @@ private static final int USE_ROW_SLOT_END = 45; public final Container dispenser; + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.CraftInventoryView view = null; ++ private org.bukkit.craftbukkit.inventory.@org.jspecify.annotations.Nullable CraftInventoryView view = null; + private final Inventory inventory; + // CraftBukkit end - public DispenserMenu(int containerId, Inventory playerInventory) { - this(containerId, playerInventory, new SimpleContainer(9)); + public DispenserMenu(final int containerId, final Inventory inventory) { + this(containerId, inventory, new SimpleContainer(9)); @@ -20,6 +_,9 @@ - public DispenserMenu(int containerId, Inventory playerInventory, Container container) { + public DispenserMenu(final int containerId, final Inventory inventory, final Container dispenser) { super(MenuType.GENERIC_3x3, containerId); + // CraftBukkit start - Save player -+ this.inventory = playerInventory; ++ this.inventory = inventory; + // CraftBukkit end - checkContainerSize(container, 9); - this.dispenser = container; - container.startOpen(playerInventory.player); + checkContainerSize(dispenser, 9); + this.dispenser = dispenser; + dispenser.startOpen(inventory.player); @@ -38,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return this.dispenser.stillValid(player); } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/EnchantmentMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/EnchantmentMenu.java.patch index 0a8bd3647751..9e4ceb9aedc1 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/EnchantmentMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/EnchantmentMenu.java.patch @@ -1,10 +1,14 @@ --- a/net/minecraft/world/inventory/EnchantmentMenu.java +++ b/net/minecraft/world/inventory/EnchantmentMenu.java -@@ -31,19 +_,17 @@ +@@ -32,23 +_,17 @@ public class EnchantmentMenu extends AbstractContainerMenu { - static final Identifier EMPTY_SLOT_LAPIS_LAZULI = Identifier.withDefaultNamespace("container/slot/lapis_lazuli"); + private static final Identifier EMPTY_SLOT_LAPIS_LAZULI = Identifier.withDefaultNamespace("container/slot/lapis_lazuli"); - private final Container enchantSlots = new SimpleContainer(2) { +- { +- Objects.requireNonNull(EnchantmentMenu.this); +- } +- - @Override - public void setChanged() { - super.setChanged(); @@ -19,15 +23,15 @@ public final int[] enchantClue = new int[]{-1, -1, -1}; public final int[] levelClue = new int[]{-1, -1, -1}; + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.view.CraftEnchantmentView view = null; ++ private org.bukkit.craftbukkit.inventory.view.@org.jspecify.annotations.Nullable CraftEnchantmentView view = null; + private final org.bukkit.entity.Player player; + // CraftBukkit end - public EnchantmentMenu(int containerId, Inventory playerInventory) { - this(containerId, playerInventory, ContainerLevelAccess.NULL); -@@ -51,6 +_,22 @@ + public EnchantmentMenu(final int containerId, final Inventory inventory) { + this(containerId, inventory, ContainerLevelAccess.NULL); +@@ -56,6 +_,22 @@ - public EnchantmentMenu(int containerId, Inventory playerInventory, ContainerLevelAccess access) { + public EnchantmentMenu(final int containerId, final Inventory inventory, final ContainerLevelAccess access) { super(MenuType.ENCHANTMENT, containerId); + // Paper start - Add missing InventoryHolders + this.enchantSlots = new SimpleContainer(this.createBlockHolder(access), 2) { // Paper - Add missing InventoryHolders @@ -47,37 +51,37 @@ + // Paper end - Add missing InventoryHolders this.access = access; this.addSlot(new Slot(this.enchantSlots, 0, 15, 47) { - @Override -@@ -80,13 +_,14 @@ + { +@@ -93,13 +_,14 @@ this.addDataSlot(DataSlot.shared(this.levelClue, 0)); this.addDataSlot(DataSlot.shared(this.levelClue, 1)); this.addDataSlot(DataSlot.shared(this.levelClue, 2)); -+ this.player = (org.bukkit.entity.Player) playerInventory.player.getBukkitEntity(); // CraftBukkit ++ this.player = (org.bukkit.entity.Player) inventory.player.getBukkitEntity(); // CraftBukkit } @Override - public void slotsChanged(Container inventory) { - if (inventory == this.enchantSlots) { - ItemStack item = inventory.getItem(0); -- if (!item.isEmpty() && item.isEnchantable()) { -+ if (!item.isEmpty()) { // CraftBukkit - relax condition + public void slotsChanged(final Container container) { + if (container == this.enchantSlots) { + ItemStack itemStack = container.getItem(0); +- if (!itemStack.isEmpty() && itemStack.isEnchantable()) { ++ if (!itemStack.isEmpty()) { // CraftBukkit - relax condition this.access.execute((level, pos) -> { - IdMap> holderIdMap = level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap(); - int i1 = 0; -@@ -119,6 +_,42 @@ + IdMap> holders = level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap(); + int bookcases = 0; +@@ -132,6 +_,42 @@ } } + // CraftBukkit start -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); + org.bukkit.enchantments.EnchantmentOffer[] offers = new org.bukkit.enchantments.EnchantmentOffer[3]; + for (int j = 0; j < 3; ++j) { -+ org.bukkit.enchantments.Enchantment enchantment = (this.enchantClue[j] >= 0) ? org.bukkit.craftbukkit.enchantments.CraftEnchantment.minecraftHolderToBukkit(holderIdMap.byId(this.enchantClue[j])) : null; ++ org.bukkit.enchantments.Enchantment enchantment = (this.enchantClue[j] >= 0) ? org.bukkit.craftbukkit.enchantments.CraftEnchantment.minecraftHolderToBukkit(holders.byId(this.enchantClue[j])) : null; + offers[j] = (enchantment != null) ? new org.bukkit.enchantments.EnchantmentOffer(enchantment, this.levelClue[j], this.costs[j]) : null; + } + -+ org.bukkit.event.enchantment.PrepareItemEnchantEvent event = new org.bukkit.event.enchantment.PrepareItemEnchantEvent(this.player, this.getBukkitView(), this.access.getLocation().getBlock(), craftItemStack, offers, i1); -+ event.setCancelled(!item.isEnchantable()); ++ org.bukkit.event.enchantment.PrepareItemEnchantEvent event = new org.bukkit.event.enchantment.PrepareItemEnchantEvent(this.player, this.getBukkitView(), this.access.getLocation().getBlock(), craftItemStack, offers, bookcases); ++ event.setCancelled(!itemStack.isEnchantable()); + level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { @@ -93,7 +97,7 @@ + org.bukkit.enchantments.EnchantmentOffer offer = event.getOffers()[j]; + if (offer != null) { + this.costs[j] = offer.getCost(); -+ this.enchantClue[j] = holderIdMap.getId(org.bukkit.craftbukkit.enchantments.CraftEnchantment ++ this.enchantClue[j] = holders.getId(org.bukkit.craftbukkit.enchantments.CraftEnchantment + .bukkitToMinecraftHolder(offer.getEnchantment())); + this.levelClue[j] = offer.getEnchantmentLevel(); + } else { @@ -107,28 +111,28 @@ this.broadcastChanges(); }); } else { -@@ -145,19 +_,52 @@ +@@ -158,19 +_,52 @@ return false; } else { this.access.execute((level, pos) -> { -- ItemStack itemStack = item; -+ ItemStack itemStack = item; // Paper - diff on change - List enchantmentList = this.getEnchantmentList(level.registryAccess(), item, id, this.costs[id]); -- if (!enchantmentList.isEmpty()) { +- ItemStack enchantmentItem = itemStack; ++ ItemStack enchantmentItem = itemStack; // Paper - diff on change + List newEnchantment = this.getEnchantmentList(level.registryAccess(), itemStack, buttonId, this.costs[buttonId]); +- if (!newEnchantment.isEmpty()) { + // CraftBukkit start + IdMap> registry = level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap(); -+ if (true || !enchantmentList.isEmpty()) { -+ // player.onEnchantmentPerformed(item, i); // Moved down ++ if (true || !newEnchantment.isEmpty()) { ++ // player.onEnchantmentPerformed(itemStack, enchantmentCost); // Moved down + java.util.Map enchants = new java.util.HashMap<>(); -+ for (EnchantmentInstance instance : enchantmentList) { ++ for (EnchantmentInstance instance : newEnchantment) { + enchants.put(org.bukkit.craftbukkit.enchantments.CraftEnchantment.minecraftHolderToBukkit(instance.enchantment()), instance.level()); + } -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); -+ Holder holder = registry.byId(this.enchantClue[id]); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(enchantmentItem); ++ Holder holder = registry.byId(this.enchantClue[buttonId]); + if (holder == null) return; + org.bukkit.enchantments.Enchantment hintedEnchantment = org.bukkit.craftbukkit.enchantments.CraftEnchantment.minecraftHolderToBukkit(holder); -+ int hintedEnchantmentLevel = this.levelClue[id]; -+ org.bukkit.event.enchantment.EnchantItemEvent event = new org.bukkit.event.enchantment.EnchantItemEvent((org.bukkit.entity.Player) player.getBukkitEntity(), this.getBukkitView(), this.access.getLocation().getBlock(), craftItemStack, this.costs[id], enchants, hintedEnchantment, hintedEnchantmentLevel, id); ++ int hintedEnchantmentLevel = this.levelClue[buttonId]; ++ org.bukkit.event.enchantment.EnchantItemEvent event = new org.bukkit.event.enchantment.EnchantItemEvent((org.bukkit.entity.Player) player.getBukkitEntity(), this.getBukkitView(), this.access.getLocation().getBlock(), craftItemStack, this.costs[buttonId], enchants, hintedEnchantment, hintedEnchantmentLevel, buttonId); + level.getCraftServer().getPluginManager().callEvent(event); + int itemLevel = event.getExpLevelCost(); + if (event.isCancelled() || (itemLevel > player.experienceLevel && !player.getAbilities().instabuild) || event.getEnchantsToAdd().isEmpty()) { @@ -136,43 +140,43 @@ + } + // CraftBukkit end + // Paper start -+ itemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.getOrCloneOnMutation(craftItemStack, event.getItem()); -+ if (itemStack != item) { -+ this.enchantSlots.setItem(0, itemStack); ++ enchantmentItem = org.bukkit.craftbukkit.inventory.CraftItemStack.getOrCloneOnMutation(craftItemStack, event.getItem()); ++ if (enchantmentItem != itemStack) { ++ this.enchantSlots.setItem(0, enchantmentItem); + } -+ if (itemStack.is(Items.BOOK)) { -+ itemStack = itemStack.transmuteCopy(Items.ENCHANTED_BOOK); -+ this.enchantSlots.setItem(0, itemStack); ++ if (enchantmentItem.is(Items.BOOK)) { ++ enchantmentItem = enchantmentItem.transmuteCopy(Items.ENCHANTED_BOOK); ++ this.enchantSlots.setItem(0, enchantmentItem); + } + // Paper end + + // CraftBukkit start + for (java.util.Map.Entry entry : event.getEnchantsToAdd().entrySet()) { -+ Holder enchant = org.bukkit.craftbukkit.enchantments.CraftEnchantment.bukkitToMinecraftHolder(entry.getKey()); -+ if (enchant == null) { ++ Holder enchantment = org.bukkit.craftbukkit.enchantments.CraftEnchantment.bukkitToMinecraftHolder(entry.getKey()); ++ if (enchantment == null) { + continue; + } + -+ itemStack.enchant(enchant, entry.getValue()); ++ enchantmentItem.enchant(enchantment, entry.getValue()); + } + // CraftBukkit end - player.onEnchantmentPerformed(item, i); -- if (item.is(Items.BOOK)) { -- itemStack = item.transmuteCopy(Items.ENCHANTED_BOOK); -- this.enchantSlots.setItem(0, itemStack); + player.onEnchantmentPerformed(itemStack, enchantmentCost); +- if (itemStack.is(Items.BOOK)) { +- enchantmentItem = itemStack.transmuteCopy(Items.ENCHANTED_BOOK); +- this.enchantSlots.setItem(0, enchantmentItem); - } - -- for (EnchantmentInstance enchantmentInstance : enchantmentList) { -- itemStack.enchant(enchantmentInstance.enchantment(), enchantmentInstance.level()); +- for (EnchantmentInstance enchantment : newEnchantment) { +- enchantmentItem.enchant(enchantment.enchantment(), enchantment.level()); - } - + + // CraftBukkit - TODO: let plugins change this - item1.consume(i, player); - if (item1.isEmpty()) { + currency.consume(enchantmentCost, player); + if (currency.isEmpty()) { this.enchantSlots.setItem(1, ItemStack.EMPTY); -@@ -202,6 +_,12 @@ - return item.isEmpty() ? 0 : item.getCount(); +@@ -215,6 +_,12 @@ + return goldStack.isEmpty() ? 0 : goldStack.getCount(); } + // Paper start - add enchantment seed update API @@ -184,17 +188,17 @@ public int getEnchantmentSeed() { return this.enchantmentSeed.get(); } -@@ -214,6 +_,7 @@ +@@ -227,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return stillValid(this.access, player, Blocks.ENCHANTING_TABLE); } -@@ -261,4 +_,17 @@ +@@ -274,4 +_,17 @@ - return itemStack; + return clicked; } + + // CraftBukkit start diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/FurnaceResultSlot.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/FurnaceResultSlot.java.patch index 5f4858a6d130..a94ad0f3c913 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/FurnaceResultSlot.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/FurnaceResultSlot.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/inventory/FurnaceResultSlot.java +++ b/net/minecraft/world/inventory/FurnaceResultSlot.java @@ -45,7 +_,7 @@ - protected void checkTakeAchievements(ItemStack stack) { - stack.onCraftedBy(this.player, this.removeCount); + protected void checkTakeAchievements(final ItemStack carried) { + carried.onCraftedBy(this.player, this.removeCount); if (this.player instanceof ServerPlayer serverPlayer && this.container instanceof AbstractFurnaceBlockEntity abstractFurnaceBlockEntity) { - abstractFurnaceBlockEntity.awardUsedRecipesAndPopExperience(serverPlayer); -+ abstractFurnaceBlockEntity.awardUsedRecipesAndPopExperience(serverPlayer, stack, this.removeCount); // CraftBukkit ++ abstractFurnaceBlockEntity.awardUsedRecipesAndPopExperience(serverPlayer, carried, this.removeCount); // CraftBukkit } this.removeCount = 0; diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/GrindstoneMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/GrindstoneMenu.java.patch index 06fc1bb17cde..62e740e61299 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/GrindstoneMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/GrindstoneMenu.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/inventory/GrindstoneMenu.java +++ b/net/minecraft/world/inventory/GrindstoneMenu.java -@@ -21,6 +_,21 @@ +@@ -22,6 +_,21 @@ import net.minecraft.world.phys.Vec3; public class GrindstoneMenu extends AbstractContainerMenu { + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.CraftInventoryView view = null; ++ private org.bukkit.craftbukkit.inventory.@org.jspecify.annotations.Nullable CraftInventoryView view = null; + private final org.bukkit.entity.Player player; + + @Override @@ -22,12 +22,16 @@ public static final int MAX_NAME_LENGTH = 35; public static final int INPUT_SLOT = 0; public static final int ADDITIONAL_SLOT = 1; -@@ -29,14 +_,8 @@ +@@ -30,18 +_,8 @@ private static final int INV_SLOT_END = 30; private static final int USE_ROW_SLOT_START = 30; private static final int USE_ROW_SLOT_END = 39; - private final Container resultSlots = new ResultContainer(); -- final Container repairSlots = new SimpleContainer(2) { +- private final Container repairSlots = new SimpleContainer(2) { +- { +- Objects.requireNonNull(GrindstoneMenu.this); +- } +- - @Override - public void setChanged() { - super.setChanged(); @@ -38,10 +42,10 @@ + final Container repairSlots; // Paper - Add missing InventoryHolders - move down private final ContainerLevelAccess access; - public GrindstoneMenu(int containerId, Inventory playerInventory) { -@@ -45,6 +_,22 @@ + public GrindstoneMenu(final int containerId, final Inventory inventory) { +@@ -50,6 +_,22 @@ - public GrindstoneMenu(int containerId, Inventory playerInventory, final ContainerLevelAccess access) { + public GrindstoneMenu(final int containerId, final Inventory inventory, final ContainerLevelAccess access) { super(MenuType.GRINDSTONE, containerId); + // Paper start - Add missing InventoryHolders + this.resultSlots = new ResultContainer(this.createBlockHolder(access)); // Paper - Add missing InventoryHolders @@ -61,31 +65,31 @@ + // Paper end - Add missing InventoryHolders this.access = access; this.addSlot(new Slot(this.repairSlots, 0, 49, 19) { - @Override -@@ -68,7 +_,11 @@ - public void onTake(Player player, ItemStack stack) { - access.execute((level, blockPos) -> { + { +@@ -85,7 +_,11 @@ + public void onTake(final Player player, final ItemStack carried) { + access.execute((level, pos) -> { if (level instanceof ServerLevel) { -- ExperienceOrb.award((ServerLevel)level, Vec3.atCenterOf(blockPos), this.getExperienceAmount(level)); +- ExperienceOrb.award((ServerLevel)level, Vec3.atCenterOf(pos), this.getExperienceAmount(level)); + // Paper start - Fire BlockExpEvent on grindstone use -+ org.bukkit.event.block.BlockExpEvent event = new org.bukkit.event.block.BlockExpEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos), this.getExperienceAmount(level)); ++ org.bukkit.event.block.BlockExpEvent event = new org.bukkit.event.block.BlockExpEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), this.getExperienceAmount(level)); + event.callEvent(); -+ ExperienceOrb.awardWithDirection((ServerLevel) level, Vec3.atCenterOf(blockPos), Vec3.ZERO, event.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player, null); ++ ExperienceOrb.awardWithDirection((ServerLevel) level, Vec3.atCenterOf(pos), Vec3.ZERO, event.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player, null); + // Paper end - Fire BlockExpEvent on grindstone use } - level.levelEvent(LevelEvent.SOUND_GRINDSTONE_USED, blockPos, 0); -@@ -105,6 +_,7 @@ + level.levelEvent(LevelEvent.SOUND_GRINDSTONE_USED, pos, 0); +@@ -122,6 +_,7 @@ } }); - this.addStandardInventorySlots(playerInventory, 8, 84); -+ this.player = (org.bukkit.entity.Player) playerInventory.player.getBukkitEntity(); // CraftBukkit + this.addStandardInventorySlots(inventory, 8, 84); ++ this.player = (org.bukkit.entity.Player) inventory.player.getBukkitEntity(); // CraftBukkit } @Override -@@ -112,11 +_,13 @@ - super.slotsChanged(inventory); - if (inventory == this.repairSlots) { +@@ -129,11 +_,13 @@ + super.slotsChanged(container); + if (container == this.repairSlots) { this.createResult(); + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareResultEvent(this, RESULT_SLOT); // Paper - Add PrepareResultEvent } @@ -98,10 +102,10 @@ this.broadcastChanges(); } -@@ -202,6 +_,7 @@ +@@ -221,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return stillValid(this.access, player, Blocks.GRINDSTONE); } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/HopperMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/HopperMenu.java.patch index 17d3a0332a1f..69ef0414a894 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/HopperMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/HopperMenu.java.patch @@ -5,7 +5,7 @@ public static final int CONTAINER_SIZE = 5; private final Container hopper; + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.CraftInventoryView view = null; ++ private org.bukkit.craftbukkit.inventory.@org.jspecify.annotations.Nullable CraftInventoryView view = null; + private final Inventory inventory; + + @Override @@ -20,20 +20,20 @@ + } + // CraftBukkit end - public HopperMenu(int containerId, Inventory playerInventory) { - this(containerId, playerInventory, new SimpleContainer(5)); + public HopperMenu(final int containerId, final Inventory inventory) { + this(containerId, inventory, new SimpleContainer(5)); @@ -17,6 +_,7 @@ - public HopperMenu(int containerId, Inventory playerInventory, Container container) { + public HopperMenu(final int containerId, final Inventory inventory, final Container hopper) { super(MenuType.HOPPER, containerId); - this.hopper = container; -+ this.inventory = playerInventory; // CraftBukkit - checkContainerSize(container, 5); - container.startOpen(playerInventory.player); + this.hopper = hopper; ++ this.inventory = inventory; // CraftBukkit + checkContainerSize(hopper, 5); + hopper.startOpen(inventory.player); @@ -29,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return this.hopper.stillValid(player); } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/InventoryMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/InventoryMenu.java.patch index 5a34b07001ad..34d8de1b61cd 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/InventoryMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/InventoryMenu.java.patch @@ -1,21 +1,21 @@ --- a/net/minecraft/world/inventory/InventoryMenu.java +++ b/net/minecraft/world/inventory/InventoryMenu.java -@@ -44,9 +_,13 @@ +@@ -45,9 +_,13 @@ private static final EquipmentSlot[] SLOT_IDS = new EquipmentSlot[]{EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET}; public final boolean active; private final Player owner; -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.CraftInventoryView view = null; // CraftBukkit ++ private org.bukkit.craftbukkit.inventory.@org.jspecify.annotations.Nullable CraftInventoryView view = null; // CraftBukkit - public InventoryMenu(Inventory playerInventory, boolean active, final Player owner) { + public InventoryMenu(final Inventory inventory, final boolean active, final Player owner) { - super(null, 0, 2, 2); + // CraftBukkit start -+ super(null, 0, 2, 2, playerInventory); // CraftBukkit ++ super(null, 0, 2, 2, inventory); // CraftBukkit + this.setTitle(net.minecraft.network.chat.Component.translatable("container.crafting")); // SPIGOT-4722: Allocate title for player inventory + // CraftBukkit end this.active = active; this.owner = owner; this.addResultSlot(owner, 154, 28); -@@ -188,4 +_,54 @@ +@@ -193,4 +_,54 @@ protected Player owner() { return this.owner; } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/ItemCombinerMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/ItemCombinerMenu.java.patch index 942a38db2986..dc86a706abf5 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/ItemCombinerMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/ItemCombinerMenu.java.patch @@ -1,10 +1,14 @@ --- a/net/minecraft/world/inventory/ItemCombinerMenu.java +++ b/net/minecraft/world/inventory/ItemCombinerMenu.java -@@ -15,12 +_,7 @@ +@@ -16,16 +_,7 @@ protected final ContainerLevelAccess access; protected final Player player; protected final Container inputSlots; - protected final ResultContainer resultSlots = new ResultContainer() { +- { +- Objects.requireNonNull(ItemCombinerMenu.this); +- } +- - @Override - public void setChanged() { - ItemCombinerMenu.this.slotsChanged(this); @@ -13,13 +17,17 @@ + protected final ResultContainer resultSlots; // Paper - Add missing InventoryHolders; delay field init private final int resultSlotIndex; - protected boolean mayPickup(Player player, boolean hasStack) { -@@ -36,6 +_,14 @@ + protected boolean mayPickup(final Player player, final boolean hasItem) { +@@ -45,6 +_,18 @@ ) { super(menuType, containerId); this.access = access; + // Paper start - Add missing InventoryHolders; delay field init + this.resultSlots = new ResultContainer(this.createBlockHolder(this.access)) { ++ { ++ Objects.requireNonNull(ItemCombinerMenu.this); ++ } ++ + @Override + public void setChanged() { + ItemCombinerMenu.this.slotsChanged(this); @@ -27,29 +35,48 @@ + }; + // Paper end - Add missing InventoryHolders; delay field init this.player = inventory.player; - this.inputSlots = this.createContainer(slotDefinition.getNumOfInputSlots()); - this.resultSlotIndex = slotDefinition.getResultSlotIndex(); -@@ -79,7 +_,7 @@ + this.inputSlots = this.createContainer(itemInputSlots.getNumOfInputSlots()); + this.resultSlotIndex = itemInputSlots.getResultSlotIndex(); +@@ -54,15 +_,15 @@ + } + + private void createInputSlots(final ItemCombinerMenuSlotDefinition itemInputSlots) { +- for (final ItemCombinerMenuSlotDefinition.SlotDefinition slot : itemInputSlots.getSlots()) { +- this.addSlot(new Slot(this.inputSlots, slot.slotIndex(), slot.x(), slot.y()) { ++ for (final ItemCombinerMenuSlotDefinition.SlotDefinition slotDefinition : itemInputSlots.getSlots()) { // Paper - fix conflicting variable name ++ this.addSlot(new Slot(this.inputSlots, slotDefinition.slotIndex(), slotDefinition.x(), slotDefinition.y()) { // Paper - fix conflicting variable name + { + Objects.requireNonNull(ItemCombinerMenu.this); + } + + @Override + public boolean mayPlace(final ItemStack itemStack) { +- return slot.mayPlace().test(itemStack); ++ return slotDefinition.mayPlace().test(itemStack); // Paper - fix conflicting variable name + } + }); + } +@@ -96,7 +_,7 @@ public abstract void createResult(); - private SimpleContainer createContainer(int size) { + private SimpleContainer createContainer(final int size) { - return new SimpleContainer(size) { + return new SimpleContainer(this.createBlockHolder(this.access), size) { - @Override - public void setChanged() { - super.setChanged(); -@@ -93,6 +_,7 @@ - super.slotsChanged(inventory); - if (inventory == this.inputSlots) { + { + Objects.requireNonNull(ItemCombinerMenu.this); + } +@@ -114,6 +_,7 @@ + super.slotsChanged(container); + if (container == this.inputSlots) { this.createResult(); + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareResultEvent(this, this instanceof SmithingMenu ? 3 : 2); // Paper - Add PrepareResultEvent } } -@@ -104,6 +_,7 @@ +@@ -125,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return this.access .evaluate((level, pos) -> !this.isValidBlock(level.getBlockState(pos)) ? false : player.isWithinBlockInteractionRange(pos, 4.0), true); diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/LecternMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/LecternMenu.java.patch index 2f73276c279a..dc23de136d67 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/LecternMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/LecternMenu.java.patch @@ -1,17 +1,17 @@ --- a/net/minecraft/world/inventory/LecternMenu.java +++ b/net/minecraft/world/inventory/LecternMenu.java -@@ -14,12 +_,29 @@ +@@ -15,12 +_,29 @@ public static final int BUTTON_PAGE_JUMP_RANGE_START = 100; private final Container lectern; private final ContainerData lecternData; - -- public LecternMenu(int containerId) { +- public LecternMenu(final int containerId) { - this(containerId, new SimpleContainer(1), new SimpleContainerData(1)); - } - -- public LecternMenu(int containerId, Container lectern, ContainerData lecternData) { +- public LecternMenu(final int containerId, final Container lectern, final ContainerData lecternData) { + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.view.CraftLecternView view = null; ++ private org.bukkit.craftbukkit.inventory.view.@org.jspecify.annotations.Nullable CraftLecternView view = null; + private final org.bukkit.entity.Player player; + + @Override @@ -27,36 +27,36 @@ + // CraftBukkit end + + // CraftBukkit start - add player inventory -+ public LecternMenu(int containerId, net.minecraft.world.entity.player.Inventory playerinventory) { -+ this(containerId, new SimpleContainer(1), new SimpleContainerData(1), playerinventory); ++ public LecternMenu(final int containerId, final net.minecraft.world.entity.player.Inventory inventory) { ++ this(containerId, new SimpleContainer(1), new SimpleContainerData(1), inventory); + } + -+ public LecternMenu(int containerId, Container lectern, ContainerData lecternData, net.minecraft.world.entity.player.Inventory playerinventory) { ++ public LecternMenu(final int containerId, final Container lectern, final ContainerData lecternData, net.minecraft.world.entity.player.Inventory inventory) { + // CraftBukkit end - add player inventory super(MenuType.LECTERN, containerId); checkContainerSize(lectern, 1); checkContainerDataCount(lecternData, 1); -@@ -33,10 +_,12 @@ +@@ -38,10 +_,12 @@ } }); this.addDataSlots(lecternData); -+ this.player = (org.bukkit.entity.Player) playerinventory.player.getBukkitEntity(); // CraftBukkit ++ this.player = (org.bukkit.entity.Player) inventory.player.getBukkitEntity(); // CraftBukkit } @Override - public boolean clickMenuButton(Player player, int id) { + public boolean clickMenuButton(final Player player, final int buttonId) { + io.papermc.paper.event.player.PlayerLecternPageChangeEvent playerLecternPageChangeEvent; org.bukkit.craftbukkit.inventory.CraftInventoryLectern bukkitView; // Paper - Add PlayerLecternPageChangeEvent - if (id >= 100) { - int i = id - 100; - this.setData(0, i); -@@ -45,12 +_,26 @@ - switch (id) { + if (buttonId >= 100) { + int pageToSet = buttonId - 100; + this.setData(0, pageToSet); +@@ -50,12 +_,26 @@ + switch (buttonId) { case 1: { - int i = this.lecternData.get(0); -- this.setData(0, i - 1); + int currentPage = this.lecternData.get(0); +- this.setData(0, currentPage - 1); + // Paper start - Add PlayerLecternPageChangeEvent + bukkitView = (org.bukkit.craftbukkit.inventory.CraftInventoryLectern) getBukkitView().getTopInventory(); -+ playerLecternPageChangeEvent = new io.papermc.paper.event.player.PlayerLecternPageChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), bukkitView.getHolder(), bukkitView.getBook(), io.papermc.paper.event.player.PlayerLecternPageChangeEvent.PageChangeDirection.LEFT, i, i - 1); ++ playerLecternPageChangeEvent = new io.papermc.paper.event.player.PlayerLecternPageChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), bukkitView.getHolder(), bukkitView.getBook(), io.papermc.paper.event.player.PlayerLecternPageChangeEvent.PageChangeDirection.LEFT, currentPage, currentPage - 1); + if (!playerLecternPageChangeEvent.callEvent()) { + return false; + } @@ -65,11 +65,11 @@ return true; } case 2: { - int i = this.lecternData.get(0); -- this.setData(0, i + 1); + int currentPage = this.lecternData.get(0); +- this.setData(0, currentPage + 1); + // Paper start - Add PlayerLecternPageChangeEvent + bukkitView = (org.bukkit.craftbukkit.inventory.CraftInventoryLectern) getBukkitView().getTopInventory(); -+ playerLecternPageChangeEvent = new io.papermc.paper.event.player.PlayerLecternPageChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), bukkitView.getHolder(), bukkitView.getBook(), io.papermc.paper.event.player.PlayerLecternPageChangeEvent.PageChangeDirection.RIGHT, i, i + 1); ++ playerLecternPageChangeEvent = new io.papermc.paper.event.player.PlayerLecternPageChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), bukkitView.getHolder(), bukkitView.getBook(), io.papermc.paper.event.player.PlayerLecternPageChangeEvent.PageChangeDirection.RIGHT, currentPage, currentPage + 1); + if (!playerLecternPageChangeEvent.callEvent()) { + return false; + } @@ -78,25 +78,24 @@ return true; } case 3: -@@ -58,6 +_,13 @@ +@@ -63,6 +_,12 @@ return false; } + // CraftBukkit start - Event for taking the book + org.bukkit.event.player.PlayerTakeLecternBookEvent event = new org.bukkit.event.player.PlayerTakeLecternBookEvent(this.player, this.getBukkitView().getTopInventory().getHolder()); -+ org.bukkit.Bukkit.getServer().getPluginManager().callEvent(event); -+ if (event.isCancelled()) { ++ if (!event.callEvent()) { + return false; + } + // CraftBukkit end - ItemStack itemStack = this.lectern.removeItemNoUpdate(0); + ItemStack book = this.lectern.removeItemNoUpdate(0); this.lectern.setChanged(); - if (!player.getInventory().add(itemStack)) { -@@ -84,6 +_,8 @@ + if (!player.getInventory().add(book)) { +@@ -89,6 +_,8 @@ @Override - public boolean stillValid(Player player) { -+ if (this.lectern instanceof net.minecraft.world.level.block.entity.LecternBlockEntity.LecternInventory && !((net.minecraft.world.level.block.entity.LecternBlockEntity.LecternInventory) this.lectern).getLectern().hasBook()) return false; // CraftBukkit + public boolean stillValid(final Player player) { ++ if (this.lectern instanceof net.minecraft.world.level.block.entity.LecternBlockEntity.LecternInventory lecternInventory && !lecternInventory.getLectern().hasBook()) return false; // CraftBukkit + if (!this.checkReachable) return true; // CraftBukkit return this.lectern.stillValid(player); } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/LoomMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/LoomMenu.java.patch index 66aebda55cd6..4e4bf954797f 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/LoomMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/LoomMenu.java.patch @@ -1,10 +1,14 @@ --- a/net/minecraft/world/inventory/LoomMenu.java +++ b/net/minecraft/world/inventory/LoomMenu.java -@@ -38,21 +_,23 @@ +@@ -39,29 +_,23 @@ private final Slot patternSlot; private final Slot resultSlot; - long lastSoundTime; + private long lastSoundTime; - private final Container inputContainer = new SimpleContainer(3) { +- { +- Objects.requireNonNull(LoomMenu.this); +- } +- - @Override - public void setChanged() { - super.setChanged(); @@ -13,6 +17,10 @@ - } - }; - private final Container outputContainer = new SimpleContainer(1) { +- { +- Objects.requireNonNull(LoomMenu.this); +- } +- - @Override - public void setChanged() { - super.setChanged(); @@ -22,7 +30,7 @@ + private final Container inputContainer; // Paper - Add missing InventoryHolders - move down + private final Container outputContainer; // Paper - Add missing InventoryHolders - move down + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.view.CraftLoomView view = null; ++ private org.bukkit.craftbukkit.inventory.view.@org.jspecify.annotations.Nullable CraftLoomView view = null; + private final org.bukkit.entity.Player player; + + @Override @@ -37,10 +45,10 @@ + } + // CraftBukkit end - public LoomMenu(int containerId, Inventory playerInventory) { - this(containerId, playerInventory, ContainerLevelAccess.NULL); -@@ -61,6 +_,28 @@ - public LoomMenu(int containerId, Inventory playerInventory, final ContainerLevelAccess access) { + public LoomMenu(final int containerId, final Inventory inventory) { + this(containerId, inventory, ContainerLevelAccess.NULL); +@@ -70,6 +_,28 @@ + public LoomMenu(final int containerId, final Inventory inventory, final ContainerLevelAccess access) { super(MenuType.LOOM, containerId); this.access = access; + // CraftBukkit start @@ -66,28 +74,31 @@ + }; + // CraftBukkit end this.bannerSlot = this.addSlot(new Slot(this.inputContainer, 0, 13, 26) { - @Override - public boolean mayPlace(ItemStack stack) { -@@ -106,18 +_,44 @@ - this.addStandardInventorySlots(playerInventory, 8, 84); + { + Objects.requireNonNull(LoomMenu.this); +@@ -131,6 +_,7 @@ + this.addStandardInventorySlots(inventory, 8, 84); this.addDataSlot(this.selectedBannerPatternIndex); - this.patternGetter = playerInventory.player.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN); -+ this.player = (org.bukkit.entity.Player) playerInventory.player.getBukkitEntity(); // CraftBukkit + this.patternGetter = inventory.player.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN); ++ this.player = (org.bukkit.entity.Player) inventory.player.getBukkitEntity(); // CraftBukkit } + private static boolean isPatternItem(final ItemStack itemStack) { +@@ -143,14 +_,39 @@ + @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return stillValid(this.access, player, Blocks.LOOM); } @Override - public boolean clickMenuButton(Player player, int id) { - if (id >= 0 && id < this.selectablePatterns.size()) { -- this.selectedBannerPatternIndex.set(id); -- this.setupResultSlot(this.selectablePatterns.get(id)); + public boolean clickMenuButton(final Player player, final int buttonId) { + if (buttonId >= 0 && buttonId < this.selectablePatterns.size()) { +- this.selectedBannerPatternIndex.set(buttonId); +- this.setupResultSlot(this.selectablePatterns.get(buttonId)); + // Paper start - Add PlayerLoomPatternSelectEvent -+ int selectablePatternIndex = id; ++ int selectablePatternIndex = buttonId; + io.papermc.paper.event.player.PlayerLoomPatternSelectEvent event = new io.papermc.paper.event.player.PlayerLoomPatternSelectEvent((org.bukkit.entity.Player) player.getBukkitEntity(), this.getBukkitView().getTopInventory(), org.bukkit.craftbukkit.block.banner.CraftPatternType.minecraftHolderToBukkit(this.selectablePatterns.get(selectablePatternIndex))); + if (!event.callEvent()) { + player.containerMenu.sendAllDataToRemote(); @@ -115,7 +126,7 @@ return true; } else { return false; -@@ -181,7 +_,8 @@ +@@ -212,7 +_,8 @@ this.resultSlot.set(ItemStack.EMPTY); } @@ -125,19 +136,19 @@ } else { this.resultSlot.set(ItemStack.EMPTY); this.selectablePatterns = List.of(); -@@ -270,7 +_,14 @@ - itemStack.update( - DataComponents.BANNER_PATTERNS, - BannerPatternLayers.EMPTY, -- bannerPatternLayers -> new BannerPatternLayers.Builder().addAll(bannerPatternLayers).add(pattern, dyeColor).build() -+ // CraftBukkit start -+ bannerPatternLayers -> { -+ if (bannerPatternLayers.layers().size() > 20) { -+ bannerPatternLayers = new BannerPatternLayers(List.copyOf(bannerPatternLayers.layers().subList(0, 20))); +@@ -302,7 +_,14 @@ + result.update( + DataComponents.BANNER_PATTERNS, + BannerPatternLayers.EMPTY, +- layers -> new BannerPatternLayers.Builder().addAll(layers).add(pattern, patternColor).build() ++ // CraftBukkit start ++ layers -> { ++ if (layers.layers().size() > 20) { ++ layers = new BannerPatternLayers(List.copyOf(layers.layers().subList(0, 20))); ++ } ++ return new BannerPatternLayers.Builder().addAll(layers).add(pattern, patternColor).build(); + } -+ return new BannerPatternLayers.Builder().addAll(bannerPatternLayers).add(pattern, dyeColor).build(); -+ } -+ // CraftBukkit end - ); ++ // CraftBukkit end + ); + } } - diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/MenuType.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/MenuType.java.patch index a5767492ae1b..b76d59df6740 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/MenuType.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/MenuType.java.patch @@ -4,7 +4,7 @@ public static final MenuType FURNACE = register("furnace", FurnaceMenu::new); public static final MenuType GRINDSTONE = register("grindstone", GrindstoneMenu::new); public static final MenuType HOPPER = register("hopper", HopperMenu::new); -- public static final MenuType LECTERN = register("lectern", (containerId, playerInventory) -> new LecternMenu(containerId)); +- public static final MenuType LECTERN = register("lectern", (containerId, inventory) -> new LecternMenu(containerId)); + public static final MenuType LECTERN = register("lectern", LecternMenu::new); // CraftBukkit public static final MenuType LOOM = register("loom", LoomMenu::new); public static final MenuType MERCHANT = register("merchant", MerchantMenu::new); diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/MerchantContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/MerchantContainer.java.patch index 043e9ad15879..94698cdaefeb 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/MerchantContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/MerchantContainer.java.patch @@ -34,15 +34,15 @@ + this.maxStack = size; + } + -+ public @javax.annotation.Nullable org.bukkit.inventory.InventoryHolder getOwner() { -+ return (this.merchant instanceof net.minecraft.world.entity.npc.villager.AbstractVillager abstractVillager) ? (org.bukkit.craftbukkit.entity.CraftAbstractVillager) abstractVillager.getBukkitEntity() : null; ++ public org.bukkit.inventory.@Nullable InventoryHolder getOwner() { ++ return (this.merchant instanceof net.minecraft.world.entity.npc.villager.AbstractVillager villager) ? (org.bukkit.craftbukkit.entity.CraftAbstractVillager) villager.getBukkitEntity() : null; + } + + @Override -+ public @javax.annotation.Nullable org.bukkit.Location getLocation() { -+ return (this.merchant instanceof net.minecraft.world.entity.npc.villager.AbstractVillager abstractVillager) ? abstractVillager.getBukkitEntity().getLocation() : null; // Paper - Fix inventories returning null Locations ++ public org.bukkit.@Nullable Location getLocation() { ++ return (this.merchant instanceof net.minecraft.world.entity.npc.villager.AbstractVillager villager) ? villager.getBukkitEntity().getLocation() : null; // Paper - Fix inventories returning null Locations + } + // CraftBukkit end - public MerchantContainer(Merchant merchant) { - this.merchant = merchant; + public MerchantContainer(final Merchant villager) { + this.merchant = villager; diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/MerchantMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/MerchantMenu.java.patch index fec640cad860..2fa9d6af0183 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/MerchantMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/MerchantMenu.java.patch @@ -5,7 +5,7 @@ private boolean showProgressBar; private boolean canRestock; + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.view.CraftMerchantView view = null; ++ private org.bukkit.craftbukkit.inventory.view.@org.jspecify.annotations.Nullable CraftMerchantView view = null; + private final Inventory inventory; + + @Override @@ -17,70 +17,70 @@ + } + // CraftBukkit end - public MerchantMenu(int containerId, Inventory playerInventory) { - this(containerId, playerInventory, new ClientSideMerchant(playerInventory.player)); + public MerchantMenu(final int containerId, final Inventory inventory) { + this(containerId, inventory, new ClientSideMerchant(inventory.player)); @@ -42,6 +_,7 @@ this.addSlot(new Slot(this.tradeContainer, 0, 136, 37)); this.addSlot(new Slot(this.tradeContainer, 1, 162, 37)); - this.addSlot(new MerchantResultSlot(playerInventory.player, trader, this.tradeContainer, 2, 220, 37)); -+ this.inventory = playerInventory; // CraftBukkit - this.addStandardInventorySlots(playerInventory, 108, 84); + this.addSlot(new MerchantResultSlot(inventory.player, merchant, this.tradeContainer, 2, 220, 37)); ++ this.inventory = inventory; // CraftBukkit + this.addStandardInventorySlots(inventory, 108, 84); } @@ -61,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!checkReachable) return true; // Paper - checkReachable return this.trader.stillValid(player); } @@ -105,12 +_,12 @@ - ItemStack item = slot.getItem(); - itemStack = item.copy(); + ItemStack stack = slot.getItem(); + clicked = stack.copy(); if (slotIndex == 2) { -- if (!this.moveItemStackTo(item, 3, 39, true)) { -+ if (!this.moveItemStackTo(item, 3, 39, true, true)) { // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent +- if (!this.moveItemStackTo(stack, 3, 39, true)) { ++ if (!this.moveItemStackTo(stack, 3, 39, true, true)) { // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent return ItemStack.EMPTY; } -- slot.onQuickCraft(item, itemStack); +- slot.onQuickCraft(stack, clicked); - this.playTradeSound(); -+ // slot.onQuickCraft(item, itemStack); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent; moved to after the non-check moveItemStackTo call ++ // slot.onQuickCraft(stack, clicked); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent; moved to after the non-check moveItemStackTo call + // this.playTradeSound(); } else if (slotIndex != 0 && slotIndex != 1) { if (slotIndex >= 3 && slotIndex < 30) { - if (!this.moveItemStackTo(item, 30, 39, false)) { + if (!this.moveItemStackTo(stack, 30, 39, false)) { @@ -123,6 +_,7 @@ return ItemStack.EMPTY; } + if (slotIndex != 2) { // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent; moved down for slot 2 - if (item.isEmpty()) { + if (stack.isEmpty()) { slot.setByPlayer(ItemStack.EMPTY); } else { @@ -134,13 +_,28 @@ } - slot.onTake(player, item); + slot.onTake(player, stack); + } // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent; handle slot 2 + if (slotIndex == 2) { // is merchant result slot -+ slot.onTake(player, item); -+ if (item.isEmpty()) { ++ slot.onTake(player, stack); ++ if (stack.isEmpty()) { + slot.set(ItemStack.EMPTY); + return ItemStack.EMPTY; + } + -+ this.moveItemStackTo(item, 3, 39, true, false); // This should always succeed because it's checked above ++ this.moveItemStackTo(stack, 3, 39, true, false); // This should always succeed because it's checked above + -+ slot.onQuickCraft(item, itemStack); ++ slot.onQuickCraft(stack, clicked); + this.playTradeSound(); + slot.set(ItemStack.EMPTY); // item should ALWAYS be empty + } + // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent } - return itemStack; + return clicked; } private void playTradeSound() { diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/MerchantResultSlot.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/MerchantResultSlot.java.patch index cc2a6f28f895..aefc85bb40fd 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/MerchantResultSlot.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/MerchantResultSlot.java.patch @@ -3,37 +3,37 @@ @@ -47,13 +_,34 @@ @Override - public void onTake(Player player, ItemStack stack) { -- this.checkTakeAchievements(stack); -+ // this.checkTakeAchievements(stack); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent; move to after event is called and not cancelled - MerchantOffer activeOffer = this.slots.getActiveOffer(); + public void onTake(final Player player, final ItemStack carried) { +- this.checkTakeAchievements(carried); ++ // this.checkTakeAchievements(carried); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent; move to after event is called and not cancelled + MerchantOffer offer = this.slots.getActiveOffer(); + // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent + io.papermc.paper.event.player.PlayerPurchaseEvent event = null; -+ if (activeOffer != null && player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { ++ if (offer != null && player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { + if (this.merchant instanceof net.minecraft.world.entity.npc.villager.AbstractVillager abstractVillager) { -+ event = new io.papermc.paper.event.player.PlayerTradeEvent(serverPlayer.getBukkitEntity(), (org.bukkit.entity.AbstractVillager) abstractVillager.getBukkitEntity(), activeOffer.asBukkit(), true, true); ++ event = new io.papermc.paper.event.player.PlayerTradeEvent(serverPlayer.getBukkitEntity(), (org.bukkit.entity.AbstractVillager) abstractVillager.getBukkitEntity(), offer.asBukkit(), true, true); + } else if (this.merchant instanceof org.bukkit.craftbukkit.inventory.CraftMerchantCustom.MinecraftMerchant minecraftMerchant) { -+ event = new io.papermc.paper.event.player.PlayerPurchaseEvent(serverPlayer.getBukkitEntity(), minecraftMerchant.getCraftMerchant(), activeOffer.asBukkit(), false, true); ++ event = new io.papermc.paper.event.player.PlayerPurchaseEvent(serverPlayer.getBukkitEntity(), minecraftMerchant.getCraftMerchant(), offer.asBukkit(), false, true); + } + if (event != null) { + if (!event.callEvent()) { -+ stack.setCount(0); ++ carried.setCount(0); + player.containerMenu.sendAllDataToRemote(); + int level = merchant instanceof net.minecraft.world.entity.npc.villager.Villager villager ? villager.getVillagerData().level() : 1; + serverPlayer.sendMerchantOffers(player.containerMenu.containerId, merchant.getOffers(), level, merchant.getVillagerXp(), merchant.showProgressBar(), merchant.canRestock()); + return; + } -+ activeOffer = org.bukkit.craftbukkit.inventory.CraftMerchantRecipe.fromBukkit(event.getTrade()).toMinecraft(); ++ offer = org.bukkit.craftbukkit.inventory.CraftMerchantRecipe.fromBukkit(event.getTrade()).toMinecraft(); + } + } -+ this.checkTakeAchievements(stack); ++ this.checkTakeAchievements(carried); + // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent - if (activeOffer != null) { - ItemStack item = this.slots.getItem(0); - ItemStack item1 = this.slots.getItem(1); - if (activeOffer.take(item, item1) || activeOffer.take(item1, item)) { -- this.merchant.notifyTrade(activeOffer); -+ this.merchant.processTrade(activeOffer, event); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent + if (offer != null) { + ItemStack buyA = this.slots.getItem(0); + ItemStack buyB = this.slots.getItem(1); + if (offer.take(buyA, buyB) || offer.take(buyB, buyA)) { +- this.merchant.notifyTrade(offer); ++ this.merchant.processTrade(offer, event); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent player.awardStat(Stats.TRADED_WITH_VILLAGER); - this.slots.setItem(0, item); - this.slots.setItem(1, item1); + this.slots.setItem(0, buyA); + this.slots.setItem(1, buyB); diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/PlayerEnderChestContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/PlayerEnderChestContainer.java.patch index e41f6d341dc6..1cb0ed3ae49c 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/PlayerEnderChestContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/PlayerEnderChestContainer.java.patch @@ -25,4 +25,4 @@ + // CraftBukkit end } - public void setActiveChest(EnderChestBlockEntity enderChestBlockEntity) { + public void setActiveChest(final EnderChestBlockEntity activeChest) { diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/ResultContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/ResultContainer.java.patch index 36e9f3a8cff2..0721adab0118 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/ResultContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/ResultContainer.java.patch @@ -22,7 +22,7 @@ + // Paper end - Add missing InventoryHolders + } + -+ // Don't need a transaction; the InventoryCrafting keeps track of it for us ++ // Don't need a transaction; the CraftingContainer keeps track of it for us + public void onOpen(org.bukkit.craftbukkit.entity.CraftHumanEntity player) {} + public void onClose(org.bukkit.craftbukkit.entity.CraftHumanEntity player) {} + public java.util.List getViewers() { diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/ResultSlot.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/ResultSlot.java.patch index 959b6d3d5e7e..e545b51623ec 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/ResultSlot.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/ResultSlot.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/inventory/ResultSlot.java +++ b/net/minecraft/world/inventory/ResultSlot.java @@ -39,6 +_,11 @@ - protected void onQuickCraft(ItemStack stack, int amount) { - this.removeCount += amount; - this.checkTakeAchievements(stack); + protected void onQuickCraft(final ItemStack picked, final int count) { + this.removeCount += count; + this.checkTakeAchievements(picked); + // Paper start - add ItemCraftedEvent -+ if (!stack.isEmpty()) { -+ new io.papermc.paper.event.inventory.ItemCraftedEvent((org.bukkit.entity.Player) this.player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(stack)).callEvent(); ++ if (!picked.isEmpty()) { ++ new io.papermc.paper.event.inventory.ItemCraftedEvent((org.bukkit.entity.Player) this.player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(picked)).callEvent(); + } + // Paper end - add ItemCraftedEvent } @Override @@ -72,7 +_,7 @@ - private NonNullList getRemainingItems(CraftingInput input, Level level) { + private NonNullList getRemainingItems(final CraftingInput input, final Level level) { return level instanceof ServerLevel serverLevel ? serverLevel.recipeAccess() - .getRecipeFor(RecipeType.CRAFTING, input, serverLevel) @@ -24,12 +24,12 @@ @@ -80,6 +_,11 @@ @Override - public void onTake(Player player, ItemStack stack) { + public void onTake(final Player player, final ItemStack carried) { + // Paper start - add ItemCraftedEvent -+ if (!stack.isEmpty()) { -+ new io.papermc.paper.event.inventory.ItemCraftedEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(stack)).callEvent(); ++ if (!carried.isEmpty()) { ++ new io.papermc.paper.event.inventory.ItemCraftedEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(carried)).callEvent(); + } + // Paper end - add ItemCraftedEvent - this.checkTakeAchievements(stack); - CraftingInput.Positioned positionedCraftInput = this.craftSlots.asPositionedCraftInput(); - CraftingInput craftingInput = positionedCraftInput.input(); + this.checkTakeAchievements(carried); + CraftingInput.Positioned positionedRecipe = this.craftSlots.asPositionedCraftInput(); + CraftingInput input = positionedRecipe.input(); diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/ShulkerBoxMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/ShulkerBoxMenu.java.patch index d5208fa9c81d..96369001f045 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/ShulkerBoxMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/ShulkerBoxMenu.java.patch @@ -5,7 +5,7 @@ private static final int CONTAINER_SIZE = 27; private final Container container; + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.CraftInventoryView view; ++ private org.bukkit.craftbukkit.inventory.@org.jspecify.annotations.Nullable CraftInventoryView view; + private final Inventory inventory; + + @Override @@ -24,22 +24,22 @@ + } + // CraftBukkit end - public ShulkerBoxMenu(int containerId, Inventory playerInventory) { - this(containerId, playerInventory, new SimpleContainer(27)); + public ShulkerBoxMenu(final int containerId, final Inventory inventory) { + this(containerId, inventory, new SimpleContainer(27)); @@ -18,7 +_,8 @@ super(MenuType.SHULKER_BOX, containerId); checkContainerSize(container, 27); this.container = container; -- container.startOpen(playerInventory.player); -+ this.inventory = playerInventory; // CraftBukkit -+ // container.startOpen(playerInventory.player); // Paper - don't startOpen until menu actually opens - int i = 3; - int i1 = 9; +- container.startOpen(inventory.player); ++ this.inventory = inventory; // CraftBukkit ++ // container.startOpen(inventory.player); // Paper - don't startOpen until menu actually opens + int rows = 3; + int columns = 9; @@ -33,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return this.container.stillValid(player); } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/SmithingMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/SmithingMenu.java.patch index afdbf864d4af..f768177b4717 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/SmithingMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/SmithingMenu.java.patch @@ -8,28 +8,28 @@ + private org.bukkit.craftbukkit.inventory.CraftInventoryView bukkitEntity; + // CraftBukkit end - public SmithingMenu(int containerId, Inventory playerInventory) { - this(containerId, playerInventory, ContainerLevelAccess.NULL); -@@ -100,6 +_,7 @@ - if (this.level instanceof ServerLevel) { - boolean flag = this.getSlot(0).hasItem() && this.getSlot(1).hasItem() && this.getSlot(2).hasItem() && !this.getSlot(this.getResultSlot()).hasItem(); - this.hasRecipeError.set(flag ? 1 : 0); + public SmithingMenu(final int containerId, final Inventory inventory) { + this(containerId, inventory, ContainerLevelAccess.NULL); +@@ -103,6 +_,7 @@ + && this.getSlot(2).hasItem() + && !this.getSlot(this.getResultSlot()).hasItem(); + this.hasRecipeError.set(hasRecipeError ? 1 : 0); + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareResultEvent(this, RESULT_SLOT); // Paper - Add PrepareResultEvent } } -@@ -116,7 +_,9 @@ - recipeFor.ifPresentOrElse(recipe -> { - ItemStack itemStack = recipe.value().assemble(smithingRecipeInput, this.level.registryAccess()); +@@ -119,7 +_,9 @@ + foundRecipe.ifPresentOrElse(recipe -> { + ItemStack result = recipe.value().assemble(input); this.resultSlots.setRecipeUsed((RecipeHolder)recipe); -- this.resultSlots.setItem(0, itemStack); +- this.resultSlots.setItem(0, result); + // CraftBukkit start -+ org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareSmithingEvent(this.getBukkitView(), itemStack); ++ org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareSmithingEvent(this.getBukkitView(), result); + // CraftBukkit end }, () -> { this.resultSlots.setRecipeUsed(null); this.resultSlots.setItem(0, ItemStack.EMPTY); -@@ -138,4 +_,18 @@ +@@ -141,4 +_,18 @@ public boolean hasRecipeError() { return this.hasRecipeError.get() > 0; } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/StonecutterMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/StonecutterMenu.java.patch index f0262737cac7..8513d6e3f342 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/StonecutterMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/StonecutterMenu.java.patch @@ -1,37 +1,42 @@ --- a/net/minecraft/world/inventory/StonecutterMenu.java +++ b/net/minecraft/world/inventory/StonecutterMenu.java -@@ -25,7 +_,7 @@ +@@ -26,7 +_,7 @@ private static final int USE_ROW_SLOT_START = 29; private static final int USE_ROW_SLOT_END = 38; private final ContainerLevelAccess access; -- final DataSlot selectedRecipeIndex = DataSlot.standalone(); -+ final DataSlot selectedRecipeIndex = DataSlot.shared(new int[1], 0); // Paper - Add PlayerStonecutterRecipeSelectEvent +- private final DataSlot selectedRecipeIndex = DataSlot.standalone(); ++ private final DataSlot selectedRecipeIndex = DataSlot.shared(new int[1], 0); // Paper - Add PlayerStonecutterRecipeSelectEvent private final Level level; private SelectableRecipe.SingleInputSet recipesForInput = SelectableRecipe.SingleInputSet.empty(); private ItemStack input = ItemStack.EMPTY; -@@ -33,15 +_,23 @@ +@@ -34,19 +_,23 @@ final Slot inputSlot; final Slot resultSlot; - Runnable slotUpdateListener = () -> {}; + private Runnable slotUpdateListener = () -> {}; - public final Container container = new SimpleContainer(1) { +- { +- Objects.requireNonNull(StonecutterMenu.this); +- } +- - @Override - public void setChanged() { - super.setChanged(); - StonecutterMenu.this.slotsChanged(this); - StonecutterMenu.this.slotUpdateListener.run(); +- } +- }; +- private final ResultContainer resultContainer = new ResultContainer(); + public final Container container; // Paper - Add missing InventoryHolders - move down -+ final ResultContainer resultContainer; // Paper - Add missing InventoryHolders - move down ++ private final ResultContainer resultContainer; // Paper - Add missing InventoryHolders - move down + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.view.CraftStonecutterView view = null; ++ private org.bukkit.craftbukkit.inventory.view.@org.jspecify.annotations.Nullable CraftStonecutterView view = null; + private final org.bukkit.entity.Player player; + + @Override + public org.bukkit.craftbukkit.inventory.view.CraftStonecutterView getBukkitView() { + if (this.view != null) { + return this.view; - } -- }; -- final ResultContainer resultContainer = new ResultContainer(); ++ } + + org.bukkit.craftbukkit.inventory.CraftInventoryStonecutter inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryStonecutter(this.container, this.resultContainer); + this.view = new org.bukkit.craftbukkit.inventory.view.CraftStonecutterView(this.player, inventory, this); @@ -39,12 +44,12 @@ + } + // CraftBukkit end - public StonecutterMenu(int containerId, Inventory playerInventory) { - this(containerId, playerInventory, ContainerLevelAccess.NULL); -@@ -51,6 +_,23 @@ + public StonecutterMenu(final int containerId, final Inventory inventory) { + this(containerId, inventory, ContainerLevelAccess.NULL); +@@ -56,6 +_,23 @@ super(MenuType.STONECUTTER, containerId); this.access = access; - this.level = playerInventory.player.level(); + this.level = inventory.player.level(); + // Paper start + this.container = new SimpleContainer(this.createBlockHolder(access), 1) { // Paper - Add missing InventoryHolders + @Override @@ -64,35 +69,35 @@ + // Paper end this.inputSlot = this.addSlot(new Slot(this.container, 0, 20, 33)); this.resultSlot = this.addSlot(new Slot(this.resultContainer, 1, 143, 33) { - @Override -@@ -83,6 +_,7 @@ + { +@@ -92,6 +_,7 @@ }); - this.addStandardInventorySlots(playerInventory, 8, 84); + this.addStandardInventorySlots(inventory, 8, 84); this.addDataSlot(this.selectedRecipeIndex); -+ this.player = (org.bukkit.entity.Player) playerInventory.player.getBukkitEntity(); // CraftBukkit ++ this.player = (org.bukkit.entity.Player) inventory.player.getBukkitEntity(); // CraftBukkit } public int getSelectedRecipeIndex() { -@@ -103,6 +_,7 @@ +@@ -112,6 +_,7 @@ @Override - public boolean stillValid(Player player) { + public boolean stillValid(final Player player) { + if (!this.checkReachable) return true; // CraftBukkit return stillValid(this.access, player, Blocks.STONECUTTER); } -@@ -112,8 +_,34 @@ +@@ -121,8 +_,34 @@ return false; } else { - if (this.isValidRecipeIndex(id)) { -- this.selectedRecipeIndex.set(id); -- this.setupResultSlot(id); + if (this.isValidRecipeIndex(buttonId)) { +- this.selectedRecipeIndex.set(buttonId); +- this.setupResultSlot(buttonId); + // Paper start - Add PlayerStonecutterRecipeSelectEvent -+ int recipeIndex = id; ++ int recipeIndex = buttonId; + this.selectedRecipeIndex.set(recipeIndex); + this.selectedRecipeIndex.checkAndClearUpdateFlag(); // mark as changed -+ paperEventBlock: if (this.isValidRecipeIndex(id)) { -+ final Optional> recipe = this.recipesForInput.entries().get(id).recipe().recipe(); ++ paperEventBlock: if (this.isValidRecipeIndex(buttonId)) { ++ final Optional> recipe = this.recipesForInput.entries().get(buttonId).recipe().recipe(); + if (recipe.isEmpty()) break paperEventBlock; // The recipe selected does not have an actual server recipe (presumably its the empty one). Cannot call the event, just break. + + io.papermc.paper.event.player.PlayerStonecutterRecipeSelectEvent event = new io.papermc.paper.event.player.PlayerStonecutterRecipeSelectEvent((org.bukkit.entity.Player) player.getBukkitEntity(), getBukkitView().getTopInventory(), (org.bukkit.inventory.StonecuttingRecipe) recipe.get().toBukkitRecipe()); @@ -118,11 +123,11 @@ } return true; -@@ -131,6 +_,7 @@ - this.input = item.copy(); - this.setupRecipeList(item); +@@ -140,6 +_,7 @@ + this.input = input.copy(); + this.setupRecipeList(input); } + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareResultEvent(this, RESULT_SLOT); // Paper - Add PrepareResultEvent } - private void setupRecipeList(ItemStack stack) { + private void setupRecipeList(final ItemStack item) { diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/TransientCraftingContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/TransientCraftingContainer.java.patch index cded5ee713a4..c0c5b5924079 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/TransientCraftingContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/TransientCraftingContainer.java.patch @@ -6,7 +6,7 @@ + // CraftBukkit start - add fields + public List transaction = new java.util.ArrayList<>(); -+ private net.minecraft.world.item.crafting.RecipeHolder currentRecipe; ++ private net.minecraft.world.item.crafting.@org.jspecify.annotations.Nullable RecipeHolder currentRecipe; + public net.minecraft.world.Container resultInventory; + private Player owner; + private int maxStack = MAX_STACK; @@ -32,7 +32,7 @@ + } + + @Override -+ public @javax.annotation.Nullable org.bukkit.inventory.InventoryHolder getOwner() { ++ public org.bukkit.inventory.@org.jspecify.annotations.Nullable InventoryHolder getOwner() { + return (this.owner == null) ? null : this.owner.getBukkitEntity(); + } + @@ -53,12 +53,12 @@ + } + + @Override -+ public net.minecraft.world.item.crafting.RecipeHolder getCurrentRecipe() { ++ public net.minecraft.world.item.crafting.@org.jspecify.annotations.Nullable RecipeHolder getCurrentRecipe() { + return this.currentRecipe; + } + + @Override -+ public void setCurrentRecipe(net.minecraft.world.item.crafting.RecipeHolder currentRecipe) { ++ public void setCurrentRecipe(net.minecraft.world.item.crafting.@org.jspecify.annotations.Nullable RecipeHolder currentRecipe) { + this.currentRecipe = currentRecipe; + } + @@ -68,6 +68,6 @@ + } + // CraftBukkit end + - public TransientCraftingContainer(AbstractContainerMenu menu, int width, int height) { + public TransientCraftingContainer(final AbstractContainerMenu menu, final int width, final int height) { this(menu, width, height, NonNullList.withSize(width * height, ItemStack.EMPTY)); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/ArmorStandItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ArmorStandItem.java.patch index fe9ce9ce0727..2d44e2ce4371 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ArmorStandItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ArmorStandItem.java.patch @@ -2,14 +2,14 @@ +++ b/net/minecraft/world/item/ArmorStandItem.java @@ -45,6 +_,12 @@ - float f = Mth.floor((Mth.wrapDegrees(context.getRotation() - 180.0F) + 22.5F) / 45.0F) * 45.0F; - armorStand.snapTo(armorStand.getX(), armorStand.getY(), armorStand.getZ(), f, 0.0F); + float yRot = Mth.floor((Mth.wrapDegrees(context.getRotation() - 180.0F) + 22.5F) / 45.0F) * 45.0F; + entity.snapTo(entity.getX(), entity.getY(), entity.getZ(), yRot, 0.0F); + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(context, armorStand).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(context, entity).isCancelled()) { + if (context.getPlayer() != null) context.getPlayer().containerMenu.sendAllDataToRemote(); // Paper - Fix inventory desync + return InteractionResult.FAIL; + } + // CraftBukkit end - serverLevel.addFreshEntityWithPassengers(armorStand); - level.playSound( - null, armorStand.getX(), armorStand.getY(), armorStand.getZ(), SoundEvents.ARMOR_STAND_PLACE, SoundSource.BLOCKS, 0.75F, 0.8F + serverLevel.addFreshEntityWithPassengers(entity); + level.playSound(null, entity.getX(), entity.getY(), entity.getZ(), SoundEvents.ARMOR_STAND_PLACE, SoundSource.BLOCKS, 0.75F, 0.8F); + entity.gameEvent(GameEvent.ENTITY_PLACE, context.getPlayer()); diff --git a/paper-server/patches/sources/net/minecraft/world/item/AxeItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/AxeItem.java.patch index cb0c66dc7f0f..167b36275867 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/AxeItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/AxeItem.java.patch @@ -5,10 +5,10 @@ } else { ItemStack itemInHand = context.getItemInHand(); + // Paper start - EntityChangeBlockEvent -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, clickedPos, optional.get())) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, newBlock.get())) { + return InteractionResult.PASS; + } + // Paper end if (player instanceof ServerPlayer) { - CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger((ServerPlayer)player, clickedPos, itemInHand); + CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger((ServerPlayer)player, pos, itemInHand); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch index 52cf1527e917..b528d26d3d34 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch @@ -1,83 +1,75 @@ --- a/net/minecraft/world/item/BlockItem.java +++ b/net/minecraft/world/item/BlockItem.java -@@ -57,6 +_,14 @@ +@@ -56,6 +_,7 @@ return InteractionResult.FAIL; } else { - BlockState placementState = this.getPlacementState(blockPlaceContext); -+ // CraftBukkit start - special case for handling block placement with water lilies and snow buckets -+ org.bukkit.block.BlockState bukkitState = null; -+ if (this instanceof PlaceOnWaterBlockItem || this instanceof SolidBucketItem) { -+ bukkitState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockPlaceContext.getLevel(), blockPlaceContext.getClickedPos()); -+ } -+ final org.bukkit.block.BlockState oldBukkitState = bukkitState != null ? bukkitState : org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockPlaceContext.getLevel(), blockPlaceContext.getClickedPos()); // Paper - Reset placed block on exception -+ // CraftBukkit end -+ + BlockState placementState = this.getPlacementState(updatedPlaceContext); ++ final org.bukkit.block.BlockState previousState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(updatedPlaceContext.getLevel(), updatedPlaceContext.getClickedPos()); // Paper - Reset placed block on exception if (placementState == null) { return InteractionResult.FAIL; - } else if (!this.placeBlock(blockPlaceContext, placementState)) { -@@ -69,15 +_,39 @@ - BlockState blockState = level.getBlockState(clickedPos); - if (blockState.is(placementState.getBlock())) { - blockState = this.updateBlockStateFromTag(clickedPos, level, itemInHand, blockState); + } else if (!this.placeBlock(updatedPlaceContext, placementState)) { +@@ -68,21 +_,45 @@ + BlockState placedState = level.getBlockState(pos); + if (placedState.is(placementState.getBlock())) { + placedState = this.updateBlockStateFromTag(pos, level, itemStack, placedState); + // Paper start - Reset placed block on exception + try { - this.updateCustomBlockEntityTag(clickedPos, level, player, itemInHand, blockState); - updateBlockEntityComponents(level, clickedPos, itemInHand); + this.updateCustomBlockEntityTag(pos, level, player, itemStack, placedState); + updateBlockEntityComponents(level, pos, itemStack); + } catch (Exception ex) { -+ ((org.bukkit.craftbukkit.block.CraftBlockState) oldBukkitState).revertPlace(); ++ ((org.bukkit.craftbukkit.block.CraftBlockState) previousState).revertPlace(); + if (player instanceof ServerPlayer serverPlayer) { -+ org.apache.logging.log4j.LogManager.getLogger().error("Player {} tried placing invalid block", player.getScoreboardName(), ex); -+ serverPlayer.getBukkitEntity().kickPlayer("Packet processing error"); ++ net.minecraft.server.MinecraftServer.LOGGER.warn("Player {} tried placing invalid block", player.getScoreboardName(), ex); ++ serverPlayer.connection.disconnect(net.minecraft.network.chat.Component.literal("Packet processing error")); + return InteractionResult.FAIL; + } + throw ex; // Rethrow exception if not placed by a player + } + // Paper end - Reset placed block on exception - blockState.getBlock().setPlacedBy(level, clickedPos, blockState, player, itemInHand); -+ // CraftBukkit start -+ if (bukkitState != null) { -+ org.bukkit.event.block.BlockPlaceEvent placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent((net.minecraft.server.level.ServerLevel) level, player, blockPlaceContext.getHand(), bukkitState, clickedPos); -+ if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) { -+ ((org.bukkit.craftbukkit.block.CraftBlockState) bukkitState).revertPlace(); + placedState.getBlock().setPlacedBy(level, pos, placedState, player, itemStack); ++ // CraftBukkit start - special case for handling block placement with water lilies, frog spawn and snow buckets ++ if (player != null && (this instanceof PlaceOnWaterBlockItem || this instanceof SolidBucketItem)) { ++ org.bukkit.event.block.BlockPlaceEvent placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent((net.minecraft.server.level.ServerLevel) level, player, updatedPlaceContext.getHand(), previousState, pos); ++ if (placeEvent.isCancelled() || !placeEvent.canBuild()) { ++ ((org.bukkit.craftbukkit.block.CraftBlockState) previousState).revertPlace(); + -+ player.containerMenu.forceHeldSlot(blockPlaceContext.getHand()); ++ player.containerMenu.forceHeldSlot(updatedPlaceContext.getHand()); + return InteractionResult.FAIL; + } + } + // CraftBukkit end if (player instanceof ServerPlayer) { - CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer)player, clickedPos, itemInHand); + CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer)player, pos, itemStack); } } - SoundType soundType = blockState.getSoundType(); + SoundType soundType = placedState.getSoundType(); + if (player == null) // Paper - Fix block place logic; reintroduce this for the dispenser (i.e the shulker) level.playSound( - player, - clickedPos, -@@ -88,7 +_,7 @@ + player, pos, this.getPlaceSound(placedState), SoundSource.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F ); - level.gameEvent(GameEvent.BLOCK_PLACE, clickedPos, GameEvent.Context.of(player, blockState)); - itemInHand.consume(1, player); + level.gameEvent(GameEvent.BLOCK_PLACE, pos, GameEvent.Context.of(player, placedState)); + itemStack.consume(1, player); - return InteractionResult.SUCCESS; -+ return InteractionResult.SUCCESS.configurePaper(e -> e.placedBlockAt(clickedPos.immutable())); // Paper - track placed block position from block item ++ return InteractionResult.SUCCESS.configurePaper(e -> e.placedBlockAt(pos.immutable())); // Paper - track placed block position from block item } } } -@@ -135,8 +_,19 @@ +@@ -131,8 +_,20 @@ - protected boolean canPlace(BlockPlaceContext context, BlockState state) { + protected boolean canPlace(final BlockPlaceContext context, final BlockState stateForPlacement) { Player player = context.getPlayer(); -- return (!this.mustSurvive() || state.canSurvive(context.getLevel(), context.getClickedPos())) -- && context.getLevel().isUnobstructed(state, context.getClickedPos(), CollisionContext.placementContext(player)); +- return (!this.mustSurvive() || stateForPlacement.canSurvive(context.getLevel(), context.getClickedPos())) +- && context.getLevel().isUnobstructed(stateForPlacement, context.getClickedPos(), CollisionContext.placementContext(player)); + // CraftBukkit start + Level world = context.getLevel(); // Paper - Cancel hit for vanished players -+ boolean canBuild = (!this.mustSurvive() || state.canSurvive(world, context.getClickedPos())) && world.checkEntityCollision(state, player, CollisionContext.placementContext(player), context.getClickedPos(), true); // Paper - Cancel hit for vanished players ++ boolean canBuild = (!this.mustSurvive() || stateForPlacement.canSurvive(world, context.getClickedPos())) ++ && world.checkEntityCollision(stateForPlacement, player, CollisionContext.placementContext(player), context.getClickedPos(), true); // Paper - Cancel hit for vanished players + org.bukkit.entity.Player bukkitPlayer = (context.getPlayer() instanceof ServerPlayer) ? (org.bukkit.entity.Player) context.getPlayer().getBukkitEntity() : null; + + org.bukkit.event.block.BlockCanBuildEvent event = new org.bukkit.event.block.BlockCanBuildEvent( + org.bukkit.craftbukkit.block.CraftBlock.at(world, context.getClickedPos()), bukkitPlayer, -+ org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(state), canBuild, org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(context.getHand()) ++ stateForPlacement.asBlockData(), canBuild, org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(context.getHand()) + ); + world.getCraftServer().getPluginManager().callEvent(event); + @@ -86,12 +78,12 @@ } protected boolean mustSurvive() { -@@ -160,7 +_,7 @@ +@@ -156,7 +_,7 @@ return false; } - if (!type.onlyOpCanSetNbt() || player != null && player.canUseGameMasterBlocks()) { + if (!type.onlyOpCanSetNbt() || player != null && (player.canUseGameMasterBlocks() || (player.getAbilities().instabuild && player.getBukkitEntity().hasPermission("minecraft.nbt.place")))) { // Spigot - add permission - return typedEntityData.loadInto(blockEntity, level.registryAccess()); + return customData.loadInto(blockEntity, level.registryAccess()); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/BoatItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/BoatItem.java.patch index ebd369c1cf4c..2c9c26a6af7c 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/BoatItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/BoatItem.java.patch @@ -2,25 +2,25 @@ +++ b/net/minecraft/world/item/BoatItem.java @@ -30,7 +_,7 @@ @Override - public InteractionResult use(Level level, Player player, InteractionHand hand) { - ItemStack itemInHand = player.getItemInHand(hand); -- HitResult playerPovHitResult = getPlayerPOVHitResult(level, player, ClipContext.Fluid.ANY); -+ net.minecraft.world.phys.BlockHitResult playerPovHitResult = getPlayerPOVHitResult(level, player, ClipContext.Fluid.ANY); // Paper - if (playerPovHitResult.getType() == HitResult.Type.MISS) { + public InteractionResult use(final Level level, final Player player, final InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); +- HitResult hitResult = getPlayerPOVHitResult(level, player, ClipContext.Fluid.ANY); ++ net.minecraft.world.phys.BlockHitResult hitResult = getPlayerPOVHitResult(level, player, ClipContext.Fluid.ANY); // Paper + if (hitResult.getType() == HitResult.Type.MISS) { return InteractionResult.PASS; } else { @@ -51,6 +_,13 @@ } - if (playerPovHitResult.getType() == HitResult.Type.BLOCK) { + if (hitResult.getType() == HitResult.Type.BLOCK) { + // CraftBukkit start - Boat placement -+ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(player, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, playerPovHitResult.getBlockPos(), playerPovHitResult.getDirection(), itemInHand, false, hand, playerPovHitResult.getLocation()); ++ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(player, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, hitResult.getBlockPos(), hitResult.getDirection(), itemStack, false, hand, hitResult.getLocation()); + + if (event.isCancelled()) { + return InteractionResult.PASS; + } + // CraftBukkit end - AbstractBoat boat = this.getBoat(level, playerPovHitResult, itemInHand, player); + AbstractBoat boat = this.getBoat(level, hitResult, itemStack, player); if (boat == null) { return InteractionResult.FAIL; @@ -60,7 +_,15 @@ @@ -29,7 +29,7 @@ if (!level.isClientSide()) { - level.addFreshEntity(boat); + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(level, playerPovHitResult.getBlockPos(), player.getDirection(), player, boat, hand).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(level, hitResult.getBlockPos(), player.getDirection(), player, boat, hand).isCancelled()) { + return InteractionResult.FAIL; + } + @@ -37,6 +37,6 @@ + return InteractionResult.PASS; + } + // CraftBukkit end - level.gameEvent(player, GameEvent.ENTITY_PLACE, playerPovHitResult.getLocation()); - itemInHand.consume(1, player); + level.gameEvent(player, GameEvent.ENTITY_PLACE, hitResult.getLocation()); + itemStack.consume(1, player); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/BoneMealItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/BoneMealItem.java.patch index 71411b0ac61e..bdae8c85b266 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/BoneMealItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/BoneMealItem.java.patch @@ -3,29 +3,29 @@ @@ -35,13 +_,18 @@ @Override - public InteractionResult useOn(UseOnContext context) { + public InteractionResult useOn(final UseOnContext context) { + // CraftBukkit start - extract bonemeal application logic to separate, static method + return BoneMealItem.applyBonemeal(context); + } -+ public static InteractionResult applyBonemeal(UseOnContext context) { ++ public static InteractionResult applyBonemeal(final UseOnContext context) { + // CraftBukkit end Level level = context.getLevel(); - BlockPos clickedPos = context.getClickedPos(); - BlockPos blockPos = clickedPos.relative(context.getClickedFace()); - ItemStack itemInHand = context.getItemInHand(); - if (growCrop(itemInHand, level, clickedPos)) { + BlockPos pos = context.getClickedPos(); + BlockPos relative = pos.relative(context.getClickedFace()); + ItemStack boneMealStack = context.getItemInHand(); + if (growCrop(boneMealStack, level, pos)) { if (!level.isClientSide()) { -- itemInHand.causeUseVibration(context.getPlayer(), GameEvent.ITEM_INTERACT_FINISH); -+ if (context.getPlayer() != null) itemInHand.causeUseVibration(context.getPlayer(), GameEvent.ITEM_INTERACT_FINISH); // CraftBukkit - SPIGOT-7518 - level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_PLANT_GROWTH, clickedPos, 15); - } - -@@ -51,7 +_,7 @@ - boolean isFaceSturdy = blockState.isFaceSturdy(level, clickedPos, context.getClickedFace()); - if (isFaceSturdy && growWaterPlant(itemInHand, level, blockPos, context.getClickedFace())) { +- boneMealStack.causeUseVibration(context.getPlayer(), GameEvent.ITEM_INTERACT_FINISH); ++ if (context.getPlayer() != null) boneMealStack.causeUseVibration(context.getPlayer(), GameEvent.ITEM_INTERACT_FINISH); // CraftBukkit - SPIGOT-7518 + level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_PLANT_GROWTH, pos, 15); + return InteractionResult.SUCCESS_SERVER; + } else { +@@ -52,7 +_,7 @@ + boolean solidBlockFace = clickedState.isFaceSturdy(level, pos, context.getClickedFace()); + if (solidBlockFace && growWaterPlant(boneMealStack, level, relative, context.getClickedFace())) { if (!level.isClientSide()) { -- itemInHand.causeUseVibration(context.getPlayer(), GameEvent.ITEM_INTERACT_FINISH); -+ if (context.getPlayer() != null) itemInHand.causeUseVibration(context.getPlayer(), GameEvent.ITEM_INTERACT_FINISH); // CraftBukkit - SPIGOT-7518 - level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_PLANT_GROWTH, blockPos, 15); +- boneMealStack.causeUseVibration(context.getPlayer(), GameEvent.ITEM_INTERACT_FINISH); ++ if (context.getPlayer() != null) boneMealStack.causeUseVibration(context.getPlayer(), GameEvent.ITEM_INTERACT_FINISH); // CraftBukkit - SPIGOT-7518 + level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_PLANT_GROWTH, relative, 15); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/BowItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/BowItem.java.patch index 52bde9e226af..6fde4d9f0c9e 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/BowItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/BowItem.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/item/BowItem.java @@ -38,7 +_,7 @@ } else { - List list = draw(stack, projectile, player); - if (level instanceof ServerLevel serverLevel && !list.isEmpty()) { -- this.shoot(serverLevel, player, player.getUsedItemHand(), stack, list, powerForTime * 3.0F, 1.0F, powerForTime == 1.0F, null); -+ this.shoot(serverLevel, player, player.getUsedItemHand(), stack, list, powerForTime * 3.0F, 1.0F, powerForTime == 1.0F, null, powerForTime); // Paper - Pass draw strength + List firedProjectiles = draw(itemStack, projectile, player); + if (level instanceof ServerLevel serverLevel && !firedProjectiles.isEmpty()) { +- this.shoot(serverLevel, player, player.getUsedItemHand(), itemStack, firedProjectiles, pow * 3.0F, 1.0F, pow == 1.0F, null); ++ this.shoot(serverLevel, player, player.getUsedItemHand(), itemStack, firedProjectiles, pow * 3.0F, 1.0F, pow == 1.0F, null, pow); // Paper - Pass draw strength } level.playSound( diff --git a/paper-server/patches/sources/net/minecraft/world/item/BucketItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/BucketItem.java.patch index 534428c9d894..bcc7764e4ea5 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/BucketItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/BucketItem.java.patch @@ -1,50 +1,50 @@ --- a/net/minecraft/world/item/BucketItem.java +++ b/net/minecraft/world/item/BucketItem.java -@@ -31,6 +_,7 @@ +@@ -32,6 +_,7 @@ import org.jspecify.annotations.Nullable; public class BucketItem extends Item implements DispensibleContainerItem { + private static @Nullable ItemStack itemLeftInHandAfterPlayerBucketEmptyEvent = null; // Paper - Fix PlayerBucketEmptyEvent result itemstack public final Fluid content; - public BucketItem(Fluid content, Item.Properties properties) { -@@ -57,12 +_,22 @@ + public BucketItem(final Fluid content, final Item.Properties properties) { +@@ -56,12 +_,22 @@ } else if (this.content == Fluids.EMPTY) { - BlockState blockState = level.getBlockState(blockPos); - if (blockState.getBlock() instanceof BucketPickup bucketPickup) { + BlockState blockState = level.getBlockState(pos); + if (blockState.getBlock() instanceof BucketPickup bucketPickupBlock) { + // CraftBukkit start -+ ItemStack dummyFluid = bucketPickup.pickupBlock(player, org.bukkit.craftbukkit.util.DummyGeneratorAccess.INSTANCE, blockPos, blockState); ++ ItemStack dummyFluid = bucketPickupBlock.pickupBlock(player, org.bukkit.craftbukkit.util.DummyLevelAccessor.INSTANCE, pos, blockState); + if (dummyFluid.isEmpty()) return InteractionResult.FAIL; // Don't fire event if the bucket won't be filled. -+ org.bukkit.event.player.PlayerBucketFillEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(level, player, blockPos, blockPos, playerPovHitResult.getDirection(), itemInHand, dummyFluid.getItem(), hand); ++ org.bukkit.event.player.PlayerBucketFillEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent(level, player, pos, pos, hitResult.getDirection(), itemStack, dummyFluid.getItem(), hand); + + if (event.isCancelled()) { + player.containerMenu.sendAllDataToRemote(); // SPIGOT-4541 + return InteractionResult.FAIL; + } + // CraftBukkit end - ItemStack itemStack = bucketPickup.pickupBlock(player, level, blockPos, blockState); - if (!itemStack.isEmpty()) { + ItemStack taken = bucketPickupBlock.pickupBlock(player, level, pos, blockState); + if (!taken.isEmpty()) { player.awardStat(Stats.ITEM_USED.get(this)); - bucketPickup.getPickupSound().ifPresent(sound -> player.playSound(sound, 1.0F, 1.0F)); - level.gameEvent(player, GameEvent.FLUID_PICKUP, blockPos); -- ItemStack itemStack1 = ItemUtils.createFilledResult(itemInHand, player, itemStack); -+ ItemStack itemStack1 = ItemUtils.createFilledResult(itemInHand, player, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack())); // CraftBukkit + bucketPickupBlock.getPickupSound().ifPresent(soundEvent -> player.playSound(soundEvent, 1.0F, 1.0F)); + level.gameEvent(player, GameEvent.FLUID_PICKUP, pos); +- ItemStack result = ItemUtils.createFilledResult(itemStack, player, taken); ++ ItemStack result = ItemUtils.createFilledResult(itemStack, player, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack())); // CraftBukkit if (!level.isClientSide()) { - CriteriaTriggers.FILLED_BUCKET.trigger((ServerPlayer)player, itemStack); + CriteriaTriggers.FILLED_BUCKET.trigger((ServerPlayer)player, taken); } -@@ -75,7 +_,7 @@ +@@ -74,7 +_,7 @@ } else { - BlockState blockState = level.getBlockState(blockPos); - BlockPos blockPos2 = blockState.getBlock() instanceof LiquidBlockContainer && this.content == Fluids.WATER ? blockPos : blockPos1; -- if (this.emptyContents(player, level, blockPos2, playerPovHitResult)) { -+ if (this.emptyContents(player, level, blockPos2, playerPovHitResult, playerPovHitResult.getDirection(), blockPos, itemInHand, hand)) { // CraftBukkit - this.checkExtraContent(player, level, itemInHand, blockPos2); + BlockState clicked = level.getBlockState(pos); + BlockPos placePos = clicked.getBlock() instanceof LiquidBlockContainer && this.content == Fluids.WATER ? pos : directionOffsetPos; +- if (this.emptyContents(player, level, placePos, hitResult)) { ++ if (this.emptyContents(player, level, placePos, hitResult, hitResult.getDirection(), pos, itemStack, hand)) { // CraftBukkit + this.checkExtraContent(player, level, itemStack, placePos); if (player instanceof ServerPlayer) { - CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer)player, blockPos2, itemInHand); -@@ -92,6 +_,13 @@ + CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer)player, placePos, itemStack); +@@ -91,6 +_,13 @@ } - public static ItemStack getEmptySuccessItem(ItemStack bucketStack, Player player) { + public static ItemStack getEmptySuccessItem(final ItemStack itemStack, final Player player) { + // Paper start - Fix PlayerBucketEmptyEvent result itemstack + if (itemLeftInHandAfterPlayerBucketEmptyEvent != null) { + ItemStack itemInHand = itemLeftInHandAfterPlayerBucketEmptyEvent; @@ -52,29 +52,29 @@ + return itemInHand; + } + // Paper end - Fix PlayerBucketEmptyEvent result itemstack - return !player.hasInfiniteMaterials() ? new ItemStack(Items.BUCKET) : bucketStack; + return !player.hasInfiniteMaterials() ? new ItemStack(Items.BUCKET) : itemStack; } -@@ -101,6 +_,12 @@ +@@ -100,6 +_,12 @@ @Override - public boolean emptyContents(@Nullable LivingEntity entity, Level level, BlockPos pos, @Nullable BlockHitResult hitResult) { + public boolean emptyContents(final @Nullable LivingEntity user, final Level level, final BlockPos pos, final @Nullable BlockHitResult hitResult) { + // CraftBukkit start -+ return this.emptyContents(entity, level, pos, hitResult, null, null, null, InteractionHand.MAIN_HAND); ++ return this.emptyContents(user, level, pos, hitResult, null, null, null, InteractionHand.MAIN_HAND); + } + -+ public boolean emptyContents(@Nullable LivingEntity entity, Level level, BlockPos pos, @Nullable BlockHitResult hitResult, Direction direction, BlockPos clicked, ItemStack itemstack, InteractionHand hand) { ++ public boolean emptyContents(final @Nullable LivingEntity user, final Level level, final BlockPos pos, final @Nullable BlockHitResult hitResult, final Direction direction, final BlockPos clicked, final ItemStack itemStack, final InteractionHand hand) { + // CraftBukkit end if (!(this.content instanceof FlowingFluid flowingFluid)) { return false; } else { -@@ -112,8 +_,18 @@ - || block instanceof LiquidBlockContainer liquidBlockContainer - && liquidBlockContainer.canPlaceLiquid(entity, level, pos, blockState, this.content); - boolean flag2 = blockState.isAir() || flag1 && (!flag || hitResult == null); +@@ -110,8 +_,18 @@ + boolean placeLiquid = mayReplace + || block instanceof LiquidBlockContainer container && container.canPlaceLiquid(user, level, pos, blockState, this.content); + boolean canPlaceFluidInsideBlock = blockState.isAir() || placeLiquid && (!shiftKeyDown || hitResult == null); + // CraftBukkit start -+ if (flag2 && entity instanceof Player player) { -+ org.bukkit.event.player.PlayerBucketEmptyEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketEmptyEvent(level, player, pos, clicked, direction, itemstack, hand); ++ if (canPlaceFluidInsideBlock && user instanceof Player player) { ++ org.bukkit.event.player.PlayerBucketEmptyEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketEmptyEvent(level, player, pos, clicked, direction, itemStack, hand); + if (event.isCancelled()) { + player.containerMenu.sendAllDataToRemote(); // SPIGOT-4541 + return false; @@ -82,9 +82,9 @@ + itemLeftInHandAfterPlayerBucketEmptyEvent = event.getItemStack() != null ? event.getItemStack().equals(org.bukkit.craftbukkit.inventory.CraftItemStack.asNewCraftStack(net.minecraft.world.item.Items.BUCKET)) ? null : org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY; // Paper - Fix PlayerBucketEmptyEvent result itemstack + } + // CraftBukkit end - if (!flag2) { -- return hitResult != null && this.emptyContents(entity, level, hitResult.getBlockPos().relative(hitResult.getDirection()), null); -+ return hitResult != null && this.emptyContents(entity, level, hitResult.getBlockPos().relative(hitResult.getDirection()), null, direction, clicked, itemstack, hand); // CraftBukkit + if (!canPlaceFluidInsideBlock) { +- return hitResult != null && this.emptyContents(user, level, hitResult.getBlockPos().relative(hitResult.getDirection()), null); ++ return hitResult != null && this.emptyContents(user, level, hitResult.getBlockPos().relative(hitResult.getDirection()), null, direction, clicked, itemStack, hand); // CraftBukkit } else if (level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos) && this.content.is(FluidTags.WATER)) { int x = pos.getX(); int y = pos.getY(); diff --git a/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch index a68d47e47dca..de368c09a9c5 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch @@ -1,56 +1,56 @@ --- a/net/minecraft/world/item/CrossbowItem.java +++ b/net/minecraft/world/item/CrossbowItem.java @@ -88,8 +_,15 @@ - return getPowerForTime(i, stack, entity) >= 1.0F && isCharged(stack); + return getPowerForTime(timeHeld, itemStack, entity) >= 1.0F && isCharged(itemStack); } + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Add EntityLoadCrossbowEvent - private static boolean tryLoadProjectiles(LivingEntity shooter, ItemStack crossbowStack) { -- List list = draw(crossbowStack, shooter.getProjectile(crossbowStack), shooter); + private static boolean tryLoadProjectiles(final LivingEntity shooter, final ItemStack heldItem) { +- List drawn = draw(heldItem, shooter.getProjectile(heldItem), shooter); + // Paper start - Add EntityLoadCrossbowEvent -+ return tryLoadProjectiles(shooter, crossbowStack, true); ++ return tryLoadProjectiles(shooter, heldItem, true); + } + -+ private static boolean tryLoadProjectiles(LivingEntity shooter, ItemStack crossbowStack, boolean consume) { -+ List list = draw(crossbowStack, shooter.getProjectile(crossbowStack), shooter, consume); ++ private static boolean tryLoadProjectiles(final LivingEntity shooter, final ItemStack heldItem, final boolean consume) { ++ List drawn = draw(heldItem, shooter.getProjectile(heldItem), shooter, consume); + // Paper end - Add EntityLoadCrossbowEvent - if (!list.isEmpty()) { - crossbowStack.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.of(list)); + if (!drawn.isEmpty()) { + heldItem.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.ofNonEmpty(drawn)); return true; -@@ -141,7 +_,11 @@ - @Override - protected Projectile createProjectile(Level level, LivingEntity shooter, ItemStack weapon, ItemStack ammo, boolean isCrit) { - if (ammo.is(Items.FIREWORK_ROCKET)) { -- return new FireworkRocketEntity(level, ammo, shooter, shooter.getX(), shooter.getEyeY() - 0.15F, shooter.getZ(), true); +@@ -159,7 +_,11 @@ + final Level level, final LivingEntity shooter, final ItemStack heldItem, final ItemStack projectile, final boolean isCrit + ) { + if (projectile.is(Items.FIREWORK_ROCKET)) { +- return new FireworkRocketEntity(level, projectile, shooter, shooter.getX(), shooter.getEyeY() - 0.15F, shooter.getZ(), true); + // Paper start -+ FireworkRocketEntity entity = new FireworkRocketEntity(level, ammo, shooter, shooter.getX(), shooter.getEyeY() - 0.15F, shooter.getZ(), true); ++ FireworkRocketEntity entity = new FireworkRocketEntity(level, projectile, shooter, shooter.getX(), shooter.getEyeY() - 0.15F, shooter.getZ(), true); + entity.spawningEntity = shooter.getUUID(); // Paper + return entity; + // Paper end } else { - Projectile projectile = super.createProjectile(level, shooter, weapon, ammo, isCrit); - if (projectile instanceof AbstractArrow abstractArrow) { -@@ -163,7 +_,7 @@ + Projectile projectileEntity = super.createProjectile(level, shooter, heldItem, projectile, isCrit); + if (projectileEntity instanceof AbstractArrow arrow) { +@@ -187,7 +_,7 @@ if (level instanceof ServerLevel serverLevel) { - ChargedProjectiles chargedProjectiles = weapon.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.EMPTY); - if (chargedProjectiles != null && !chargedProjectiles.isEmpty()) { -- this.shoot(serverLevel, shooter, hand, weapon, chargedProjectiles.getItems(), velocity, inaccuracy, shooter instanceof Player, target); -+ this.shoot(serverLevel, shooter, hand, weapon, chargedProjectiles.getItems(), velocity, inaccuracy, shooter instanceof Player, target, 1); // Paper - Pass draw strength - if (shooter instanceof ServerPlayer serverPlayer) { - CriteriaTriggers.SHOT_CROSSBOW.trigger(serverPlayer, weapon); - serverPlayer.awardStat(Stats.ITEM_USED.get(weapon.getItem())); -@@ -211,7 +_,13 @@ - ); + ChargedProjectiles charged = weapon.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.EMPTY); + if (charged != null && !charged.isEmpty()) { +- this.shoot(serverLevel, shooter, hand, weapon, charged.itemCopies(), power, uncertainty, shooter instanceof Player, targetOverride); ++ this.shoot(serverLevel, shooter, hand, weapon, charged.itemCopies(), power, uncertainty, shooter instanceof Player, targetOverride, 1.0F); // Paper - Pass draw strength + if (shooter instanceof ServerPlayer player) { + CriteriaTriggers.SHOT_CROSSBOW.trigger(player, weapon); + player.awardStat(Stats.ITEM_USED.get(weapon.getItem())); +@@ -227,7 +_,13 @@ + .ifPresent(sound -> level.playSound(null, entity.getX(), entity.getY(), entity.getZ(), sound.value(), SoundSource.PLAYERS, 0.5F, 1.0F)); } -- if (f >= 1.0F && !isCharged(stack) && tryLoadProjectiles(livingEntity, stack)) { -+ if (f >= 1.0F && !isCharged(stack)) { +- if (tickPercent >= 1.0F && !isCharged(itemStack) && tryLoadProjectiles(entity, itemStack)) { ++ if (tickPercent >= 1.0F && !isCharged(itemStack)) { + // Paper start - Add EntityLoadCrossbowEvent -+ final io.papermc.paper.event.entity.EntityLoadCrossbowEvent event = new io.papermc.paper.event.entity.EntityLoadCrossbowEvent(livingEntity.getBukkitLivingEntity(), stack.asBukkitMirror(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(livingEntity.getUsedItemHand())); -+ if (!event.callEvent() || !tryLoadProjectiles(livingEntity, stack, event.shouldConsumeItem()) || !event.shouldConsumeItem()) { ++ final io.papermc.paper.event.entity.EntityLoadCrossbowEvent event = new io.papermc.paper.event.entity.EntityLoadCrossbowEvent(entity.getBukkitLivingEntity(), itemStack.asBukkitMirror(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(entity.getUsedItemHand())); ++ if (!event.callEvent() || !tryLoadProjectiles(entity, itemStack, event.shouldConsumeItem()) || !event.shouldConsumeItem()) { + return; + } + // Paper end - Add EntityLoadCrossbowEvent - chargingSounds.end() + sounds.end() .ifPresent( sound -> level.playSound( diff --git a/paper-server/patches/sources/net/minecraft/world/item/DebugStickItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/DebugStickItem.java.patch index affcf590be1f..77743d475147 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/DebugStickItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/DebugStickItem.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/item/DebugStickItem.java +++ b/net/minecraft/world/item/DebugStickItem.java -@@ -49,7 +_,7 @@ - } - - public boolean handleInteraction(Player player, BlockState stateClicked, LevelAccessor level, BlockPos pos, boolean shouldCycleState, ItemStack debugStack) { +@@ -51,7 +_,7 @@ + public boolean handleInteraction( + final ServerPlayer player, final BlockState state, final LevelAccessor level, final BlockPos pos, final boolean cycle, final ItemStack itemStackInHand + ) { - if (!player.canUseGameMasterBlocks()) { + if (!player.canUseGameMasterBlocks() && !(player.getAbilities().instabuild && player.getBukkitEntity().hasPermission("minecraft.debugstick")) && !player.getBukkitEntity().hasPermission("minecraft.debugstick.always")) { // Spigot return false; } else { - Holder blockHolder = stateClicked.getBlockHolder(); + Holder block = state.typeHolder(); diff --git a/paper-server/patches/sources/net/minecraft/world/item/DyeItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/DyeItem.java.patch index aac31da40df4..4347f67384dd 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/DyeItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/DyeItem.java.patch @@ -1,21 +1,21 @@ --- a/net/minecraft/world/item/DyeItem.java +++ b/net/minecraft/world/item/DyeItem.java -@@ -27,7 +_,17 @@ - if (target instanceof Sheep sheep && sheep.isAlive() && !sheep.isSheared() && sheep.getColor() != this.dyeColor) { - sheep.level().playSound(player, sheep, SoundEvents.DYE_USE, SoundSource.PLAYERS, 1.0F, 1.0F); - if (!player.level().isClientSide()) { -- sheep.setColor(this.dyeColor); -+ // CraftBukkit start -+ byte bColor = (byte) this.dyeColor.getId(); -+ org.bukkit.event.entity.SheepDyeWoolEvent event = new org.bukkit.event.entity.SheepDyeWoolEvent((org.bukkit.entity.Sheep) sheep.getBukkitEntity(), org.bukkit.DyeColor.getByWoolData(bColor), (org.bukkit.entity.Player) player.getBukkitEntity()); -+ sheep.level().getCraftServer().getPluginManager().callEvent(event); +@@ -23,7 +_,17 @@ + if (dyeColor != null && sheep.getColor() != dyeColor) { + sheep.level().playSound(player, sheep, SoundEvents.DYE_USE, SoundSource.PLAYERS, 1.0F, 1.0F); + if (!player.level().isClientSide()) { +- sheep.setColor(dyeColor); ++ // CraftBukkit start ++ byte bColor = (byte) dyeColor.getId(); ++ org.bukkit.event.entity.SheepDyeWoolEvent event = new org.bukkit.event.entity.SheepDyeWoolEvent((org.bukkit.entity.Sheep) sheep.getBukkitEntity(), org.bukkit.DyeColor.getByWoolData(bColor), (org.bukkit.entity.Player) player.getBukkitEntity()); ++ sheep.level().getCraftServer().getPluginManager().callEvent(event); + -+ if (event.isCancelled()) { -+ return InteractionResult.PASS; -+ } ++ if (event.isCancelled()) { ++ return InteractionResult.PASS; ++ } + -+ sheep.setColor(DyeColor.byId((byte) event.getColor().getWoolData())); -+ // CraftBukkit end - stack.shrink(1); - } ++ sheep.setColor(DyeColor.byId((byte) event.getColor().getWoolData())); ++ // CraftBukkit end + itemStack.shrink(1); + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch index 936bc3448386..c04d7fd6a803 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch @@ -2,8 +2,8 @@ +++ b/net/minecraft/world/item/EggItem.java @@ -23,22 +_,35 @@ @Override - public InteractionResult use(Level level, Player player, InteractionHand hand) { - ItemStack itemInHand = player.getItemInHand(hand); + public InteractionResult use(final Level level, final Player player, final InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); - level.playSound( - null, - player.getX(), @@ -15,14 +15,14 @@ - 0.4F / (level.getRandom().nextFloat() * 0.4F + 0.8F) - ); - if (level instanceof ServerLevel serverLevel) { -- Projectile.spawnProjectileFromRotation(ThrownEgg::new, serverLevel, itemInHand, player, 0.0F, 1.5F, 1.0F); +- Projectile.spawnProjectileFromRotation(ThrownEgg::new, serverLevel, itemStack, player, 0.0F, 1.5F, 1.0F); - } + // Paper start -+ final Projectile.Delayed thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, (ServerLevel) level, itemInHand, player, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, 1.0F); -+ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Projectile) thrownEgg.projectile().getBukkitEntity()); ++ final Projectile.Delayed thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, (ServerLevel) level, itemStack, player, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, 1.0F); ++ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownEgg.projectile().getBukkitEntity()); + if (event.callEvent() && thrownEgg.attemptSpawn()) { + if (event.shouldConsume()) { -+ itemInHand.consume(1, player); ++ itemStack.consume(1, player); + } else { + player.containerMenu.forceHeldSlot(hand); + } @@ -40,7 +40,7 @@ + // Paper - move up - player.awardStat(Stats.ITEM_USED.get(this)); -- itemInHand.consume(1, player); +- itemStack.consume(1, player); + player.awardStat(Stats.ITEM_USED.get(this)); + // Paper start + } else { diff --git a/paper-server/patches/sources/net/minecraft/world/item/EmptyMapItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/EmptyMapItem.java.patch index 9cd8c1185b5c..bc25fbb2abdb 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/EmptyMapItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/EmptyMapItem.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/item/EmptyMapItem.java +++ b/net/minecraft/world/item/EmptyMapItem.java @@ -17,10 +_,16 @@ - public InteractionResult use(Level level, Player player, InteractionHand hand) { - ItemStack itemInHand = player.getItemInHand(hand); + public InteractionResult use(final Level level, final Player player, final InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); if (level instanceof ServerLevel serverLevel) { -+ org.bukkit.inventory.ItemStack emptyMap = itemInHand.asBukkitCopy(); // Paper - PlayerMapFilledEvent - itemInHand.consume(1, player); ++ org.bukkit.inventory.ItemStack emptyMap = itemStack.asBukkitCopy(); // Paper - PlayerMapFilledEvent + itemStack.consume(1, player); player.awardStat(Stats.ITEM_USED.get(this)); serverLevel.playSound(null, player, SoundEvents.UI_CARTOGRAPHY_TABLE_TAKE_RESULT, player.getSoundSource(), 1.0F, 1.0F); - ItemStack itemStack = MapItem.create(serverLevel, player.getBlockX(), player.getBlockZ(), (byte)0, true, false); + ItemStack map = MapItem.create(serverLevel, player.getBlockX(), player.getBlockZ(), (byte)0, true, false); + // Paper start - PlayerMapFilledEvent -+ io.papermc.paper.event.player.PlayerMapFilledEvent event = new io.papermc.paper.event.player.PlayerMapFilledEvent((org.bukkit.entity.Player) player.getBukkitEntity(), emptyMap, org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack)); ++ io.papermc.paper.event.player.PlayerMapFilledEvent event = new io.papermc.paper.event.player.PlayerMapFilledEvent((org.bukkit.entity.Player) player.getBukkitEntity(), emptyMap, org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(map)); + event.callEvent(); -+ itemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getCreatedMap()); ++ map = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getCreatedMap()); + // Paper end - PlayerMapFilledEvent - if (itemInHand.isEmpty()) { - return InteractionResult.SUCCESS.heldItemTransformedTo(itemStack); + if (itemStack.isEmpty()) { + return InteractionResult.SUCCESS.heldItemTransformedTo(map); } else { diff --git a/paper-server/patches/sources/net/minecraft/world/item/EndCrystalItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/EndCrystalItem.java.patch index a82c58ec7624..3ec27164f56d 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/EndCrystalItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/EndCrystalItem.java.patch @@ -1,30 +1,21 @@ --- a/net/minecraft/world/item/EndCrystalItem.java +++ b/net/minecraft/world/item/EndCrystalItem.java -@@ -27,7 +_,7 @@ - if (!blockState.is(Blocks.OBSIDIAN) && !blockState.is(Blocks.BEDROCK)) { - return InteractionResult.FAIL; - } else { -- BlockPos blockPos = clickedPos.above(); -+ BlockPos blockPos = clickedPos.above(); final BlockPos aboveBlockPos = blockPos; // Paper - OBFHELPER - if (!level.isEmptyBlock(blockPos)) { - return InteractionResult.FAIL; - } else { @@ -41,11 +_,17 @@ if (level instanceof ServerLevel) { - EndCrystal endCrystal = new EndCrystal(level, d + 0.5, d1, d2 + 0.5); - endCrystal.setShowBottom(false); + EndCrystal crystal = new EndCrystal(level, x + 0.5, y, z + 0.5); + crystal.setShowBottom(false); + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(context, endCrystal).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(context, crystal).isCancelled()) { + if (context.getPlayer() != null) context.getPlayer().containerMenu.sendAllDataToRemote(); // Paper - Fix inventory desync + return InteractionResult.FAIL; + } + // CraftBukkit end - level.addFreshEntity(endCrystal); - level.gameEvent(context.getPlayer(), GameEvent.ENTITY_PLACE, blockPos); - EndDragonFight dragonFight = ((ServerLevel)level).getDragonFight(); - if (dragonFight != null) { -- dragonFight.tryRespawn(); -+ dragonFight.tryRespawn(aboveBlockPos); // Paper - Perf: Do crystal-portal proximity check before entity lookup + level.addFreshEntity(crystal); + level.gameEvent(context.getPlayer(), GameEvent.ENTITY_PLACE, above); + EnderDragonFight fight = ((ServerLevel)level).getDragonFight(); + if (fight != null) { +- fight.tryRespawn(); ++ fight.tryRespawn(above); // Paper - Perf: Do crystal-portal proximity check before entity lookup } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/EnderEyeItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/EnderEyeItem.java.patch index 8b88132b3f42..f5c54962a93a 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/EnderEyeItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/EnderEyeItem.java.patch @@ -3,15 +3,15 @@ @@ -44,6 +_,11 @@ return InteractionResult.SUCCESS; } else { - BlockState blockState1 = blockState.setValue(EndPortalFrameBlock.HAS_EYE, true); + BlockState newState = targetState.setValue(EndPortalFrameBlock.HAS_EYE, true); + // Paper start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(context.getPlayer(), clickedPos, blockState1)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(context.getPlayer(), pos, newState)) { + return InteractionResult.PASS; + } + // Paper end - Block.pushEntitiesUp(blockState, blockState1, level, clickedPos); - level.setBlock(clickedPos, blockState1, Block.UPDATE_CLIENTS); - level.updateNeighbourForOutputSignal(clickedPos, Blocks.END_PORTAL_FRAME); + Block.pushEntitiesUp(targetState, newState, level, pos); + level.setBlock(pos, newState, Block.UPDATE_CLIENTS); + level.updateNeighbourForOutputSignal(pos, Blocks.END_PORTAL_FRAME); @@ -61,7 +_,27 @@ } } @@ -42,8 +42,8 @@ return InteractionResult.SUCCESS; @@ -91,7 +_,11 @@ - eyeOfEnder.setItem(itemInHand); - eyeOfEnder.signalTo(Vec3.atLowerCornerOf(blockPos)); + eyeOfEnder.setItem(itemStack); + eyeOfEnder.signalTo(Vec3.atLowerCornerOf(nearestMapFeature)); level.gameEvent(GameEvent.PROJECTILE_SHOOT, eyeOfEnder.position(), GameEvent.Context.of(player)); - level.addFreshEntity(eyeOfEnder); + // CraftBukkit start @@ -52,5 +52,5 @@ + } + // CraftBukkit end if (player instanceof ServerPlayer serverPlayer) { - CriteriaTriggers.USED_ENDER_EYE.trigger(serverPlayer, blockPos); + CriteriaTriggers.USED_ENDER_EYE.trigger(serverPlayer, nearestMapFeature); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch index 28fbeb9fcae3..233d4a56fe41 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch @@ -2,8 +2,8 @@ +++ b/net/minecraft/world/item/EnderpearlItem.java @@ -21,22 +_,42 @@ @Override - public InteractionResult use(Level level, Player player, InteractionHand hand) { - ItemStack itemInHand = player.getItemInHand(hand); + public InteractionResult use(final Level level, final Player player, final InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); - level.playSound( - null, - player.getX(), @@ -15,14 +15,14 @@ - 0.4F / (level.getRandom().nextFloat() * 0.4F + 0.8F) - ); if (level instanceof ServerLevel serverLevel) { -- Projectile.spawnProjectileFromRotation(ThrownEnderpearl::new, serverLevel, itemInHand, player, 0.0F, PROJECTILE_SHOOT_POWER, 1.0F); +- Projectile.spawnProjectileFromRotation(ThrownEnderpearl::new, serverLevel, itemStack, player, 0.0F, 1.5F, 1.0F); + // CraftBukkit start + // Paper start - PlayerLaunchProjectileEvent -+ final Projectile.Delayed thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, serverLevel, itemInHand, player, 0.0F, EnderpearlItem.PROJECTILE_SHOOT_POWER, 1.0F); -+ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Projectile) thrownEnderpearl.projectile().getBukkitEntity()); ++ final Projectile.Delayed thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, serverLevel, itemStack, player, 0.0F, EnderpearlItem.PROJECTILE_SHOOT_POWER, 1.0F); ++ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownEnderpearl.projectile().getBukkitEntity()); + if (event.callEvent() && thrownEnderpearl.attemptSpawn()) { + if (event.shouldConsume()) { -+ itemInHand.consume(1, player); ++ itemStack.consume(1, player); + } else { + player.containerMenu.forceHeldSlot(hand); + } @@ -41,7 +41,7 @@ + } else { + if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { + serverPlayer.deregisterEnderPearl(thrownEnderpearl.projectile()); -+ serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundCooldownPacket(player.getCooldowns().getCooldownGroup(itemInHand), 0)); // prevent visual desync of cooldown on the slot ++ serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundCooldownPacket(player.getCooldowns().getCooldownGroup(itemStack), 0)); // prevent visual desync of cooldown on the slot + } + // Paper end - PlayerLaunchProjectileEvent + player.containerMenu.forceHeldSlot(hand); @@ -51,7 +51,7 @@ + // CraftBukkit end - player.awardStat(Stats.ITEM_USED.get(this)); -- itemInHand.consume(1, player); +- itemStack.consume(1, player); + // Paper - PlayerLaunchProjectileEvent - moved up return InteractionResult.SUCCESS; } diff --git a/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch index eeafee1fb89e..7ca72e919df4 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch @@ -2,8 +2,8 @@ +++ b/net/minecraft/world/item/ExperienceBottleItem.java @@ -21,22 +_,37 @@ @Override - public InteractionResult use(Level level, Player player, InteractionHand hand) { - ItemStack itemInHand = player.getItemInHand(hand); + public InteractionResult use(final Level level, final Player player, final InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); - level.playSound( - null, - player.getX(), @@ -16,13 +16,13 @@ - ); + // Paper - PlayerLaunchProjectileEvent - moved down if (level instanceof ServerLevel serverLevel) { -- Projectile.spawnProjectileFromRotation(ThrownExperienceBottle::new, serverLevel, itemInHand, player, -20.0F, 0.7F, 1.0F); +- Projectile.spawnProjectileFromRotation(ThrownExperienceBottle::new, serverLevel, itemStack, player, -20.0F, 0.7F, 1.0F); + // Paper start - PlayerLaunchProjectileEvent -+ final Projectile.Delayed thrownExperienceBottle = Projectile.spawnProjectileFromRotationDelayed(ThrownExperienceBottle::new, serverLevel, itemInHand, player, -20.0F, 0.7F, 1.0F); -+ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Projectile) thrownExperienceBottle.projectile().getBukkitEntity()); ++ final Projectile.Delayed thrownExperienceBottle = Projectile.spawnProjectileFromRotationDelayed(ThrownExperienceBottle::new, serverLevel, itemStack, player, -20.0F, 0.7F, 1.0F); ++ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownExperienceBottle.projectile().getBukkitEntity()); + if (event.callEvent() && thrownExperienceBottle.attemptSpawn()) { + if (event.shouldConsume()) { -+ itemInHand.consume(1, player); ++ itemStack.consume(1, player); + } else { + player.containerMenu.forceHeldSlot(hand); + } @@ -46,7 +46,7 @@ } - player.awardStat(Stats.ITEM_USED.get(this)); -- itemInHand.consume(1, player); +- itemStack.consume(1, player); + // Paper - PlayerLaunchProjectileEvent - moved up return InteractionResult.SUCCESS; } diff --git a/paper-server/patches/sources/net/minecraft/world/item/FireChargeItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/FireChargeItem.java.patch index 0ea5aed2bbc2..49ed538b3d83 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/FireChargeItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/FireChargeItem.java.patch @@ -2,30 +2,30 @@ +++ b/net/minecraft/world/item/FireChargeItem.java @@ -36,12 +_,28 @@ if (!CampfireBlock.canLight(blockState) && !CandleBlock.canLight(blockState) && !CandleCakeBlock.canLight(blockState)) { - clickedPos = clickedPos.relative(context.getClickedFace()); - if (BaseFireBlock.canBePlacedAt(level, clickedPos, context.getHorizontalDirection())) { + pos = pos.relative(context.getClickedFace()); + if (BaseFireBlock.canBePlacedAt(level, pos, context.getHorizontalDirection())) { + // CraftBukkit start - fire BlockIgniteEvent -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, clickedPos, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FIREBALL, context.getPlayer()).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, pos, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FIREBALL, context.getPlayer()).isCancelled()) { + if (!context.getPlayer().getAbilities().instabuild) { + context.getItemInHand().shrink(1); + } + return InteractionResult.PASS; + } + // CraftBukkit end - this.playSound(level, clickedPos); - level.setBlockAndUpdate(clickedPos, BaseFireBlock.getState(level, clickedPos)); - level.gameEvent(context.getPlayer(), GameEvent.BLOCK_PLACE, clickedPos); - flag = true; + this.playSound(level, pos); + level.setBlockAndUpdate(pos, BaseFireBlock.getState(level, pos)); + level.gameEvent(context.getPlayer(), GameEvent.BLOCK_PLACE, pos); + used = true; } } else { + // CraftBukkit start - fire BlockIgniteEvent -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, clickedPos, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FIREBALL, context.getPlayer()).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, pos, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FIREBALL, context.getPlayer()).isCancelled()) { + if (!context.getPlayer().getAbilities().instabuild) { + context.getItemInHand().shrink(1); + } + return InteractionResult.PASS; + } + // CraftBukkit end - this.playSound(level, clickedPos); - level.setBlockAndUpdate(clickedPos, blockState.setValue(BlockStateProperties.LIT, true)); - level.gameEvent(context.getPlayer(), GameEvent.BLOCK_CHANGE, clickedPos); + this.playSound(level, pos); + level.setBlockAndUpdate(pos, blockState.setValue(BlockStateProperties.LIT, true)); + level.gameEvent(context.getPlayer(), GameEvent.BLOCK_CHANGE, pos); diff --git a/paper-server/patches/sources/net/minecraft/world/item/FireworkRocketItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/FireworkRocketItem.java.patch index 803d94ec2889..1aaf7897ede6 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/FireworkRocketItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/FireworkRocketItem.java.patch @@ -1,26 +1,26 @@ --- a/net/minecraft/world/item/FireworkRocketItem.java +++ b/net/minecraft/world/item/FireworkRocketItem.java @@ -36,7 +_,7 @@ - ItemStack itemInHand = context.getItemInHand(); + ItemStack itemStack = context.getItemInHand(); Vec3 clickLocation = context.getClickLocation(); - Direction clickedFace = context.getClickedFace(); + Direction direction = context.getClickedFace(); - Projectile.spawnProjectile( + final Projectile.Delayed fireworkRocketEntity = Projectile.spawnProjectileDelayed( // Paper - PlayerLaunchProjectileEvent new FireworkRocketEntity( level, context.getPlayer(), @@ -46,9 +_,14 @@ - itemInHand + itemStack ), serverLevel, -- itemInHand -+ itemInHand, f -> f.spawningEntity = context.getPlayer() == null ? null : context.getPlayer().getUUID() // Paper - firework api - assign spawning entity uuid +- itemStack ++ itemStack, f -> f.spawningEntity = context.getPlayer() == null ? null : context.getPlayer().getUUID() // Paper - firework api - assign spawning entity uuid ); -- itemInHand.shrink(1); +- itemStack.shrink(1); + // Paper start - PlayerLaunchProjectileEvent -+ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Firework) fireworkRocketEntity.projectile().getBukkitEntity()); ++ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Firework) fireworkRocketEntity.projectile().getBukkitEntity()); + if (!event.callEvent() || !fireworkRocketEntity.attemptSpawn()) return InteractionResult.PASS; -+ if (event.shouldConsume() && !context.getPlayer().hasInfiniteMaterials()) itemInHand.shrink(1); ++ if (event.shouldConsume() && !context.getPlayer().hasInfiniteMaterials()) itemStack.shrink(1); + else context.getPlayer().containerMenu.forceHeldSlot(context.getHand()); + // Paper end - PlayerLaunchProjectileEvent } @@ -28,20 +28,20 @@ return InteractionResult.SUCCESS; @@ -60,13 +_,24 @@ if (player.isFallFlying()) { - ItemStack itemInHand = player.getItemInHand(hand); + ItemStack itemStack = player.getItemInHand(hand); if (level instanceof ServerLevel serverLevel) { - if (player.dropAllLeashConnections(null)) { - level.playSound(null, player, SoundEvents.LEAD_BREAK, SoundSource.NEUTRAL, 1.0F, 1.0F); + // Paper start - PlayerElytraBoostEvent -+ final Projectile.Delayed delayed = Projectile.spawnProjectileDelayed(new FireworkRocketEntity(level, itemInHand, player), serverLevel, itemInHand, f -> f.spawningEntity = player.getUUID()); // Paper - firework api - assign spawning entity uuid -+ com.destroystokyo.paper.event.player.PlayerElytraBoostEvent event = new com.destroystokyo.paper.event.player.PlayerElytraBoostEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Firework) delayed.projectile().getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand)); ++ final Projectile.Delayed delayed = Projectile.spawnProjectileDelayed(new FireworkRocketEntity(level, itemStack, player), serverLevel, itemStack, f -> f.spawningEntity = player.getUUID()); // Paper - firework api - assign spawning entity uuid ++ com.destroystokyo.paper.event.player.PlayerElytraBoostEvent event = new com.destroystokyo.paper.event.player.PlayerElytraBoostEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Firework) delayed.projectile().getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand)); + if (event.callEvent() && delayed.attemptSpawn()) { + player.awardStat(Stats.ITEM_USED.get(this)); // Moved up from below + if (player.dropAllLeashConnections(null)) { + level.playSound(null, player, SoundEvents.LEAD_BREAK, SoundSource.NEUTRAL, 1.0F, 1.0F); + } + if (event.shouldConsume() && !player.hasInfiniteMaterials()) { -+ itemInHand.shrink(1); // Moved up from below ++ itemStack.shrink(1); // Moved up from below + } else { + player.containerMenu.forceHeldSlot(hand); + } @@ -49,8 +49,8 @@ + player.containerMenu.forceHeldSlot(hand); } - -- Projectile.spawnProjectile(new FireworkRocketEntity(level, itemInHand, player), serverLevel, itemInHand); -- itemInHand.consume(1, player); +- Projectile.spawnProjectile(new FireworkRocketEntity(level, itemStack, player), serverLevel, itemStack); +- itemStack.consume(1, player); - player.awardStat(Stats.ITEM_USED.get(this)); + // Moved up consume and changed consume to shrink + // Paper end - PlayerElytraBoostEvent diff --git a/paper-server/patches/sources/net/minecraft/world/item/FishingRodItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/FishingRodItem.java.patch index 16bc842d8e84..c89981d9640b 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/FishingRodItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/FishingRodItem.java.patch @@ -1,17 +1,17 @@ --- a/net/minecraft/world/item/FishingRodItem.java +++ b/net/minecraft/world/item/FishingRodItem.java @@ -23,7 +_,7 @@ - ItemStack itemInHand = player.getItemInHand(hand); + ItemStack itemStack = player.getItemInHand(hand); if (player.fishing != null) { if (!level.isClientSide()) { -- int i = player.fishing.retrieve(itemInHand); -+ int i = player.fishing.retrieve(itemInHand, hand); // Paper - Add hand parameter to PlayerFishEvent - itemInHand.hurtAndBreak(i, player, hand.asEquipmentSlot()); +- int dmg = player.fishing.retrieve(itemStack); ++ int dmg = player.fishing.retrieve(itemStack, hand); // Paper - Add hand parameter to PlayerFishEvent + itemStack.hurtAndBreak(dmg, player, hand.asEquipmentSlot()); } @@ -39,20 +_,31 @@ ); - itemInHand.causeUseVibration(player, GameEvent.ITEM_INTERACT_FINISH); + itemStack.causeUseVibration(player, GameEvent.ITEM_INTERACT_FINISH); } else { - level.playSound( - null, @@ -25,11 +25,11 @@ - ); + // CraftBukkit - moved down if (level instanceof ServerLevel serverLevel) { - int i1 = (int)(EnchantmentHelper.getFishingTimeReduction(serverLevel, itemInHand, player) * 20.0F); - int fishingLuckBonus = EnchantmentHelper.getFishingLuckBonus(serverLevel, itemInHand, player); -- Projectile.spawnProjectile(new FishingHook(player, level, fishingLuckBonus, i1), serverLevel, itemInHand); + int lureSpeed = (int)(EnchantmentHelper.getFishingTimeReduction(serverLevel, itemStack, player) * 20.0F); + int luck = EnchantmentHelper.getFishingLuckBonus(serverLevel, itemStack, player); +- Projectile.spawnProjectile(new FishingHook(player, level, luck, lureSpeed), serverLevel, itemStack); + // CraftBukkit start -+ FishingHook fishingHook = new FishingHook(player, level, fishingLuckBonus, i1); ++ FishingHook fishingHook = new FishingHook(player, level, luck, lureSpeed); + org.bukkit.event.player.PlayerFishEvent playerFishEvent = new org.bukkit.event.player.PlayerFishEvent((org.bukkit.entity.Player) player.getBukkitEntity(), null, (org.bukkit.entity.FishHook) fishingHook.getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), org.bukkit.event.player.PlayerFishEvent.State.FISHING); + level.getCraftServer().getPluginManager().callEvent(playerFishEvent); + @@ -47,7 +47,7 @@ + 0.5F, + 0.4F / (level.getRandom().nextFloat() * 0.4F + 0.8F) + ); -+ Projectile.spawnProjectile(fishingHook, serverLevel, itemInHand); ++ Projectile.spawnProjectile(fishingHook, serverLevel, itemStack); + // CraftBukkit end } diff --git a/paper-server/patches/sources/net/minecraft/world/item/FlintAndSteelItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/FlintAndSteelItem.java.patch index f9aef7b267bf..7180cc2971ca 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/FlintAndSteelItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/FlintAndSteelItem.java.patch @@ -1,28 +1,28 @@ --- a/net/minecraft/world/item/FlintAndSteelItem.java +++ b/net/minecraft/world/item/FlintAndSteelItem.java @@ -32,6 +_,12 @@ - if (!CampfireBlock.canLight(blockState) && !CandleBlock.canLight(blockState) && !CandleCakeBlock.canLight(blockState)) { - BlockPos blockPos = clickedPos.relative(context.getClickedFace()); - if (BaseFireBlock.canBePlacedAt(level, blockPos, context.getHorizontalDirection())) { + if (!CampfireBlock.canLight(state) && !CandleBlock.canLight(state) && !CandleCakeBlock.canLight(state)) { + BlockPos relativePos = pos.relative(context.getClickedFace()); + if (BaseFireBlock.canBePlacedAt(level, relativePos, context.getHorizontalDirection())) { + // CraftBukkit start - Store the clicked block -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, blockPos, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL, player).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, relativePos, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL, player).isCancelled()) { + context.getItemInHand().hurtAndBreak(1, player, context.getHand().asEquipmentSlot()); + return InteractionResult.PASS; + } + // CraftBukkit end - level.playSound(player, blockPos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS, 1.0F, level.getRandom().nextFloat() * 0.4F + 0.8F); - BlockState state = BaseFireBlock.getState(level, blockPos); - level.setBlock(blockPos, state, Block.UPDATE_ALL_IMMEDIATE); + level.playSound(player, relativePos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS, 1.0F, level.getRandom().nextFloat() * 0.4F + 0.8F); + BlockState fireState = BaseFireBlock.getState(level, relativePos); + level.setBlock(relativePos, fireState, Block.UPDATE_ALL_IMMEDIATE); @@ -47,6 +_,12 @@ return InteractionResult.FAIL; } } else { + // CraftBukkit start - Store the clicked block -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, clickedPos, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL, player).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, pos, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL, player).isCancelled()) { + context.getItemInHand().hurtAndBreak(1, player, context.getHand().asEquipmentSlot()); + return InteractionResult.PASS; + } + // CraftBukkit end - level.playSound(player, clickedPos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS, 1.0F, level.getRandom().nextFloat() * 0.4F + 0.8F); - level.setBlock(clickedPos, blockState.setValue(BlockStateProperties.LIT, true), Block.UPDATE_ALL_IMMEDIATE); - level.gameEvent(player, GameEvent.BLOCK_CHANGE, clickedPos); + level.playSound(player, pos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS, 1.0F, level.getRandom().nextFloat() * 0.4F + 0.8F); + level.setBlock(pos, state.setValue(BlockStateProperties.LIT, true), Block.UPDATE_ALL_IMMEDIATE); + level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos); diff --git a/paper-server/patches/sources/net/minecraft/world/item/HangingEntityItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/HangingEntityItem.java.patch index 60601d933b5e..363bf1f36218 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/HangingEntityItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/HangingEntityItem.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/item/HangingEntityItem.java +++ b/net/minecraft/world/item/HangingEntityItem.java @@ -62,6 +_,20 @@ - EntityType.createDefaultStackConfig(level, itemInHand, player).accept(hangingEntity); - if (hangingEntity.survives()) { + EntityType.createDefaultStackConfig(level, itemInHand, player).accept(entity); + if (entity.survives()) { if (!level.isClientSide()) { + // CraftBukkit start - fire HangingPlaceEvent + org.bukkit.entity.Player bukkitPlayer = player == null ? null : (org.bukkit.entity.Player) player.getBukkitEntity(); @@ -10,7 +10,7 @@ + org.bukkit.block.BlockFace blockFace = org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(clickedFace); + org.bukkit.inventory.EquipmentSlot hand = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(context.getHand()); + -+ org.bukkit.event.hanging.HangingPlaceEvent event = new org.bukkit.event.hanging.HangingPlaceEvent((org.bukkit.entity.Hanging) hangingEntity.getBukkitEntity(), bukkitPlayer, blockClicked, blockFace, hand, org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemInHand)); ++ org.bukkit.event.hanging.HangingPlaceEvent event = new org.bukkit.event.hanging.HangingPlaceEvent((org.bukkit.entity.Hanging) entity.getBukkitEntity(), bukkitPlayer, blockClicked, blockFace, hand, org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemInHand)); + level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { @@ -18,6 +18,6 @@ + return InteractionResult.FAIL; + } + // CraftBukkit end - hangingEntity.playPlacementSound(); - level.gameEvent(player, GameEvent.ENTITY_PLACE, hangingEntity.position()); - level.addFreshEntity(hangingEntity); + entity.playPlacementSound(); + level.gameEvent(player, GameEvent.ENTITY_PLACE, entity.position()); + level.addFreshEntity(entity); diff --git a/paper-server/patches/sources/net/minecraft/world/item/HoneycombItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/HoneycombItem.java.patch index 1710d50a1cac..4b222bd71be1 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/HoneycombItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/HoneycombItem.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/item/HoneycombItem.java +++ b/net/minecraft/world/item/HoneycombItem.java @@ -146,6 +_,14 @@ - return getWaxed(blockState).map(blockState1 -> { + return getWaxed(oldState).map(waxedState -> { Player player = context.getPlayer(); ItemStack itemInHand = context.getItemInHand(); + // Paper start - EntityChangeBlockEvent -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, clickedPos, blockState1)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, waxedState)) { + if (!player.isCreative()) { + player.containerMenu.forceHeldSlot(context.getHand()); + } @@ -13,5 +13,5 @@ + } + // Paper end if (player instanceof ServerPlayer serverPlayer) { - CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(serverPlayer, clickedPos, itemInHand); + CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(serverPlayer, pos, itemInHand); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/ItemCooldowns.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ItemCooldowns.java.patch index 902edf377e51..521dd59f86c5 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ItemCooldowns.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ItemCooldowns.java.patch @@ -3,14 +3,14 @@ @@ -56,6 +_,13 @@ } - public void addCooldown(Identifier group, int cooldown) { + public void addCooldown(final Identifier cooldownGroup, final int time) { + // Paper start - Item cooldown events -+ this.addCooldown(group, cooldown, true); ++ this.addCooldown(cooldownGroup, time, true); + } + -+ public void addCooldown(Identifier group, int cooldown, boolean callEvent) { ++ public void addCooldown(Identifier cooldownGroup, int time, boolean callEvent) { + // Event called in server override + // Paper end - Item cooldown events - this.cooldowns.put(group, new ItemCooldowns.CooldownInstance(this.tickCount, this.tickCount + cooldown)); - this.onCooldownStarted(group, cooldown); + this.cooldowns.put(cooldownGroup, new ItemCooldowns.CooldownInstance(this.tickCount, this.tickCount + time)); + this.onCooldownStarted(cooldownGroup, time); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch index c0db603550b7..3fe334715a57 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch @@ -1,21 +1,21 @@ --- a/net/minecraft/world/item/ItemStack.java +++ b/net/minecraft/world/item/ItemStack.java -@@ -202,12 +_,20 @@ +@@ -187,12 +_,20 @@ @Override - public void encode(RegistryFriendlyByteBuf buffer, ItemStack value) { -- if (value.isEmpty()) { -+ if (value.isEmpty() || value.getItem() == null) { // CraftBukkit - NPE fix itemstack.getItem() - buffer.writeVarInt(0); + public void encode(final RegistryFriendlyByteBuf output, final ItemStack itemStack) { +- if (itemStack.isEmpty()) { ++ if (itemStack.isEmpty() || itemStack.getItem() == null) { // CraftBukkit - NPE fix itemstack.getItem() + output.writeVarInt(0); } else { -- buffer.writeVarInt(value.getCount()); -+ buffer.writeVarInt(io.papermc.paper.util.sanitizer.ItemComponentSanitizer.sanitizeCount(io.papermc.paper.util.sanitizer.ItemObfuscationSession.currentSession(), value, value.getCount())); // Paper - potentially sanitize count - Item.STREAM_CODEC.encode(buffer, value.getItemHolder()); +- output.writeVarInt(itemStack.getCount()); ++ output.writeVarInt(io.papermc.paper.util.sanitizer.ItemComponentSanitizer.sanitizeCount(io.papermc.paper.util.sanitizer.ItemObfuscationSession.currentSession(), itemStack, itemStack.getCount())); // Paper - potentially sanitize count + Item.STREAM_CODEC.encode(output, itemStack.typeHolder()); + // Paper start - adventure; conditionally render translatable components + boolean prev = net.minecraft.network.chat.ComponentSerialization.DONT_RENDER_TRANSLATABLES.get(); -+ try (final io.papermc.paper.util.SafeAutoClosable ignored = io.papermc.paper.util.sanitizer.ItemObfuscationSession.withContext(c -> c.itemStack(value))) { // pass the itemstack as context to the obfuscation session ++ try (final io.papermc.paper.util.SafeAutoClosable ignored = io.papermc.paper.util.sanitizer.ItemObfuscationSession.withContext(c -> c.itemStack(itemStack))) { // pass the itemstack as context to the obfuscation session + net.minecraft.network.chat.ComponentSerialization.DONT_RENDER_TRANSLATABLES.set(true); - codec.encode(buffer, value.components.asPatch()); + patchCodec.encode(output, itemStack.components.asPatch()); + } finally { + net.minecraft.network.chat.ComponentSerialization.DONT_RENDER_TRANSLATABLES.set(prev); + } @@ -23,86 +23,85 @@ } } }; -@@ -371,10 +_,166 @@ +@@ -368,10 +_,165 @@ return InteractionResult.PASS; } else { - Item item = this.getItem(); -- InteractionResult interactionResult = item.useOn(context); + Item usedItem = this.getItem(); +- InteractionResult result = usedItem.useOn(context); + // CraftBukkit start - handle all block place event logic here + DataComponentPatch previousPatch = this.components.asPatch(); -+ int oldCount = this.getCount(); -+ ServerLevel serverLevel = (ServerLevel) context.getLevel(); ++ int previousCount = this.getCount(); ++ ServerLevel level = (ServerLevel) context.getLevel(); ++ boolean isBonemeal = usedItem == Items.BONE_MEAL; + -+ if (!(item instanceof BucketItem/* || item instanceof SolidBucketItem*/)) { // if not bucket // Paper - Fix cancelled powdered snow bucket placement -+ serverLevel.captureBlockStates = true; -+ // special case bonemeal -+ if (item == Items.BONE_MEAL) { -+ serverLevel.captureTreeGeneration = true; ++ if (!(usedItem instanceof BucketItem)) { ++ level.captureBlockStates = true; ++ if (isBonemeal) { ++ level.captureTreeGeneration = true; + } + } -+ InteractionResult interactionResult; ++ InteractionResult result; + try { -+ interactionResult = item.useOn(context); ++ result = usedItem.useOn(context); + } finally { -+ serverLevel.captureBlockStates = false; ++ level.captureBlockStates = false; + } + DataComponentPatch newPatch = this.components.asPatch(); + int newCount = this.getCount(); -+ this.setCount(oldCount); ++ this.setCount(previousCount); + this.restorePatch(previousPatch); -+ if (interactionResult.consumesAction() && serverLevel.captureTreeGeneration && !serverLevel.capturedBlockStates.isEmpty()) { -+ serverLevel.captureTreeGeneration = false; -+ org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(clickedPos, serverLevel); ++ if (result.consumesAction() && level.captureTreeGeneration && !level.capturedBlockStates.isEmpty()) { ++ level.captureTreeGeneration = false; ++ org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level); + org.bukkit.TreeType treeType = net.minecraft.world.level.block.SaplingBlock.treeType; + net.minecraft.world.level.block.SaplingBlock.treeType = null; -+ List blocks = new java.util.ArrayList<>(serverLevel.capturedBlockStates.values()); -+ serverLevel.capturedBlockStates.clear(); ++ List blocks = new java.util.ArrayList<>(level.capturedBlockStates.values()); ++ level.capturedBlockStates.clear(); + org.bukkit.event.world.StructureGrowEvent structureEvent = null; + if (treeType != null) { -+ boolean isBonemeal = this.getItem() == Items.BONE_MEAL; + structureEvent = new org.bukkit.event.world.StructureGrowEvent(location, treeType, isBonemeal, (org.bukkit.entity.Player) player.getBukkitEntity(), (List) (List) blocks); + org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent); + } + -+ org.bukkit.event.block.BlockFertilizeEvent fertilizeEvent = new org.bukkit.event.block.BlockFertilizeEvent(org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, clickedPos), (org.bukkit.entity.Player) player.getBukkitEntity(), (List) (List) blocks); ++ org.bukkit.event.block.BlockFertilizeEvent fertilizeEvent = new org.bukkit.event.block.BlockFertilizeEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), (org.bukkit.entity.Player) player.getBukkitEntity(), (List) (List) blocks); + fertilizeEvent.setCancelled(structureEvent != null && structureEvent.isCancelled()); + org.bukkit.Bukkit.getPluginManager().callEvent(fertilizeEvent); + + if (!fertilizeEvent.isCancelled()) { + // Change the stack to its new contents if it hasn't been tampered with. -+ if (this.getCount() == oldCount && Objects.equals(this.components.asPatch(), previousPatch)) { ++ if (this.getCount() == previousCount && Objects.equals(this.components.asPatch(), previousPatch)) { + this.restorePatch(newPatch); + this.setCount(newCount); + } + for (org.bukkit.craftbukkit.block.CraftBlockState snapshot : blocks) { + // SPIGOT-7572 - Move fix for SPIGOT-7248 to CapturedBlockState, to allow bees in bee nest + snapshot.place(snapshot.getFlags()); -+ serverLevel.checkCapturedTreeStateForObserverNotify(clickedPos, snapshot); // Paper - notify observers even if grow failed ++ level.checkCapturedTreeStateForObserverNotify(pos, snapshot); // Paper - notify observers even if grow failed + } -+ player.awardStat(Stats.ITEM_USED.get(item)); // SPIGOT-7236 - award stat ++ player.awardStat(Stats.ITEM_USED.get(usedItem)); // SPIGOT-7236 - award stat + } + + SignItem.openSign = null; // SPIGOT-6758 - Reset on early return -+ return interactionResult; ++ return result; + } -+ serverLevel.captureTreeGeneration = false; - if (player != null && interactionResult instanceof InteractionResult.Success success && success.wasItemInteraction()) { -- player.awardStat(Stats.ITEM_USED.get(item)); ++ level.captureTreeGeneration = false; + if (player != null && result instanceof InteractionResult.Success success && success.wasItemInteraction()) { +- player.awardStat(Stats.ITEM_USED.get(usedItem)); + InteractionHand hand = context.getHand(); + org.bukkit.event.block.BlockPlaceEvent placeEvent = null; -+ List blocks = new java.util.ArrayList<>(serverLevel.capturedBlockStates.values()); -+ serverLevel.capturedBlockStates.clear(); ++ List blocks = new java.util.ArrayList<>(level.capturedBlockStates.values()); ++ level.capturedBlockStates.clear(); + if (blocks.size() > 1) { -+ placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockMultiPlaceEvent(serverLevel, player, hand, blocks, clickedPos); -+ } else if (blocks.size() == 1 && item != Items.POWDER_SNOW_BUCKET) { // Paper - Fix cancelled powdered snow bucket placement -+ placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(serverLevel, player, hand, blocks.getFirst(), clickedPos); ++ placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockMultiPlaceEvent(level, player, hand, blocks, pos); ++ } else if (blocks.size() == 1 && usedItem != Items.POWDER_SNOW_BUCKET) { // Paper - Fix cancelled powdered snow bucket placement ++ placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(level, player, hand, blocks.getFirst(), pos); + } + + if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) { -+ interactionResult = InteractionResult.FAIL; // cancel placement ++ result = InteractionResult.FAIL; // cancel placement + // PAIL: Remove this when MC-99075 fixed + player.containerMenu.forceHeldSlot(hand); -+ serverLevel.capturedTileEntities.clear(); // Paper - Allow chests to be placed with NBT data; clear out block entities as chests and such will pop loot ++ level.capturedTileEntities.clear(); // Paper - Allow chests to be placed with NBT data; clear out block entities as chests and such will pop loot + // revert back all captured blocks + for (org.bukkit.block.BlockState blockstate : blocks) { + ((org.bukkit.craftbukkit.block.CraftBlockState) blockstate).revertPlace(); @@ -111,50 +110,50 @@ + SignItem.openSign = null; // SPIGOT-6758 - Reset on early return + } else { + // Change the stack to its new contents if it hasn't been tampered with. -+ if (this.getCount() == oldCount && Objects.equals(this.components.asPatch(), previousPatch)) { ++ if (this.getCount() == previousCount && Objects.equals(this.components.asPatch(), previousPatch)) { + this.restorePatch(newPatch); + this.setCount(newCount); + } + -+ for (java.util.Map.Entry e : serverLevel.capturedTileEntities.entrySet()) { -+ serverLevel.setBlockEntity(e.getValue()); ++ for (java.util.Map.Entry e : level.capturedTileEntities.entrySet()) { ++ level.setBlockEntity(e.getValue()); + } + + for (org.bukkit.block.BlockState blockstate : blocks) { + int updateFlags = ((org.bukkit.craftbukkit.block.CraftBlockState) blockstate).getFlags(); + net.minecraft.world.level.block.state.BlockState oldBlock = ((org.bukkit.craftbukkit.block.CraftBlockState) blockstate).getHandle(); + BlockPos newPos = ((org.bukkit.craftbukkit.block.CraftBlockState) blockstate).getPosition(); -+ net.minecraft.world.level.block.state.BlockState block = serverLevel.getBlockState(newPos); ++ net.minecraft.world.level.block.state.BlockState block = level.getBlockState(newPos); + + if (!(block.getBlock() instanceof net.minecraft.world.level.block.BaseEntityBlock)) { // Containers get placed automatically -+ block.onPlace(serverLevel, newPos, oldBlock, true, context); ++ block.onPlace(level, newPos, oldBlock, true, context); + } + -+ serverLevel.notifyAndUpdatePhysics(newPos, null, oldBlock, block, serverLevel.getBlockState(newPos), updateFlags, net.minecraft.world.level.block.Block.UPDATE_LIMIT); // send null chunk as chunk.k() returns false by this point ++ level.notifyAndUpdatePhysics(newPos, null, oldBlock, block, level.getBlockState(newPos), updateFlags, net.minecraft.world.level.block.Block.UPDATE_LIMIT); // send null chunk as chunk.k() returns false by this point + } + -+ if (this.item == Items.WITHER_SKELETON_SKULL) { // Special case skulls to allow wither spawns to be cancelled -+ BlockPos bp = clickedPos; -+ if (!serverLevel.getBlockState(clickedPos).canBeReplaced()) { -+ if (!serverLevel.getBlockState(clickedPos).isSolid()) { ++ if (usedItem == Items.WITHER_SKELETON_SKULL) { // Special case skulls to allow wither spawns to be cancelled ++ BlockPos bp = pos; ++ if (!level.getBlockState(pos).canBeReplaced()) { ++ if (!level.getBlockState(pos).isSolid()) { + bp = null; + } else { + bp = bp.relative(context.getClickedFace()); + } + } + if (bp != null) { -+ net.minecraft.world.level.block.entity.BlockEntity te = serverLevel.getBlockEntity(bp); ++ net.minecraft.world.level.block.entity.BlockEntity te = level.getBlockEntity(bp); + if (te instanceof net.minecraft.world.level.block.entity.SkullBlockEntity) { -+ net.minecraft.world.level.block.WitherSkullBlock.checkSpawn(serverLevel, bp, (net.minecraft.world.level.block.entity.SkullBlockEntity) te); ++ net.minecraft.world.level.block.WitherSkullBlock.checkSpawn(level, bp, (net.minecraft.world.level.block.entity.SkullBlockEntity) te); + } + } + } + + // SPIGOT-4678 -+ if (this.item instanceof SignItem && SignItem.openSign != null) { ++ if (usedItem instanceof SignItem && SignItem.openSign != null) { + try { -+ if (serverLevel.getBlockEntity(SignItem.openSign) instanceof net.minecraft.world.level.block.entity.SignBlockEntity blockEntity) { -+ if (serverLevel.getBlockState(SignItem.openSign).getBlock() instanceof net.minecraft.world.level.block.SignBlock signBlock) { ++ if (level.getBlockEntity(SignItem.openSign) instanceof net.minecraft.world.level.block.entity.SignBlockEntity blockEntity) { ++ if (level.getBlockState(SignItem.openSign).getBlock() instanceof net.minecraft.world.level.block.SignBlock signBlock) { + signBlock.openTextEdit(player, blockEntity, true, io.papermc.paper.event.player.PlayerOpenSignEvent.Cause.PLACE); // CraftBukkit // Paper - Add PlayerOpenSignEvent + } + } @@ -164,80 +163,80 @@ + } + + // SPIGOT-7315: Moved from BedBlock#setPlacedBy -+ if (placeEvent != null && this.item instanceof BedItem) { -+ BlockPos pos = ((org.bukkit.craftbukkit.block.CraftBlock) placeEvent.getBlock()).getPosition(); -+ net.minecraft.world.level.block.state.BlockState state = serverLevel.getBlockState(pos); ++ if (placeEvent != null && usedItem instanceof BedItem) { ++ BlockPos bedPos = ((org.bukkit.craftbukkit.block.CraftBlock) placeEvent.getBlock()).getPosition(); ++ net.minecraft.world.level.block.state.BlockState state = level.getBlockState(bedPos); + + if (state.getBlock() instanceof net.minecraft.world.level.block.BedBlock) { -+ serverLevel.updateNeighborsAt(pos, net.minecraft.world.level.block.Blocks.AIR); -+ state.updateNeighbourShapes(serverLevel, pos, net.minecraft.world.level.block.Block.UPDATE_ALL); ++ level.updateNeighborsAt(bedPos, net.minecraft.world.level.block.Blocks.AIR); ++ state.updateNeighbourShapes(level, bedPos, net.minecraft.world.level.block.Block.UPDATE_ALL); + } + } + + // SPIGOT-1288 - play sound stripped from BlockItem -+ if (this.item instanceof BlockItem && success.paperSuccessContext().placedBlockPosition() != null) { ++ if (usedItem instanceof BlockItem && success.paperSuccessContext().placedPos() != null) { + // Paper start - Fix spigot sound playing for BlockItem ItemStacks -+ net.minecraft.world.level.block.state.BlockState state = serverLevel.getBlockState(success.paperSuccessContext().placedBlockPosition()); ++ net.minecraft.world.level.block.state.BlockState state = level.getBlockState(success.paperSuccessContext().placedPos()); + net.minecraft.world.level.block.SoundType soundType = state.getSoundType(); + // Paper end - Fix spigot sound playing for BlockItem ItemStacks -+ serverLevel.playSound(player, clickedPos, soundType.getPlaceSound(), net.minecraft.sounds.SoundSource.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F); ++ level.playSound(player, pos, soundType.getPlaceSound(), net.minecraft.sounds.SoundSource.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F); + } + -+ player.awardStat(Stats.ITEM_USED.get(item)); ++ player.awardStat(Stats.ITEM_USED.get(usedItem)); + } } -+ serverLevel.capturedTileEntities.clear(); -+ serverLevel.capturedBlockStates.clear(); ++ level.capturedTileEntities.clear(); ++ level.capturedBlockStates.clear(); + // CraftBukkit end - return interactionResult; + return result; } -@@ -455,31 +_,67 @@ +@@ -448,31 +_,67 @@ return this.isDamageableItem() && this.getDamageValue() >= this.getMaxDamage() - 1; } -- public void hurtAndBreak(int damage, ServerLevel level, @Nullable ServerPlayer player, Consumer onBreak) { -- int i = this.processDurabilityChange(damage, level, player); -- if (i != 0) { -+ public void hurtAndBreak(int damage, ServerLevel level, @Nullable LivingEntity player, Consumer onBreak) { // Paper - Add EntityDamageItemEvent +- public void hurtAndBreak(final int amount, final ServerLevel level, final @Nullable ServerPlayer player, final Consumer onBreak) { +- int newAmount = this.processDurabilityChange(amount, level, player); +- if (newAmount != 0) { ++ public void hurtAndBreak(final int amount, final ServerLevel level, final @Nullable LivingEntity player, final Consumer onBreak) { // Paper - Add EntityDamageItemEvent + // Paper start - add force boolean overload -+ this.hurtAndBreak(damage, level, player, onBreak, false); ++ this.hurtAndBreak(amount, level, player, onBreak, false); + } + -+ public void hurtAndBreak(int damage, ServerLevel level, @Nullable LivingEntity player, Consumer onBreak, boolean force) { // Paper - Add EntityDamageItemEvent ++ public void hurtAndBreak(final int amount, final ServerLevel level, final @Nullable LivingEntity player, final Consumer onBreak, final boolean force) { // Paper - Add EntityDamageItemEvent + // Paper end -+ final int originalDamage = damage; // Paper - Expand PlayerItemDamageEvent -+ int i = this.processDurabilityChange(damage, level, player, force); // Paper ++ final int originalDamage = amount; // Paper - Expand PlayerItemDamageEvent ++ int newAmount = this.processDurabilityChange(amount, level, player, force); // Paper + // CraftBukkit start -+ if (i > 0 && player instanceof final ServerPlayer serverPlayer) { // Paper - Add EntityDamageItemEvent - limit to positive damage and run for player -+ org.bukkit.event.player.PlayerItemDamageEvent event = new org.bukkit.event.player.PlayerItemDamageEvent(serverPlayer.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this), i, originalDamage); // Paper - Add EntityDamageItemEvent ++ if (newAmount > 0 && player instanceof final ServerPlayer serverPlayer) { // Paper - Add EntityDamageItemEvent - limit to positive damage and run for player ++ org.bukkit.event.player.PlayerItemDamageEvent event = new org.bukkit.event.player.PlayerItemDamageEvent(serverPlayer.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this), newAmount, originalDamage); // Paper - Add EntityDamageItemEvent + event.getPlayer().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + -+ i = event.getDamage(); ++ newAmount = event.getDamage(); + // Paper start - Add EntityDamageItemEvent -+ } else if (i > 0 && player != null) { -+ io.papermc.paper.event.entity.EntityDamageItemEvent event = new io.papermc.paper.event.entity.EntityDamageItemEvent(player.getBukkitLivingEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this), i); ++ } else if (newAmount > 0 && player != null) { ++ io.papermc.paper.event.entity.EntityDamageItemEvent event = new io.papermc.paper.event.entity.EntityDamageItemEvent(player.getBukkitLivingEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this), newAmount); + if (!event.callEvent()) { + return; + } -+ i = event.getDamage(); ++ newAmount = event.getDamage(); + // Paper end - Add EntityDamageItemEvent + } + // CraftBukkit end -+ if (i != 0) { // Paper - Add EntityDamageItemEvent - diff on change for above event ifs. - this.applyDamage(this.getDamageValue() + i, player, onBreak); ++ if (newAmount != 0) { // Paper - Add EntityDamageItemEvent - diff on change for above event ifs. + this.applyDamage(this.getDamageValue() + newAmount, player, onBreak); } } -- private int processDurabilityChange(int damage, ServerLevel level, @Nullable ServerPlayer player) { -+ private int processDurabilityChange(int damage, ServerLevel level, @Nullable LivingEntity player) { // Paper - Add EntityDamageItemEvent +- private int processDurabilityChange(final int amount, final ServerLevel level, final @Nullable ServerPlayer player) { ++ private int processDurabilityChange(final int amount, final ServerLevel level, final @Nullable LivingEntity player) { // Paper - Add EntityDamageItemEvent + // Paper start - itemstack damage api -+ return processDurabilityChange(damage, level, player, false); ++ return this.processDurabilityChange(amount, level, player, false); + } -+ private int processDurabilityChange(int damage, ServerLevel level, @Nullable LivingEntity player, boolean force) { ++ private int processDurabilityChange(final int amount, final ServerLevel level, final @Nullable LivingEntity player, final boolean force) { + // Paper end - itemstack damage api if (!this.isDamageableItem()) { return 0; @@ -245,19 +244,19 @@ + } else if (player instanceof ServerPlayer && player.hasInfiniteMaterials() && !force) { // Paper - Add EntityDamageItemEvent return 0; } else { - return damage > 0 ? EnchantmentHelper.processDurabilityChange(level, this, damage) : damage; + return amount > 0 ? EnchantmentHelper.processDurabilityChange(level, this, amount) : amount; } } -- private void applyDamage(int damage, @Nullable ServerPlayer player, Consumer onBreak) { +- private void applyDamage(final int newDamage, final @Nullable ServerPlayer player, final Consumer onBreak) { - if (player != null) { -- CriteriaTriggers.ITEM_DURABILITY_CHANGED.trigger(player, this, damage); -+ private void applyDamage(int damage, @Nullable LivingEntity player, Consumer onBreak) { // Paper - Add EntityDamageItemEvent +- CriteriaTriggers.ITEM_DURABILITY_CHANGED.trigger(player, this, newDamage); ++ private void applyDamage(final int newDamage, final @Nullable LivingEntity player, final Consumer onBreak) { // Paper - Add EntityDamageItemEvent + if (player instanceof final ServerPlayer serverPlayer) { // Paper - Add EntityDamageItemEvent -+ CriteriaTriggers.ITEM_DURABILITY_CHANGED.trigger(serverPlayer, this, damage); // Paper - Add EntityDamageItemEvent ++ CriteriaTriggers.ITEM_DURABILITY_CHANGED.trigger(serverPlayer, this, newDamage); // Paper - Add EntityDamageItemEvent } - this.setDamageValue(damage); + this.setDamageValue(newDamage); if (this.isBroken()) { Item item = this.getItem(); + // CraftBukkit start - Check for item breaking @@ -268,20 +267,20 @@ this.shrink(1); onBreak.accept(item); } -@@ -492,7 +_,26 @@ +@@ -485,7 +_,26 @@ return; } -- int min = Math.min(this.getDamageValue() + i, this.getMaxDamage() - 1); -+ int min = Math.min(this.getDamageValue() + i, this.getMaxDamage() - 1); // Paper - Expand PlayerItemDamageEvent - diff on change as min computation is copied post event. +- int newDamage = Math.min(this.getDamageValue() + newAmount, this.getMaxDamage() - 1); ++ int newDamage = Math.min(this.getDamageValue() + newAmount, this.getMaxDamage() - 1); // Paper - Expand PlayerItemDamageEvent - diff on change as min computation is copied post event. + + // Paper start - Expand PlayerItemDamageEvent -+ if (min - this.getDamageValue() > 0) { ++ if (newDamage - this.getDamageValue() > 0) { + org.bukkit.event.player.PlayerItemDamageEvent event = new org.bukkit.event.player.PlayerItemDamageEvent( + serverPlayer.getBukkitEntity(), + org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this), -+ min - this.getDamageValue(), -+ damage ++ newDamage - this.getDamageValue(), ++ amount + ); + if (!event.callEvent() || event.getDamage() == 0) { + return; @@ -289,30 +288,30 @@ + + // Prevent breaking the item in this code path as callers may expect the item to survive + // (given the method name) -+ min = Math.min(this.getDamageValue() + event.getDamage(), this.getMaxDamage() - 1); ++ newDamage = Math.min(this.getDamageValue() + event.getDamage(), this.getMaxDamage() - 1); + } + // Paper end - Expand PlayerItemDamageEvent + - this.applyDamage(min, serverPlayer, item -> {}); + this.applyDamage(newDamage, serverPlayer, i -> {}); } } -@@ -502,9 +_,14 @@ +@@ -495,9 +_,14 @@ } - public void hurtAndBreak(int amount, LivingEntity entity, EquipmentSlot slot) { + public void hurtAndBreak(final int amount, final LivingEntity owner, final EquipmentSlot slot) { + // Paper start - add param to skip infinite mats check -+ this.hurtAndBreak(amount, entity, slot, false); ++ this.hurtAndBreak(amount, owner, slot, false); + } -+ public void hurtAndBreak(int amount, LivingEntity entity, EquipmentSlot slot, boolean force) { ++ public void hurtAndBreak(final int amount, final LivingEntity owner, final EquipmentSlot slot, final boolean force) { + // Paper end - add param to skip infinite mats check - if (entity.level() instanceof ServerLevel serverLevel) { + if (owner.level() instanceof ServerLevel serverLevel) { this.hurtAndBreak( -- amount, serverLevel, entity instanceof ServerPlayer serverPlayer ? serverPlayer : null, item -> entity.onEquippedItemBroken(item, slot) -+ amount, serverLevel, entity, item -> {if (slot != null) entity.onEquippedItemBroken(item, slot); }, force // Paper - Add EntityDamageItemEvent & itemstack damage API - do not process entity related callbacks when damaging from API +- amount, serverLevel, owner instanceof ServerPlayer player ? player : null, brokenItem -> owner.onEquippedItemBroken(brokenItem, slot) ++ amount, serverLevel, owner, brokenItem -> {if (slot != null) owner.onEquippedItemBroken(brokenItem, slot); }, force // Paper - Add EntityDamageItemEvent & itemstack damage API - do not process entity related callbacks when damaging from API ); } } -@@ -753,6 +_,12 @@ +@@ -748,6 +_,12 @@ return this.getItem().useOnRelease(this); } @@ -322,10 +321,10 @@ + } + // CraftBukkit end + - public @Nullable T set(DataComponentType component, @Nullable T value) { - return this.components.set(component, value); + public @Nullable T set(final DataComponentType type, final @Nullable T value) { + return this.components.set(type, value); } -@@ -796,6 +_,28 @@ +@@ -791,6 +_,28 @@ this.components.setAll(components); } @@ -354,15 +353,15 @@ public Component getHoverName() { Component customName = this.getCustomName(); return customName != null ? customName : this.getItemName(); -@@ -1026,6 +_,19 @@ - EnchantmentHelper.forEachModifier(this, slot, action); +@@ -1006,6 +_,19 @@ + EnchantmentHelper.forEachModifier(this, slot, consumer); } + // CraftBukkit start + @Deprecated + public void setItem(Item item) { -+ this.bukkitStack = null; // Paper -+ this.item = item; ++ this.bukkitStack = null; ++ this.item = item.builtInRegistryHolder(); + // Paper start - change base component prototype + final DataComponentPatch patch = this.getComponentsPatch(); + this.components = new PatchedDataComponentMap(this.item.components()); @@ -372,14 +371,14 @@ + // CraftBukkit end + public Component getDisplayName() { - MutableComponent mutableComponent = Component.empty().append(this.getHoverName()); + MutableComponent hoverName = Component.empty().append(this.getHoverName()); if (this.has(DataComponents.CUSTOM_NAME)) { -@@ -1085,7 +_,7 @@ +@@ -1070,7 +_,7 @@ } - public void consume(int amount, @Nullable LivingEntity entity) { -- if (entity == null || !entity.hasInfiniteMaterials()) { -+ if ((entity == null || !entity.hasInfiniteMaterials()) && this != ItemStack.EMPTY) { // CraftBukkit + public void consume(final int amount, final @Nullable LivingEntity owner) { +- if (owner == null || !owner.hasInfiniteMaterials()) { ++ if ((owner == null || !owner.hasInfiniteMaterials()) && this != ItemStack.EMPTY) { // CraftBukkit this.shrink(amount); } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/ItemUtils.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ItemUtils.java.patch index 72de90ab1c6c..3a9b7477d9e9 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ItemUtils.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ItemUtils.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/world/item/ItemUtils.java +++ b/net/minecraft/world/item/ItemUtils.java -@@ -41,7 +_,15 @@ - public static void onContainerDestroyed(ItemEntity container, Iterable contents) { +@@ -44,7 +_,15 @@ + public static void onContainerDestroyed(final ItemEntity container, final Stream contents) { Level level = container.level(); if (!level.isClientSide()) { -- contents.forEach(itemStack -> level.addFreshEntity(new ItemEntity(level, container.getX(), container.getY(), container.getZ(), itemStack))); +- contents.forEach(stack -> level.addFreshEntity(new ItemEntity(level, container.getX(), container.getY(), container.getZ(), stack))); + // Paper start - call EntityDropItemEvent -+ contents.forEach(itemStack -> { -+ ItemEntity droppedItem = new ItemEntity(level, container.getX(), container.getY(), container.getZ(), itemStack); ++ contents.forEach(stack -> { ++ ItemEntity droppedItem = new ItemEntity(level, container.getX(), container.getY(), container.getZ(), stack); + org.bukkit.event.entity.EntityDropItemEvent event = new org.bukkit.event.entity.EntityDropItemEvent(container.getBukkitEntity(), (org.bukkit.entity.Item) droppedItem.getBukkitEntity()); + if (event.callEvent()) { + level.addFreshEntity(droppedItem); diff --git a/paper-server/patches/sources/net/minecraft/world/item/LeadItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/LeadItem.java.patch index 5d84bec664fe..7f93debcac9a 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/LeadItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/LeadItem.java.patch @@ -1,58 +1,47 @@ --- a/net/minecraft/world/item/LeadItem.java +++ b/net/minecraft/world/item/LeadItem.java -@@ -26,25 +_,38 @@ - if (blockState.is(BlockTags.FENCES)) { +@@ -27,24 +_,36 @@ + if (state.is(BlockTags.FENCES)) { Player player = context.getPlayer(); if (!level.isClientSide() && player != null) { -- return bindPlayerMobs(player, level, clickedPos); -+ return bindPlayerMobs(player, level, clickedPos, context.getHand()); // CraftBukkit - Pass hand +- return bindPlayerMobs(player, level, pos); ++ return bindPlayerMobs(player, level, pos, context.getHand()); // CraftBukkit - Pass hand } } return InteractionResult.PASS; } -- public static InteractionResult bindPlayerMobs(Player player, Level level, BlockPos pos) { -+ public static InteractionResult bindPlayerMobs(Player player, Level level, BlockPos pos, net.minecraft.world.InteractionHand interactionHand) { // CraftBukkit - Add InteractionHand - LeashFenceKnotEntity leashFenceKnotEntity = null; - List list = Leashable.leashableInArea(level, Vec3.atCenterOf(pos), leashable1 -> leashable1.getLeashHolder() == player); - boolean flag = false; - - for (Leashable leashable : list) { - if (leashFenceKnotEntity == null) { -- leashFenceKnotEntity = LeashFenceKnotEntity.getOrCreateKnot(level, pos); -+ // CraftBukkit start - fire HangingPlaceEvent -+ org.apache.commons.lang3.mutable.MutableBoolean created = new org.apache.commons.lang3.mutable.MutableBoolean(false); -+ leashFenceKnotEntity = LeashFenceKnotEntity.getOrCreateKnot(level, pos, created); -+ if (created.booleanValue()) { -+ org.bukkit.inventory.EquipmentSlot hand = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(interactionHand); -+ org.bukkit.event.hanging.HangingPlaceEvent event = new org.bukkit.event.hanging.HangingPlaceEvent((org.bukkit.entity.Hanging) leashFenceKnotEntity.getBukkitEntity(), player != null ? (org.bukkit.entity.Player) player.getBukkitEntity() : null, org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.block.BlockFace.SELF, hand); -+ level.getCraftServer().getPluginManager().callEvent(event); +- public static InteractionResult bindPlayerMobs(final Player player, final Level level, final BlockPos pos) { ++ public static InteractionResult bindPlayerMobs(final Player player, final Level level, final BlockPos pos, final net.minecraft.world.InteractionHand hand) { // CraftBukkit - Add InteractionHand + List entitiesToLeash = Leashable.leashableInArea(level, Vec3.atCenterOf(pos), l -> l.getLeashHolder() == player); + if (entitiesToLeash.isEmpty()) { + return InteractionResult.PASS; + } else { + Optional existingKnot = LeashFenceKnotEntity.getKnot(level, pos); + LeashFenceKnotEntity activeKnot = existingKnot.orElseGet(() -> LeashFenceKnotEntity.createKnot(level, pos)); ++ // CraftBukkit start - fire HangingPlaceEvent ++ if (existingKnot.isEmpty()) { ++ org.bukkit.inventory.EquipmentSlot handSlot = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand); ++ org.bukkit.event.hanging.HangingPlaceEvent event = new org.bukkit.event.hanging.HangingPlaceEvent((org.bukkit.entity.Hanging) activeKnot.getBukkitEntity(), player != null ? (org.bukkit.entity.Player) player.getBukkitEntity() : null, org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.block.BlockFace.SELF, handSlot); ++ level.getCraftServer().getPluginManager().callEvent(event); + -+ if (event.isCancelled()) { -+ leashFenceKnotEntity.discard(null); -+ return InteractionResult.PASS; -+ } ++ if (event.isCancelled()) { ++ activeKnot.discard(); ++ return InteractionResult.PASS; + } -+ // CraftBukkit end - leashFenceKnotEntity.playPlacementSound(); - } - -- if (leashable.canHaveALeashAttachedTo(leashFenceKnotEntity)) { -+ if (leashable.canHaveALeashAttachedTo(leashFenceKnotEntity) && org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerLeashEntityEvent(leashable, leashFenceKnotEntity, player, interactionHand)) { // Paper - leash event - leashable.setLeashedTo(leashFenceKnotEntity, true); - flag = true; - } -@@ -54,7 +_,18 @@ - level.gameEvent(GameEvent.BLOCK_ATTACH, pos, GameEvent.Context.of(player)); - return InteractionResult.SUCCESS_SERVER; - } else { -+ // CraftBukkit start - remove leash if we do not leash any entity because of the cancelled event -+ if (leashFenceKnotEntity != null) { -+ leashFenceKnotEntity.discard(null); + } + // CraftBukkit end - return InteractionResult.PASS; + boolean anyLeashed = false; + + for (Leashable leashable : entitiesToLeash) { +- if (leashable.canHaveALeashAttachedTo(activeKnot)) { ++ if (leashable.canHaveALeashAttachedTo(activeKnot) && org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerLeashEntityEvent(leashable, activeKnot, player, hand)) { // Paper - leash event + leashable.setLeashedTo(activeKnot, true); + anyLeashed = true; + } +@@ -63,4 +_,10 @@ + } } } + diff --git a/paper-server/patches/sources/net/minecraft/world/item/LingeringPotionItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/LingeringPotionItem.java.patch index f4ab3c1e4d0f..7d1956feb87a 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/LingeringPotionItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/LingeringPotionItem.java.patch @@ -3,7 +3,7 @@ @@ -19,6 +_,10 @@ @Override - public InteractionResult use(Level level, Player player, InteractionHand hand) { + public InteractionResult use(final Level level, final Player player, final InteractionHand hand) { + // Paper start - PlayerLaunchProjectileEvent + final InteractionResult wrapper = super.use(level, player, hand); + if (wrapper instanceof InteractionResult.Fail) return wrapper; diff --git a/paper-server/patches/sources/net/minecraft/world/item/MaceItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/MaceItem.java.patch index 5d1dddfbae69..c770adf7c823 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/MaceItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/MaceItem.java.patch @@ -3,24 +3,24 @@ @@ -54,7 +_,13 @@ @Override - public void hurtEnemy(ItemStack stack, LivingEntity target, LivingEntity attacker) { + public void hurtEnemy(final ItemStack itemStack, final LivingEntity mob, final LivingEntity attacker) { - if (canSmashAttack(attacker)) { + // Paper start - Add EntityAttemptSmashAttackEvent + final boolean canSmashAttack = canSmashAttack(attacker); -+ io.papermc.paper.event.entity.EntityAttemptSmashAttackEvent event = new io.papermc.paper.event.entity.EntityAttemptSmashAttackEvent(attacker.getBukkitLivingEntity(), target.getBukkitLivingEntity(), stack.asBukkitCopy(), canSmashAttack); ++ io.papermc.paper.event.entity.EntityAttemptSmashAttackEvent event = new io.papermc.paper.event.entity.EntityAttemptSmashAttackEvent(attacker.getBukkitLivingEntity(), mob.getBukkitLivingEntity(), itemStack.asBukkitCopy(), canSmashAttack); + event.callEvent(); + final org.bukkit.event.Event.Result result = event.getResult(); + if (result == org.bukkit.event.Event.Result.ALLOW || (canSmashAttack && result == org.bukkit.event.Event.Result.DEFAULT)) { + // Paper end - Add EntityAttemptSmashAttackEvent - ServerLevel serverLevel = (ServerLevel)attacker.level(); + ServerLevel level = (ServerLevel)attacker.level(); attacker.setDeltaMovement(attacker.getDeltaMovement().with(Direction.Axis.Y, 0.01F)); - if (attacker instanceof ServerPlayer serverPlayer) { -@@ -129,7 +_,7 @@ - double knockbackPower = getKnockbackPower(attacker, livingEntity, vec3); - Vec3 vec31 = vec3.normalize().scale(knockbackPower); + attacker.setIgnoreFallDamageFromCurrentImpulse(true, this.calculateImpactPosition(attacker)); +@@ -124,7 +_,7 @@ + double knockbackPower = getKnockbackPower(attacker, nearby, direction); + Vec3 knockbackVector = direction.normalize().scale(knockbackPower); if (knockbackPower > 0.0) { -- livingEntity.push(vec31.x, 0.7F, vec31.z); -+ livingEntity.push(vec31.x, 0.7F, vec31.z, attacker); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent - if (livingEntity instanceof ServerPlayer serverPlayer) { - serverPlayer.connection.send(new ClientboundSetEntityMotionPacket(serverPlayer)); +- nearby.push(knockbackVector.x, 0.7F, knockbackVector.z); ++ nearby.push(knockbackVector.x, 0.7F, knockbackVector.z, attacker); // Paper - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent + if (nearby instanceof ServerPlayer otherPlayer) { + otherPlayer.connection.send(new ClientboundSetEntityMotionPacket(otherPlayer)); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/MapItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/MapItem.java.patch index 07a53be2f8aa..da33116353e1 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/MapItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/MapItem.java.patch @@ -1,22 +1,24 @@ --- a/net/minecraft/world/item/MapItem.java +++ b/net/minecraft/world/item/MapItem.java -@@ -95,8 +_,8 @@ - int i9 = (i1 / i + i6 - 64) * i; - int i10 = (i2 / i + i7 - 64) * i; - Multiset multiset = LinkedHashMultiset.create(); -- LevelChunk chunk = level.getChunk(SectionPos.blockToSectionCoord(i9), SectionPos.blockToSectionCoord(i10)); +@@ -103,10 +_,10 @@ + int averagingAreaMinX = (centerX / scale + imgX - 64) * scale; + int averagingAreaMinZ = (centerZ / scale + imgY - 64) * scale; + Multiset colorCount = LinkedHashMultiset.create(); +- LevelChunk chunk = level.getChunk( ++ LevelChunk chunk = level.getChunkIfLoaded( // Paper - Maps shouldn't load chunks + SectionPos.blockToSectionCoord(averagingAreaMinX), SectionPos.blockToSectionCoord(averagingAreaMinZ) + ); - if (!chunk.isEmpty()) { -+ LevelChunk chunk = level.getChunkIfLoaded(SectionPos.blockToSectionCoord(i9), SectionPos.blockToSectionCoord(i10)); // Paper - Maps shouldn't load chunks + if (chunk != null && !chunk.isEmpty()) { // Paper - Maps shouldn't load chunks - int i11 = 0; - double d1 = 0.0; + int waterDepth = 0; + double averageAreaHeight = 0.0; if (level.dimensionType().hasCeiling()) { -@@ -203,7 +_,7 @@ +@@ -213,7 +_,7 @@ - for (int i5 = 0; i5 < 128; i5++) { - for (int i6 = 0; i6 < 128; i6++) { -- Holder biome = level.getBiome(mutableBlockPos.set((i3 + i6) * i, 0, (i4 + i5) * i)); -+ Holder biome = level.getUncachedNoiseBiome(net.minecraft.core.QuartPos.fromBlock((i3 + i6) * i), net.minecraft.core.QuartPos.fromBlock(0), net.minecraft.core.QuartPos.fromBlock((i4 + i5) * i)); // Paper - Perf: Use seed based lookup for treasure maps - flags[i5 * 128 + i6] = biome.is(BiomeTags.WATER_ON_MAP_OUTLINES); + for (int row = 0; row < 128; row++) { + for (int column = 0; column < 128; column++) { +- Holder biome = level.getBiome(pos.set((unscaledStartX + column) * scale, 0, (unscaledStartZ + row) * scale)); ++ Holder biome = level.getUncachedNoiseBiome(net.minecraft.core.QuartPos.fromBlock((unscaledStartX + column) * scale), net.minecraft.core.QuartPos.fromBlock(0), net.minecraft.core.QuartPos.fromBlock((unscaledStartZ + row) * scale)); // Paper - Perf: Use seed based lookup for treasure maps + isBiomeWatery[row * 128 + column] = biome.is(BiomeTags.WATER_ON_MAP_OUTLINES); } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/MinecartItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/MinecartItem.java.patch index 20602408ae1a..70ccca3b2790 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/MinecartItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/MinecartItem.java.patch @@ -4,14 +4,14 @@ } if (level instanceof ServerLevel serverLevel) { -- serverLevel.addFreshEntity(abstractMinecart); +- serverLevel.addFreshEntity(cart); + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(context, abstractMinecart).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(context, cart).isCancelled()) { + if (context.getPlayer() != null) context.getPlayer().containerMenu.forceHeldSlot(context.getHand()); // Paper - Fix inventory desync + return InteractionResult.FAIL; + } + // CraftBukkit end -+ if (!serverLevel.addFreshEntity(abstractMinecart)) return InteractionResult.PASS; // CraftBukkit - serverLevel.gameEvent( - GameEvent.ENTITY_PLACE, clickedPos, GameEvent.Context.of(context.getPlayer(), serverLevel.getBlockState(clickedPos.below())) - ); ++ if (!serverLevel.addFreshEntity(cart)) return InteractionResult.PASS; // CraftBukkit + serverLevel.gameEvent(GameEvent.ENTITY_PLACE, pos, GameEvent.Context.of(context.getPlayer(), serverLevel.getBlockState(pos.below()))); + } + diff --git a/paper-server/patches/sources/net/minecraft/world/item/NameTagItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/NameTagItem.java.patch index bdaa9c8bff13..5ec1f516e675 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/NameTagItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/NameTagItem.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/world/item/NameTagItem.java +++ b/net/minecraft/world/item/NameTagItem.java @@ -18,8 +_,14 @@ - Component component = stack.get(DataComponents.CUSTOM_NAME); - if (component != null && target.getType().canSerialize()) { + Component customName = itemStack.get(DataComponents.CUSTOM_NAME); + if (customName != null && target.getType().canSerialize()) { if (!player.level().isClientSide() && target.isAlive()) { -- target.setCustomName(component); +- target.setCustomName(customName); - if (target instanceof Mob mob) { + // Paper start - Add PlayerNameEntityEvent -+ io.papermc.paper.event.player.PlayerNameEntityEvent event = new io.papermc.paper.event.player.PlayerNameEntityEvent(((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity(), target.getBukkitLivingEntity(), io.papermc.paper.adventure.PaperAdventure.asAdventure(stack.getHoverName()), true); ++ io.papermc.paper.event.player.PlayerNameEntityEvent event = new io.papermc.paper.event.player.PlayerNameEntityEvent(((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity(), target.getBukkitLivingEntity(), io.papermc.paper.adventure.PaperAdventure.asAdventure(customName), true); + if (!event.callEvent()) return InteractionResult.PASS; + + LivingEntity newEntity = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getEntity()).getHandle(); diff --git a/paper-server/patches/sources/net/minecraft/world/item/PotionItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/PotionItem.java.patch index 89035150a173..016c49bf6b47 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/PotionItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/PotionItem.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/item/PotionItem.java +++ b/net/minecraft/world/item/PotionItem.java @@ -40,6 +_,16 @@ - PotionContents potionContents = itemInHand.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); - BlockState blockState = level.getBlockState(clickedPos); + PotionContents potionContents = itemStack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); + BlockState blockState = level.getBlockState(pos); if (context.getClickedFace() != Direction.DOWN && blockState.is(BlockTags.CONVERTABLE_TO_MUD) && potionContents.is(Potions.WATER)) { + // Paper start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, clickedPos, Blocks.MUD.defaultBlockState())) { -+ if (itemInHand.getCount() > 1 || player.hasInfiniteMaterials()) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, Blocks.MUD.defaultBlockState())) { ++ if (itemStack.getCount() > 1 || player.hasInfiniteMaterials()) { + player.containerMenu.sendAllDataToRemote(); + } else { + player.containerMenu.forceHeldSlot(context.getHand()); @@ -14,6 +14,6 @@ + return InteractionResult.PASS; + } + // Paper end - level.playSound(null, clickedPos, SoundEvents.GENERIC_SPLASH, SoundSource.BLOCKS, 1.0F, 1.0F); - player.setItemInHand(context.getHand(), ItemUtils.createFilledResult(itemInHand, player, new ItemStack(Items.GLASS_BOTTLE))); + level.playSound(null, pos, SoundEvents.GENERIC_SPLASH, SoundSource.BLOCKS, 1.0F, 1.0F); + player.setItemInHand(context.getHand(), ItemUtils.createFilledResult(itemStack, player, new ItemStack(Items.GLASS_BOTTLE))); if (!level.isClientSide()) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/ProjectileWeaponItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ProjectileWeaponItem.java.patch index c3731d881146..c87d9272fa57 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ProjectileWeaponItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ProjectileWeaponItem.java.patch @@ -1,64 +1,65 @@ --- a/net/minecraft/world/item/ProjectileWeaponItem.java +++ b/net/minecraft/world/item/ProjectileWeaponItem.java @@ -50,6 +_,7 @@ - float inaccuracy, - boolean isCrit, - @Nullable LivingEntity target -+ ,float drawStrength // Paper - Pass draw strength + final float uncertainty, + final boolean isCrit, + final @Nullable LivingEntity targetOverride ++ , final float drawStrength // Paper - Pass draw strength ) { - float f = EnchantmentHelper.processProjectileSpread(level, weapon, shooter, 0.0F); - float f1 = projectileItems.size() == 1 ? 0.0F : 2.0F * f / (projectileItems.size() - 1); + float maxAngle = EnchantmentHelper.processProjectileSpread(level, weapon, shooter, 0.0F); + float angleStep = projectiles.size() == 1 ? 0.0F : 2.0F * maxAngle / (projectiles.size() - 1); @@ -62,12 +_,26 @@ - float f4 = f2 + f3 * ((i + 1) / 2) * f1; - f3 = -f3; - int i1 = i; + float angle = angleOffset + direction * ((i + 1) / 2) * angleStep; + direction = -direction; + int index = i; - Projectile.spawnProjectile( -- this.createProjectile(level, shooter, weapon, itemStack, isCrit), +- this.createProjectile(level, shooter, weapon, projectile, isCrit), - level, -- itemStack, -- projectile -> this.shootProjectile(shooter, projectile, i1, velocity, inaccuracy, f4, target) +- projectile, +- projectileEntity -> this.shootProjectile(shooter, projectileEntity, index, power, uncertainty, angle, targetOverride) - ); + // CraftBukkit start -+ Projectile projectile = this.createProjectile(level, shooter, weapon, itemStack, isCrit); -+ this.shootProjectile(shooter, projectile, i1, velocity, inaccuracy, f4, target); ++ Projectile projectileEntity = this.createProjectile(level, shooter, weapon, projectile, isCrit); ++ this.shootProjectile(shooter, projectileEntity, index, power, uncertainty, angle, targetOverride); + -+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(shooter, weapon, itemStack, projectile, hand, drawStrength, true); ++ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(shooter, weapon, projectile, projectileEntity, hand, drawStrength, true); + if (event.isCancelled()) { + event.getProjectile().remove(); + return; + } + -+ if (event.getProjectile() == projectile.getBukkitEntity()) { ++ if (event.getProjectile() == projectileEntity.getBukkitEntity()) { + if (Projectile.spawnProjectile( -+ projectile, ++ projectileEntity, + level, -+ itemStack ++ projectile + ).isRemoved()) { + return; + } + } + // CraftBukkit end - weapon.hurtAndBreak(this.getDurabilityUse(itemStack), shooter, hand.asEquipmentSlot()); + weapon.hurtAndBreak(this.getDurabilityUse(projectile), shooter, hand.asEquipmentSlot()); if (weapon.isEmpty()) { break; -@@ -95,6 +_,11 @@ +@@ -103,6 +_,12 @@ } - protected static List draw(ItemStack weapon, ItemStack ammo, LivingEntity shooter) { + protected static List draw(final ItemStack weapon, final ItemStack projectile, final LivingEntity shooter) { + // Paper start -+ return draw(weapon, ammo, shooter, true); ++ return draw(weapon, projectile, shooter, true); + } -+ protected static List draw(ItemStack weapon, ItemStack ammo, LivingEntity shooter, boolean consume) { ++ ++ protected static List draw(final ItemStack weapon, final ItemStack projectile, final LivingEntity shooter, final boolean consume) { + // Paper end - if (ammo.isEmpty()) { + if (projectile.isEmpty()) { return List.of(); } else { -@@ -103,7 +_,7 @@ - ItemStack itemStack = ammo.copy(); +@@ -113,7 +_,7 @@ + ItemStack projectileCopy = projectile.copy(); - for (int i1 = 0; i1 < i; i1++) { -- ItemStack itemStack1 = useAmmo(weapon, i1 == 0 ? ammo : itemStack, shooter, i1 > 0); -+ ItemStack itemStack1 = useAmmo(weapon, i1 == 0 ? ammo : itemStack, shooter, i1 > 0 || !consume); // Paper - if (!itemStack1.isEmpty()) { - list.add(itemStack1); + for (int i = 0; i < numProjectiles; i++) { +- ItemStack drawnStack = useAmmo(weapon, i == 0 ? projectile : projectileCopy, shooter, i > 0); ++ ItemStack drawnStack = useAmmo(weapon, i == 0 ? projectile : projectileCopy, shooter, i > 0 || !consume); // Paper + if (!drawnStack.isEmpty()) { + drawn.add(drawnStack); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/ServerItemCooldowns.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ServerItemCooldowns.java.patch index 3f975521c9f1..9cbecc8fa477 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ServerItemCooldowns.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ServerItemCooldowns.java.patch @@ -30,24 +30,24 @@ + } + + @Override -+ public void addCooldown(Identifier groupId, int duration, boolean callEvent) { ++ public void addCooldown(Identifier cooldownGroup, int time, boolean callEvent) { + if (callEvent) { + final io.papermc.paper.event.player.PlayerItemGroupCooldownEvent event = new io.papermc.paper.event.player.PlayerItemGroupCooldownEvent( + this.player.getBukkitEntity(), -+ org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(groupId), -+ duration ++ org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(cooldownGroup), ++ time + ); + if (!event.callEvent()) { -+ this.player.connection.send(new ClientboundCooldownPacket(groupId, this.getCurrentCooldown(groupId))); ++ this.player.connection.send(new ClientboundCooldownPacket(cooldownGroup, this.getCurrentCooldown(cooldownGroup))); + return; + } + -+ duration = event.getCooldown(); ++ time = event.getCooldown(); + } -+ super.addCooldown(groupId, duration, false); ++ super.addCooldown(cooldownGroup, time, false); + } + // Paper end - Add PlayerItemCooldownEvent + @Override - protected void onCooldownStarted(Identifier group, int cooldown) { - super.onCooldownStarted(group, cooldown); + protected void onCooldownStarted(final Identifier cooldownGroup, final int duration) { + super.onCooldownStarted(cooldownGroup, duration); diff --git a/paper-server/patches/sources/net/minecraft/world/item/ShovelItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ShovelItem.java.patch index 01d29fae90da..97a7cf9785fa 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ShovelItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ShovelItem.java.patch @@ -2,32 +2,32 @@ +++ b/net/minecraft/world/item/ShovelItem.java @@ -45,20 +_,29 @@ Player player = context.getPlayer(); - BlockState blockState1 = FLATTENABLES.get(blockState.getBlock()); - BlockState blockState2 = null; + BlockState newState = FLATTENABLES.get(blockState.getBlock()); + BlockState updatedState = null; + Runnable afterAction = null; // Paper - if (blockState1 != null && level.getBlockState(clickedPos.above()).isAir()) { -- level.playSound(player, clickedPos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F); -+ afterAction = () -> level.playSound(player, clickedPos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F); // Paper - blockState2 = blockState1; + if (newState != null && level.getBlockState(pos.above()).isAir()) { +- level.playSound(player, pos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F); ++ afterAction = () -> level.playSound(player, pos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F); // Paper + updatedState = newState; } else if (blockState.getBlock() instanceof CampfireBlock && blockState.getValue(CampfireBlock.LIT)) { + afterAction = () -> { // Paper if (!level.isClientSide()) { - level.levelEvent(null, LevelEvent.SOUND_EXTINGUISH_FIRE, clickedPos, 0); + level.levelEvent(null, LevelEvent.SOUND_EXTINGUISH_FIRE, pos, 0); } - CampfireBlock.dowse(context.getPlayer(), level, clickedPos, blockState); + CampfireBlock.dowse(context.getPlayer(), level, pos, blockState); + }; // Paper - blockState2 = blockState.setValue(CampfireBlock.LIT, false); + updatedState = blockState.setValue(CampfireBlock.LIT, false); } - if (blockState2 != null) { + if (updatedState != null) { if (!level.isClientSide()) { + // Paper start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(context.getPlayer(), clickedPos, blockState2)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(context.getPlayer(), pos, updatedState)) { + return InteractionResult.PASS; + } + afterAction.run(); + // Paper end - level.setBlock(clickedPos, blockState2, Block.UPDATE_ALL_IMMEDIATE); - level.gameEvent(GameEvent.BLOCK_CHANGE, clickedPos, GameEvent.Context.of(player, blockState2)); + level.setBlock(pos, updatedState, Block.UPDATE_ALL_IMMEDIATE); + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(player, updatedState)); if (player != null) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/SignItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/SignItem.java.patch index eb805f28709f..55d79b84c5e4 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/SignItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/SignItem.java.patch @@ -5,18 +5,18 @@ public class SignItem extends StandingAndWallBlockItem { + public static BlockPos openSign; // CraftBukkit - public SignItem(Block standingBlock, Block wallBlock, Item.Properties properties) { - super(standingBlock, wallBlock, Direction.DOWN, properties); + public SignItem(final Block sign, final Block wallSign, final Item.Properties properties) { + super(sign, wallSign, Direction.DOWN, properties); } -@@ -27,7 +_,10 @@ +@@ -29,7 +_,10 @@ && player != null - && level.getBlockEntity(pos) instanceof SignBlockEntity signBlockEntity - && level.getBlockState(pos).getBlock() instanceof SignBlock signBlock) { -- signBlock.openTextEdit(player, signBlockEntity, true); + && level.getBlockEntity(pos) instanceof SignBlockEntity signEntity + && level.getBlockState(pos).getBlock() instanceof SignBlock sign) { +- sign.openTextEdit(player, signEntity, true); + // CraftBukkit start - SPIGOT-4678 -+ // signBlock.openTextEdit(player, signBlockEntity, true); ++ // sign.openTextEdit(player, signEntity, true); + SignItem.openSign = pos; + // CraftBukkit end } - return flag; + return success; diff --git a/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch index 5f8381a2e0d5..0f4722e438a9 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/item/SnowballItem.java +++ b/net/minecraft/world/item/SnowballItem.java -@@ -23,22 +_,41 @@ +@@ -23,22 +_,40 @@ @Override - public InteractionResult use(Level level, Player player, InteractionHand hand) { - ItemStack itemInHand = player.getItemInHand(hand); + public InteractionResult use(final Level level, final Player player, final InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); - level.playSound( - null, - player.getX(), @@ -16,19 +16,18 @@ - ); + // CraftBukkit start - moved down if (level instanceof ServerLevel serverLevel) { -- Projectile.spawnProjectileFromRotation(Snowball::new, serverLevel, itemInHand, player, 0.0F, PROJECTILE_SHOOT_POWER, 1.0F); +- Projectile.spawnProjectileFromRotation(Snowball::new, serverLevel, itemStack, player, 0.0F, 1.5F, 1.0F); + // Paper start - PlayerLaunchProjectileEvent -+ final Projectile.Delayed snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, serverLevel, itemInHand, player, 0.0F, SnowballItem.PROJECTILE_SHOOT_POWER, 1.0F); -+ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Projectile) snowball.projectile().getBukkitEntity()); ++ final Projectile.Delayed snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, serverLevel, itemStack, player, 0.0F, SnowballItem.PROJECTILE_SHOOT_POWER, 1.0F); ++ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) snowball.projectile().getBukkitEntity()); + if (event.callEvent() && snowball.attemptSpawn()) { + player.awardStat(Stats.ITEM_USED.get(this)); + if (event.shouldConsume()) { -+ itemInHand.consume(1, player); ++ itemStack.consume(1, player); + } else { + player.containerMenu.forceHeldSlot(hand); + } + // Paper end - PlayerLaunchProjectileEvent -+ + level.playSound( + null, + player.getX(), @@ -49,9 +48,9 @@ } - player.awardStat(Stats.ITEM_USED.get(this)); -- itemInHand.consume(1, player); +- itemStack.consume(1, player); + // Paper - PlayerLaunchProjectileEvent - moved up -+ // itemInHand.consume(1, player); // CraftBukkit - moved up ++ // itemStack.consume(1, player); // CraftBukkit - moved up return InteractionResult.SUCCESS; } diff --git a/paper-server/patches/sources/net/minecraft/world/item/SpawnEggItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/SpawnEggItem.java.patch index 9ce8994289c2..55e947834b83 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/SpawnEggItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/SpawnEggItem.java.patch @@ -1,28 +1,28 @@ --- a/net/minecraft/world/item/SpawnEggItem.java +++ b/net/minecraft/world/item/SpawnEggItem.java -@@ -69,6 +_,7 @@ +@@ -61,6 +_,7 @@ return InteractionResult.FAIL; } else { + if (level.paperConfig().entities.spawning.disableMobSpawnerSpawnEggTransformation) return InteractionResult.FAIL; // Paper - Allow disabling mob spawner spawn egg transformation - spawner.setEntityId(type, level.getRandom()); - level.sendBlockUpdated(clickedPos, blockState, blockState, Block.UPDATE_ALL); - level.gameEvent(context.getPlayer(), GameEvent.BLOCK_CHANGE, clickedPos); -@@ -96,7 +_,7 @@ - EntityType type = this.getType(stack); + spawnerHolder.setEntityId(type, level.getRandom()); + level.sendBlockUpdated(pos, blockState, blockState, Block.UPDATE_ALL); + level.gameEvent(context.getPlayer(), GameEvent.BLOCK_CHANGE, pos); +@@ -91,7 +_,7 @@ + EntityType type = getType(itemStack); if (type == null) { return InteractionResult.FAIL; - } else if (!type.isAllowedInPeaceful() && level.getDifficulty() == Difficulty.PEACEFUL) { -+ } else if (!type.isAllowedInPeaceful(stack.get(DataComponents.ENTITY_DATA).getUnsafe()) && level.getDifficulty() == Difficulty.PEACEFUL) { // Paper - check peaceful override ++ } else if (!type.isAllowedInPeaceful(itemStack.get(DataComponents.ENTITY_DATA).getUnsafe()) && level.getDifficulty() == Difficulty.PEACEFUL) { // Paper - check peaceful override return InteractionResult.FAIL; } else { - if (type.spawn((ServerLevel)level, stack, owner, pos, EntitySpawnReason.SPAWN_ITEM_USE, shouldOffsetY, shouldOffsetYMore) != null) { -@@ -178,7 +_,7 @@ + if (type.spawn((ServerLevel)level, itemStack, user, spawnPos, EntitySpawnReason.SPAWN_ITEM_USE, tryMoveDown, movedUp) != null) { +@@ -163,7 +_,7 @@ } else { - breedOffspring.snapTo(pos.x(), pos.y(), pos.z(), 0.0F, 0.0F); - breedOffspring.applyComponentsFromItemStack(stack); -- level.addFreshEntityWithPassengers(breedOffspring); -+ level.addFreshEntityWithPassengers(breedOffspring, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); // CraftBukkit - stack.consume(1, player); - return Optional.of(breedOffspring); + offspring.snapTo(pos.x(), pos.y(), pos.z(), 0.0F, 0.0F); + offspring.applyComponentsFromItemStack(spawnEggStack); +- level.addFreshEntityWithPassengers(offspring); ++ level.addFreshEntityWithPassengers(offspring, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); // CraftBukkit + spawnEggStack.consume(1, player); + return Optional.of(offspring); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/SplashPotionItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/SplashPotionItem.java.patch index 06950791a904..95cf5f1c94f9 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/SplashPotionItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/SplashPotionItem.java.patch @@ -3,7 +3,7 @@ @@ -19,6 +_,10 @@ @Override - public InteractionResult use(Level level, Player player, InteractionHand hand) { + public InteractionResult use(final Level level, final Player player, final InteractionHand hand) { + // Paper start - PlayerLaunchProjectileEvent + final InteractionResult wrapper = super.use(level, player, hand); + if (wrapper instanceof InteractionResult.Fail) return wrapper; diff --git a/paper-server/patches/sources/net/minecraft/world/item/StandingAndWallBlockItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/StandingAndWallBlockItem.java.patch index 90f8211a9a80..1e29b0beab7f 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/StandingAndWallBlockItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/StandingAndWallBlockItem.java.patch @@ -4,17 +4,17 @@ } } -- return blockState != null && level.isUnobstructed(blockState, clickedPos, CollisionContext.empty()) ? blockState : null; -+ // return blockState != null && level.isUnobstructed(blockState, clickedPos, CollisionContext.empty()) ? blockState : null; +- return stateForPlacement != null && level.isUnobstructed(stateForPlacement, pos, CollisionContext.empty()) ? stateForPlacement : null; ++ // return stateForPlacement != null && level.isUnobstructed(stateForPlacement, pos, CollisionContext.empty()) ? stateForPlacement : null; + // CraftBukkit start -+ if (blockState != null) { -+ boolean defaultReturn = level.isUnobstructed(blockState, clickedPos, CollisionContext.empty()); ++ if (stateForPlacement != null) { ++ boolean defaultReturn = level.isUnobstructed(stateForPlacement, pos, CollisionContext.empty()); + org.bukkit.entity.Player player = (context.getPlayer() instanceof net.minecraft.server.level.ServerPlayer serverPlayer) ? serverPlayer.getBukkitEntity() : null; + -+ org.bukkit.event.block.BlockCanBuildEvent event = new org.bukkit.event.block.BlockCanBuildEvent(org.bukkit.craftbukkit.block.CraftBlock.at(context.getLevel(), clickedPos), player, org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(blockState), defaultReturn, org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(context.getHand())); // Paper - Expose hand in BlockCanBuildEvent ++ org.bukkit.event.block.BlockCanBuildEvent event = new org.bukkit.event.block.BlockCanBuildEvent(org.bukkit.craftbukkit.block.CraftBlock.at(context.getLevel(), pos), player, stateForPlacement.asBlockData(), defaultReturn, org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(context.getHand())); // Paper - Expose hand in BlockCanBuildEvent + context.getLevel().getCraftServer().getPluginManager().callEvent(event); + -+ return (event.isBuildable()) ? blockState : null; ++ return (event.isBuildable()) ? stateForPlacement : null; + } else { + return null; + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch index 998923f073a3..5d405d26804c 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/world/item/ThrowablePotionItem.java +++ b/net/minecraft/world/item/ThrowablePotionItem.java @@ -23,11 +_,24 @@ - public InteractionResult use(Level level, Player player, InteractionHand hand) { - ItemStack itemInHand = player.getItemInHand(hand); + public InteractionResult use(final Level level, final Player player, final InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); if (level instanceof ServerLevel serverLevel) { -- Projectile.spawnProjectileFromRotation(this::createPotion, serverLevel, itemInHand, player, -20.0F, PROJECTILE_SHOOT_POWER, 1.0F); +- Projectile.spawnProjectileFromRotation(this::createPotion, serverLevel, itemStack, player, -20.0F, 0.5F, 1.0F); + // Paper start - PlayerLaunchProjectileEvent -+ final Projectile.Delayed thrownPotion = Projectile.spawnProjectileFromRotationDelayed(this::createPotion, serverLevel, itemInHand, player, -20.0F, PROJECTILE_SHOOT_POWER, 1.0F); -+ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Projectile) thrownPotion.projectile().getBukkitEntity()); ++ final Projectile.Delayed thrownPotion = Projectile.spawnProjectileFromRotationDelayed(this::createPotion, serverLevel, itemStack, player, -20.0F, 0.5F, 1.0F); ++ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownPotion.projectile().getBukkitEntity()); + if (event.callEvent() && thrownPotion.attemptSpawn()) { + if (event.shouldConsume()) { -+ itemInHand.consume(1, player); ++ itemStack.consume(1, player); + } else { + player.containerMenu.forceHeldSlot(hand); + } @@ -22,7 +22,7 @@ } player.awardStat(Stats.ITEM_USED.get(this)); -- itemInHand.consume(1, player); +- itemStack.consume(1, player); + // Paper - PlayerLaunchProjectileEvent - move up return InteractionResult.SUCCESS; } diff --git a/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch index 34ac38899186..786ea731da41 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch @@ -1,52 +1,52 @@ --- a/net/minecraft/world/item/TridentItem.java +++ b/net/minecraft/world/item/TridentItem.java -@@ -79,18 +_,38 @@ - .orElse(SoundEvents.TRIDENT_THROW); - player.awardStat(Stats.ITEM_USED.get(this)); - if (level instanceof ServerLevel serverLevel) { -- stack.hurtWithoutBreaking(1, player); -+ // stack.hurtWithoutBreaking(1, player); // CraftBukkit - moved down - if (tridentSpinAttackStrength == 0.0F) { -- ItemStack itemStack = stack.consumeAndReturn(1, player); -- ThrownTrident thrownTrident = Projectile.spawnProjectileFromRotation( -+ ItemStack itemStack = stack.copyWithCount(1); // Paper -+ Projectile.Delayed tridentDelayed = Projectile.spawnProjectileFromRotationDelayed( // Paper - PlayerLaunchProjectileEvent( - ThrownTrident::new, serverLevel, itemStack, player, 0.0F, 2.5F, 1.0F - ); -+ // Paper start - PlayerLaunchProjectileEvent -+ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), (org.bukkit.entity.Projectile) tridentDelayed.projectile().getBukkitEntity()); -+ if (!event.callEvent() || !tridentDelayed.attemptSpawn()) { -+ // CraftBukkit start -+ // Paper end - PlayerLaunchProjectileEvent -+ return false; -+ } -+ ThrownTrident thrownTrident = tridentDelayed.projectile(); // Paper - PlayerLaunchProjectileEvent -+ if (event.shouldConsume()) { -+ itemStack.hurtWithoutBreaking(1, player); // Paper - PlayerLaunchProjectileEvent - use itemStack; pickup item damage -+ } -+ thrownTrident.pickupItemStack = itemStack.copy(); // SPIGOT-4511 update since damage call moved - use itemStack; count = 1 -+ if (event.shouldConsume()) { -+ stack.consume(1, player); -+ } -+ // CraftBukkit end - if (player.hasInfiniteMaterials()) { - thrownTrident.pickup = AbstractArrow.Pickup.CREATIVE_ONLY; - } +@@ -78,18 +_,38 @@ + .orElse(SoundEvents.TRIDENT_THROW); + player.awardStat(Stats.ITEM_USED.get(this)); + if (level instanceof ServerLevel serverLevel) { +- itemStack.hurtWithoutBreaking(1, player); ++ // itemStack.hurtWithoutBreaking(1, player); // CraftBukkit - moved down + if (riptideStrength == 0.0F) { +- ItemStack thrownItemStack = itemStack.consumeAndReturn(1, player); +- ThrownTrident trident = Projectile.spawnProjectileFromRotation( ++ ItemStack thrownItemStack = itemStack.copyWithCount(1); // Paper ++ Projectile.Delayed tridentDelayed = Projectile.spawnProjectileFromRotationDelayed( // Paper - PlayerLaunchProjectileEvent( + ThrownTrident::new, serverLevel, thrownItemStack, player, 0.0F, 2.5F, 1.0F + ); ++ // Paper start - PlayerLaunchProjectileEvent ++ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) tridentDelayed.projectile().getBukkitEntity()); ++ if (!event.callEvent() || !tridentDelayed.attemptSpawn()) { ++ // CraftBukkit start ++ // Paper end - PlayerLaunchProjectileEvent ++ return false; ++ } ++ ThrownTrident trident = tridentDelayed.projectile(); // Paper - PlayerLaunchProjectileEvent ++ if (event.shouldConsume()) { ++ thrownItemStack.hurtWithoutBreaking(1, player); // Paper - PlayerLaunchProjectileEvent - use thrownItemStack; pickup item damage ++ } ++ trident.pickupItemStack = thrownItemStack.copy(); // SPIGOT-4511 update since damage call moved - use thrownItemStack; count = 1 ++ if (event.shouldConsume()) { ++ itemStack.consume(1, player); ++ } ++ // CraftBukkit end + if (player.hasInfiniteMaterials()) { + trident.pickup = AbstractArrow.Pickup.CREATIVE_ONLY; + } - level.playSound(null, thrownTrident, holder.value(), SoundSource.PLAYERS, 1.0F, 1.0F); - return true; -+ // CraftBukkit start - SPIGOT-5458 also need in this branch :( -+ } else { -+ stack.hurtWithoutBreaking(1, player); -+ // CraftBukkit end + level.playSound(null, trident, sound.value(), SoundSource.PLAYERS, 1.0F, 1.0F); + return true; ++ // CraftBukkit start - SPIGOT-5458 also need in this branch :( ++ } else { ++ itemStack.hurtWithoutBreaking(1, player); ++ // CraftBukkit end + } } - } -@@ -104,6 +_,7 @@ - f *= tridentSpinAttackStrength / squareRoot; - f1 *= tridentSpinAttackStrength / squareRoot; - f2 *= tridentSpinAttackStrength / squareRoot; -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerRiptideEvent(player, stack, f, f1, f2)) return false; // Paper - Add player riptide event - player.push(f, f1, f2); - player.startAutoSpinAttack(20, 8.0F, stack); - if (player.onGround()) { +@@ -103,6 +_,7 @@ + xd *= riptideStrength / dist; + yd *= riptideStrength / dist; + zd *= riptideStrength / dist; ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerRiptideEvent(player, itemStack, xd, yd, zd)) return false; // Paper - Add player riptide event + player.push(xd, yd, zd); + player.startAutoSpinAttack(20, 8.0F, itemStack); + if (player.onGround()) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch index ad11eafc3829..a68f6c793efe 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch @@ -1,30 +1,30 @@ --- a/net/minecraft/world/item/WindChargeItem.java +++ b/net/minecraft/world/item/WindChargeItem.java @@ -28,7 +_,7 @@ - public InteractionResult use(Level level, Player player, InteractionHand hand) { - ItemStack itemInHand = player.getItemInHand(hand); + public InteractionResult use(final Level level, final Player player, final InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); if (level instanceof ServerLevel serverLevel) { - Projectile.spawnProjectileFromRotation( + final Projectile.Delayed windCharge = Projectile.spawnProjectileFromRotationDelayed( // Paper - PlayerLaunchProjectileEvent - (level1, owner, spawnedFrom) -> new WindCharge(player, level, player.position().x(), player.getEyePosition().y(), player.position().z()), + (source, l, itemStack) -> new WindCharge(player, level, player.position().x(), player.getEyePosition().y(), player.position().z()), serverLevel, - itemInHand, + stack, @@ -37,6 +_,22 @@ - PROJECTILE_SHOOT_POWER, + 1.5F, 1.0F ); + // Paper start - PlayerLaunchProjectileEvent -+ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Projectile) windCharge.projectile().getBukkitEntity()); ++ com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), (org.bukkit.entity.Projectile) windCharge.projectile().getBukkitEntity()); + if (!event.callEvent() || !windCharge.attemptSpawn()) { + player.containerMenu.forceHeldSlot(hand); + if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { -+ serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundCooldownPacket(player.getCooldowns().getCooldownGroup(itemInHand), 0)); // prevent visual desync of cooldown on the slot ++ serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundCooldownPacket(player.getCooldowns().getCooldownGroup(stack), 0)); // prevent visual desync of cooldown on the slot + } + return InteractionResult.FAIL; + } + + player.awardStat(Stats.ITEM_USED.get(this)); -+ if (event.shouldConsume()) itemInHand.consume(1, player); ++ if (event.shouldConsume()) stack.consume(1, player); + else if (!player.hasInfiniteMaterials()) { + player.containerMenu.forceHeldSlot(hand); + } @@ -37,7 +37,7 @@ 0.4F / (level.getRandom().nextFloat() * 0.4F + 0.8F) ); - player.awardStat(Stats.ITEM_USED.get(this)); -- itemInHand.consume(1, player); +- stack.consume(1, player); + // Paper - PlayerLaunchProjectileEvent; moved up return InteractionResult.SUCCESS; } diff --git a/paper-server/patches/sources/net/minecraft/world/item/alchemy/PotionBrewing.java.patch b/paper-server/patches/sources/net/minecraft/world/item/alchemy/PotionBrewing.java.patch index f81ac811fa58..03fbf04cd2f2 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/alchemy/PotionBrewing.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/alchemy/PotionBrewing.java.patch @@ -6,44 +6,44 @@ private final List> containerMixes; + private final it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap customMixes = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>(); // Paper - Custom Potion Mixes - PotionBrewing(List containers, List> potionMixes, List> containerMixes) { - this.containers = containers; -@@ -27,7 +_,7 @@ + private PotionBrewing( + final List containers, final List> potionMixes, final List> containerMixes +@@ -29,7 +_,7 @@ } - public boolean isIngredient(ItemStack stack) { -- return this.isContainerIngredient(stack) || this.isPotionIngredient(stack); -+ return this.isContainerIngredient(stack) || this.isPotionIngredient(stack) || this.isCustomIngredient(stack); // Paper - Custom Potion Mixes + public boolean isIngredient(final ItemStack ingredient) { +- return this.isContainerIngredient(ingredient) || this.isPotionIngredient(ingredient); ++ return this.isContainerIngredient(ingredient) || this.isPotionIngredient(ingredient) || this.isCustomIngredient(ingredient); // Paper - Custom Potion Mixes } - private boolean isContainer(ItemStack stack) { -@@ -71,6 +_,11 @@ + private boolean isContainer(final ItemStack input) { +@@ -73,6 +_,11 @@ } - public boolean hasMix(ItemStack potion, ItemStack ingredient) { + public boolean hasMix(final ItemStack source, final ItemStack ingredient) { + // Paper start - Custom Potion Mixes -+ if (this.hasCustomMix(potion, ingredient)) { ++ if (this.hasCustomMix(source, ingredient)) { + return true; + } + // Paper end - Custom Potion Mixes - return this.isContainer(potion) && (this.hasContainerMix(potion, ingredient) || this.hasPotionMix(potion, ingredient)); + return this.isContainer(source) && (this.hasContainerMix(source, ingredient) || this.hasPotionMix(source, ingredient)); } -@@ -103,6 +_,13 @@ - if (potion.isEmpty()) { - return potion; - } else { -+ // Paper start - Custom Potion Mixes -+ for (io.papermc.paper.potion.PaperPotionMix mix : this.customMixes.values()) { -+ if (mix.input().test(potion) && mix.ingredient().test(ingredient)) { -+ return mix.result().copy(); +@@ -109,6 +_,13 @@ + if (potion.isEmpty()) { + return source; + } else { ++ // Paper start - Custom Potion Mixes ++ for (io.papermc.paper.potion.PaperPotionMix mix : this.customMixes.values()) { ++ if (mix.input().test(source) && mix.ingredient().test(ingredient)) { ++ return mix.result().copy(); ++ } + } -+ } -+ // Paper end - Custom Potion Mixes - Optional> optional = potion.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY).potion(); - if (optional.isEmpty()) { - return potion; -@@ -189,6 +_,50 @@ ++ // Paper end - Custom Potion Mixes + for (PotionBrewing.Mix mix : this.containerMixes) { + if (source.is(mix.from) && mix.ingredient.test(ingredient)) { + return PotionContents.createItemStack(mix.to.value(), potion.get()); +@@ -191,6 +_,50 @@ builder.addMix(Potions.AWKWARD, Items.PHANTOM_MEMBRANE, Potions.SLOW_FALLING); builder.addMix(Potions.SLOW_FALLING, Items.REDSTONE, Potions.LONG_SLOW_FALLING); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/alchemy/PotionContents.java.patch b/paper-server/patches/sources/net/minecraft/world/item/alchemy/PotionContents.java.patch index 95e0c047a69e..9c5ba623b7c6 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/alchemy/PotionContents.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/alchemy/PotionContents.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/item/alchemy/PotionContents.java @@ -158,7 +_,7 @@ if (effect.getEffect().value().isInstantenous()) { - effect.getEffect().value().applyInstantenousEffect(serverLevel, player1, player1, entity, effect.getAmplifier(), 1.0); + effect.getEffect().value().applyInstantenousEffect(serverLevel, player, player, entity, effect.getAmplifier(), 1.0); } else { - entity.addEffect(effect); + entity.addEffect(effect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.POTION_DRINK); // CraftBukkit diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/AttackRange.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/AttackRange.java.patch index a5c1424febaa..ce5e2600f932 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/AttackRange.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/AttackRange.java.patch @@ -3,22 +3,22 @@ @@ -25,14 +_,15 @@ import net.minecraft.world.phys.Vec3; - public record AttackRange(float minRange, float maxRange, float minCreativeRange, float maxCreativeRange, float hitboxMargin, float mobFactor) { + public record AttackRange(float minReach, float maxReach, float minCreativeReach, float maxCreativeReach, float hitboxMargin, float mobFactor) { + public static final AttackRange CODEC_DEFAULT = new AttackRange(0.0F, 3.0F, 0.0F, 5.0F, 0.3F, 1.0F); // Paper - add back defaults instance public static final Codec CODEC = RecordCodecBuilder.create( - instance -> instance.group( -- ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("min_reach", 0.0F).forGetter(AttackRange::minRange), -- ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("max_reach", 3.0F).forGetter(AttackRange::maxRange), -- ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("min_creative_reach", 0.0F).forGetter(AttackRange::minCreativeRange), -- ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("max_creative_reach", 5.0F).forGetter(AttackRange::maxCreativeRange), + i -> i.group( +- ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("min_reach", 0.0F).forGetter(AttackRange::minReach), +- ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("max_reach", 3.0F).forGetter(AttackRange::maxReach), +- ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("min_creative_reach", 0.0F).forGetter(AttackRange::minCreativeReach), +- ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("max_creative_reach", 5.0F).forGetter(AttackRange::maxCreativeReach), - ExtraCodecs.floatRange(0.0F, 1.0F).optionalFieldOf("hitbox_margin", 0.3F).forGetter(AttackRange::hitboxMargin), - Codec.floatRange(0.0F, 2.0F).optionalFieldOf("mob_factor", 1.0F).forGetter(AttackRange::mobFactor) -+ ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("min_reach", 0.0F).forGetter(AttackRange::minRange), // Paper - diff on change: used in CODEC_DEFAULT above -+ ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("max_reach", 3.0F).forGetter(AttackRange::maxRange), // Paper - diff on change: used in CODEC_DEFAULT above -+ ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("min_creative_reach", 0.0F).forGetter(AttackRange::minCreativeRange), // Paper - diff on change: used in CODEC_DEFAULT above -+ ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("max_creative_reach", 5.0F).forGetter(AttackRange::maxCreativeRange), // Paper - diff on change: used in CODEC_DEFAULT above ++ ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("min_reach", 0.0F).forGetter(AttackRange::minReach), // Paper - diff on change: used in CODEC_DEFAULT above ++ ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("max_reach", 3.0F).forGetter(AttackRange::maxReach), // Paper - diff on change: used in CODEC_DEFAULT above ++ ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("min_creative_reach", 0.0F).forGetter(AttackRange::minCreativeReach), // Paper - diff on change: used in CODEC_DEFAULT above ++ ExtraCodecs.floatRange(0.0F, 64.0F).optionalFieldOf("max_creative_reach", 5.0F).forGetter(AttackRange::maxCreativeReach), // Paper - diff on change: used in CODEC_DEFAULT above + ExtraCodecs.floatRange(0.0F, 1.0F).optionalFieldOf("hitbox_margin", 0.3F).forGetter(AttackRange::hitboxMargin), // Paper - diff on change: used in CODEC_DEFAULT above + Codec.floatRange(0.0F, 2.0F).optionalFieldOf("mob_factor", 1.0F).forGetter(AttackRange::mobFactor) // Paper - diff on change: used in CODEC_DEFAULT above ) - .apply(instance, AttackRange::new) + .apply(i, AttackRange::new) ); diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/BlocksAttacks.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/BlocksAttacks.java.patch index 568583af602b..44f40b7b6c36 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/BlocksAttacks.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/BlocksAttacks.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/world/item/component/BlocksAttacks.java +++ b/net/minecraft/world/item/component/BlocksAttacks.java -@@ -86,11 +_,15 @@ +@@ -85,11 +_,15 @@ ); } -- public void disable(ServerLevel level, LivingEntity entity, float duration, ItemStack stack) { -+ public void disable(ServerLevel level, LivingEntity entity, float duration, ItemStack stack, LivingEntity attacker) { // Paper - int i = this.disableBlockingForTicks(duration); - if (i > 0) { - if (entity instanceof Player player) { -- player.getCooldowns().addCooldown(stack, i); +- public void disable(final ServerLevel level, final LivingEntity user, final float baseSeconds, final ItemStack blockingWith) { ++ public void disable(final ServerLevel level, final LivingEntity user, final float baseSeconds, final ItemStack blockingWith, LivingEntity attacker) { // Paper + int cooldownTicks = this.disableBlockingForTicks(baseSeconds); + if (cooldownTicks > 0) { + if (user instanceof Player player) { +- player.getCooldowns().addCooldown(blockingWith, cooldownTicks); + // Paper start -+ final io.papermc.paper.event.player.PlayerShieldDisableEvent shieldDisableEvent = new io.papermc.paper.event.player.PlayerShieldDisableEvent((org.bukkit.entity.Player) player.getBukkitEntity(), attacker.getBukkitEntity(), i); ++ final io.papermc.paper.event.player.PlayerShieldDisableEvent shieldDisableEvent = new io.papermc.paper.event.player.PlayerShieldDisableEvent((org.bukkit.entity.Player) player.getBukkitEntity(), attacker.getBukkitEntity(), cooldownTicks); + if (!shieldDisableEvent.callEvent()) return; -+ player.getCooldowns().addCooldown(stack, shieldDisableEvent.getCooldown()); ++ player.getCooldowns().addCooldown(blockingWith, shieldDisableEvent.getCooldown()); + // Paper end } - entity.stopUsingItem(); + user.stopUsingItem(); diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/BundleContents.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/BundleContents.java.patch index c5caf800fceb..2694a8259a2c 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/BundleContents.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/BundleContents.java.patch @@ -1,38 +1,39 @@ --- a/net/minecraft/world/item/component/BundleContents.java +++ b/net/minecraft/world/item/component/BundleContents.java -@@ -25,6 +_,7 @@ - .flatXmap(BundleContents::checkAndCreate, bundleContents -> DataResult.success(bundleContents.items)); - public static final StreamCodec STREAM_CODEC = ItemStack.STREAM_CODEC +@@ -28,6 +_,7 @@ + public static final Codec CODEC = ItemStackTemplate.CODEC.listOf().xmap(BundleContents::new, contents -> contents.items); + public static final StreamCodec STREAM_CODEC = ItemStackTemplate.STREAM_CODEC .apply(ByteBufCodecs.list()) + .apply(ByteBufCodecs::increaseDepth) // Paper - Track codec depth .map(BundleContents::new, contents -> contents.items); private static final Fraction BUNDLE_IN_BUNDLE_WEIGHT = Fraction.getFraction(1, 16); private static final int NO_STACK_INDEX = -1; -@@ -76,6 +_,12 @@ - return !stack.isEmpty() && stack.getItem().canFitInsideContainerItems(); +@@ -80,6 +_,13 @@ + return !itemToAdd.isEmpty() && itemToAdd.getItem().canFitInsideContainerItems(); } + // Paper start - correct bundle inventory action + public int getMaxAmountToAdd(final ItemStack stack) { -+ return Mutable.getMaxAmountToAdd(stack, this.weight); ++ DataResult weight = this.weight(); ++ return weight.isError() ? 0 : Mutable.getMaxAmountToAdd(stack, weight.getOrThrow()); + } + // Paper end - correct bundle inventory action + public int getNumberOfItemsToShow() { - int size = this.size(); - int i = size > 12 ? 11 : 12; -@@ -171,7 +_,13 @@ + int numberOfItemStacks = this.size(); + int availableItemsToShow = numberOfItemStacks > 12 ? 11 : 12; +@@ -179,6 +_,14 @@ + Fraction remainingWeight = Fraction.ONE.subtract(this.weight); + return Math.max(remainingWeight.divideBy(itemWeight).intValue(), 0); } - - public int getMaxAmountToAdd(ItemStack stack) { -- Fraction fraction = Fraction.ONE.subtract(this.weight); ++ + // Paper start - correct bundle inventory action + // Static overload to easily compute this value without the need for an instance of mutable. -+ return getMaxAmountToAdd(stack, this.weight); -+ } + static int getMaxAmountToAdd(final ItemStack stack, final Fraction weight) { -+ Fraction fraction = Fraction.ONE.subtract(weight); ++ Fraction remainingWeight = Fraction.ONE.subtract(weight); ++ return Math.max(remainingWeight.divideBy(BundleContents.getWeight(stack).getOrThrow()).intValue(), 0); ++ } + // Paper end - correct bundle inventory action - return Math.max(fraction.divideBy(BundleContents.getWeight(stack)).intValue(), 0); - } + public int tryInsert(final ItemStack itemsToAdd) { + if (!BundleContents.canItemBeInBundle(itemsToAdd)) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/ChargedProjectiles.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/ChargedProjectiles.java.patch index e8043215a614..6055dd9917a3 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/ChargedProjectiles.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/ChargedProjectiles.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/world/item/component/ChargedProjectiles.java +++ b/net/minecraft/world/item/component/ChargedProjectiles.java -@@ -22,6 +_,7 @@ - .xmap(ChargedProjectiles::new, chargedProjectiles -> chargedProjectiles.items); - public static final StreamCodec STREAM_CODEC = ItemStack.STREAM_CODEC +@@ -21,6 +_,7 @@ + public static final Codec CODEC = ItemStackTemplate.CODEC.listOf().xmap(ChargedProjectiles::new, projectiles -> projectiles.items); + public static final StreamCodec STREAM_CODEC = ItemStackTemplate.STREAM_CODEC .apply(ByteBufCodecs.list()) + .apply(ByteBufCodecs::increaseDepth) // Paper - Track codec depth - .map(ChargedProjectiles::new, chargedProjectiles -> chargedProjectiles.items); - private final List items; + .map(ChargedProjectiles::new, projectiles -> projectiles.items); + public static ChargedProjectiles of(final ItemStackTemplate stack) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/Consumable.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/Consumable.java.patch index 6c13e5899a08..74f0fe8217b3 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/Consumable.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/Consumable.java.patch @@ -2,9 +2,9 @@ +++ b/net/minecraft/world/item/component/Consumable.java @@ -84,13 +_,35 @@ - stack.getAllOfType(ConsumableListener.class).forEach(consumableListener -> consumableListener.onConsume(level, entity, stack, this)); + stack.getAllOfType(ConsumableListener.class).forEach(component -> component.onConsume(level, user, stack, this)); if (!level.isClientSide()) { -- this.onConsumeEffects.forEach(consumeEffect -> consumeEffect.apply(level, stack, entity)); +- this.onConsumeEffects.forEach(action -> action.apply(level, stack, user)); + // CraftBukkit start + org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause; + if (stack.is(net.minecraft.world.item.Items.MILK_BUCKET)) { @@ -15,12 +15,12 @@ + cause = org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD; + } + -+ this.onConsumeEffects.forEach(consumeEffect -> consumeEffect.apply(level, stack, entity, cause)); ++ this.onConsumeEffects.forEach(consumeEffect -> consumeEffect.apply(level, stack, user, cause)); + // CraftBukkit end } - entity.gameEvent(this.animation == ItemUseAnimation.DRINK ? GameEvent.DRINK : GameEvent.EAT); - stack.consume(1, entity); + user.gameEvent(this.animation == ItemUseAnimation.DRINK ? GameEvent.DRINK : GameEvent.EAT); + stack.consume(1, user); return stack; } + @@ -35,5 +35,5 @@ + } + // CraftBukkit end - public boolean canConsume(LivingEntity entity, ItemStack stack) { + public boolean canConsume(final LivingEntity user, final ItemStack stack) { FoodProperties foodProperties = stack.get(DataComponents.FOOD); diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/ConsumableListener.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/ConsumableListener.java.patch index a47c2b2a62a1..65cfa2773d02 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/ConsumableListener.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/ConsumableListener.java.patch @@ -3,7 +3,7 @@ @@ -6,4 +_,6 @@ public interface ConsumableListener { - void onConsume(Level level, LivingEntity entity, ItemStack stack, Consumable consumable); + void onConsume(final Level level, final LivingEntity user, final ItemStack stack, final Consumable consumable); + + default void cancelUsingItem(net.minecraft.server.level.ServerPlayer player, ItemStack stack, java.util.List> collectedPackets) {} // CraftBukkit // Paper - properly resend entities - collect packets for bundle } diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/CustomData.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/CustomData.java.patch index 6173a85d3c8e..80ddb7fcfc87 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/CustomData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/CustomData.java.patch @@ -36,4 +36,4 @@ + // Paper end - expose unsafe internal compound tag for read only access @Override - public boolean equals(Object other) { + public boolean equals(final Object obj) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/DeathProtection.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/DeathProtection.java.patch index b2efb28173e3..a00b90d9d64c 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/DeathProtection.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/DeathProtection.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/item/component/DeathProtection.java @@ -37,7 +_,7 @@ - public void applyEffects(ItemStack stack, LivingEntity entity) { - for (ConsumeEffect consumeEffect : this.deathEffects) { -- consumeEffect.apply(entity.level(), stack, entity); -+ consumeEffect.apply(entity.level(), stack, entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TOTEM); // CraftBukkit + public void applyEffects(final ItemStack itemStack, final LivingEntity entity) { + for (ConsumeEffect effect : this.deathEffects) { +- effect.apply(entity.level(), itemStack, entity); ++ effect.apply(entity.level(), itemStack, entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TOTEM); // CraftBukkit } } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/ItemContainerContents.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/ItemContainerContents.java.patch index 03cce7d7e33b..ba89655d57df 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/ItemContainerContents.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/ItemContainerContents.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/world/item/component/ItemContainerContents.java +++ b/net/minecraft/world/item/component/ItemContainerContents.java -@@ -28,6 +_,7 @@ - .xmap(ItemContainerContents::fromSlots, ItemContainerContents::asSlots); - public static final StreamCodec STREAM_CODEC = ItemStack.OPTIONAL_STREAM_CODEC - .apply(ByteBufCodecs.list(256)) +@@ -31,6 +_,7 @@ + public static final StreamCodec STREAM_CODEC = ItemStackTemplate.STREAM_CODEC + .apply(ByteBufCodecs::optional) + .>>apply(ByteBufCodecs.list(256)) + .apply(ByteBufCodecs::increaseDepth) // Paper - Track codec depth - .map(ItemContainerContents::new, contents -> contents.items); - public final NonNullList items; + .map(ItemContainerContents::new, c -> c.items); + public final List> items; private final int hashCode; diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/OminousBottleAmplifier.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/OminousBottleAmplifier.java.patch index 3bad9d4109b1..2b7a1666f906 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/OminousBottleAmplifier.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/OminousBottleAmplifier.java.patch @@ -3,10 +3,10 @@ @@ -29,8 +_,15 @@ @Override - public void onConsume(Level level, LivingEntity entity, ItemStack stack, Consumable consumable) { -- entity.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, 120000, this.value, false, false, true)); + public void onConsume(final Level level, final LivingEntity user, final ItemStack stack, final Consumable consumable) { +- user.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, EFFECT_DURATION, this.value, false, false, true)); - } -+ entity.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, 120000, this.value, false, false, true)); // Paper - properly resend entities - diff on change for below ++ user.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, EFFECT_DURATION, this.value, false, false, true)); // Paper - properly resend entities - diff on change for below + } + + // Paper start - properly resend entities - collect packets for bundle @@ -17,4 +17,4 @@ + // Paper end - properly resend entities - collect packets for bundle @Override - public void addToTooltip(Item.TooltipContext context, Consumer tooltipAdder, TooltipFlag flag, DataComponentGetter componentGetter) { + public void addToTooltip( diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/ResolvableProfile.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/ResolvableProfile.java.patch index 0fb4790ff20a..8d73c45ced2a 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/ResolvableProfile.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/ResolvableProfile.java.patch @@ -1,23 +1,23 @@ --- a/net/minecraft/world/item/component/ResolvableProfile.java +++ b/net/minecraft/world/item/component/ResolvableProfile.java @@ -46,7 +_,7 @@ - private static ResolvableProfile create(Either contents, PlayerSkin.Patch skinPatch) { - return contents.map( - gameProfile -> new ResolvableProfile.Static(Either.left(gameProfile), skinPatch), + private static ResolvableProfile create(final Either value, final PlayerSkin.Patch patch) { + return value.map( + full -> new ResolvableProfile.Static(Either.left(full), patch), - partial -> (ResolvableProfile)(partial.properties.isEmpty() && partial.id.isPresent() != partial.name.isPresent() + partial -> (ResolvableProfile)(partial.properties.isEmpty() && partial.id.isPresent() != partial.name.isPresent() // Paper - diff on change - heuristic for dynamic vs static resolvable profile - used in CraftPlayerProfile#buildResolvable ? partial.name - .map(string -> new ResolvableProfile.Dynamic(Either.left(string), skinPatch)) - .orElseGet(() -> new ResolvableProfile.Dynamic(Either.right(partial.id.get()), skinPatch)) -@@ -139,9 +_,10 @@ - instance -> instance.group( + .map(s -> new ResolvableProfile.Dynamic(Either.left(s), patch)) + .orElseGet(() -> new ResolvableProfile.Dynamic(Either.right(partial.id.get()), patch)) +@@ -140,9 +_,10 @@ + i -> i.group( ExtraCodecs.PLAYER_NAME.optionalFieldOf("name").forGetter(ResolvableProfile.Partial::name), UUIDUtil.CODEC.optionalFieldOf("id").forGetter(ResolvableProfile.Partial::id), -+ UUIDUtil.STRING_CODEC.lenientOptionalFieldOf("Id").forGetter($ -> Optional.empty()), // Paper ++ UUIDUtil.STRING_CODEC.lenientOptionalFieldOf("Id").forGetter(_ -> Optional.empty()), // Paper ExtraCodecs.PROPERTY_MAP.optionalFieldOf("properties", PropertyMap.EMPTY).forGetter(ResolvableProfile.Partial::properties) ) -- .apply(instance, ResolvableProfile.Partial::new) -+ .apply(instance, (name, uuid, uuid2, propertyMap) -> new ResolvableProfile.Partial(name, uuid2.or(() -> uuid), propertyMap)) // Paper +- .apply(i, ResolvableProfile.Partial::new) ++ .apply(i, (name, uuid, uuid2, propertyMap) -> new ResolvableProfile.Partial(name, uuid2.or(() -> uuid), propertyMap)) // Paper ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ByteBufCodecs.PLAYER_NAME.apply(ByteBufCodecs::optional), diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/SuspiciousStewEffects.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/SuspiciousStewEffects.java.patch index c6c3fb78838b..d2a2de9725b6 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/SuspiciousStewEffects.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/SuspiciousStewEffects.java.patch @@ -14,5 +14,5 @@ + // CraftBukkit end + @Override - public void addToTooltip(Item.TooltipContext context, Consumer tooltipAdder, TooltipFlag flag, DataComponentGetter componentGetter) { - if (flag.isCreative()) { + public void addToTooltip( + final Item.TooltipContext context, final Consumer consumer, final TooltipFlag flag, final DataComponentGetter components diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/TypedEntityData.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/TypedEntityData.java.patch index cf02814d415d..2d3b87168c1e 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/TypedEntityData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/TypedEntityData.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/item/component/TypedEntityData.java +++ b/net/minecraft/world/item/component/TypedEntityData.java @@ -84,6 +_,32 @@ - this.tag = stripId(tag); + this.tag = stripId(data); } + // Paper start - utils for item meta @@ -25,20 +25,20 @@ + + public CompoundTag copyTagWithBlockEntityId() { + final CompoundTag tag = this.tag.copy(); -+ tag.putString("id", net.minecraft.world.level.block.entity.BlockEntityType.getKey((net.minecraft.world.level.block.entity.BlockEntityType) this.type).toString()); ++ tag.putString("id", net.minecraft.core.registries.BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey((net.minecraft.world.level.block.entity.BlockEntityType) this.type).toString()); + return tag; + } + // Paper end - utils for item meta + - public static TypedEntityData of(T type, CompoundTag tag) { - return new TypedEntityData<>(type, tag); + public static TypedEntityData of(final T type, final CompoundTag data) { + return new TypedEntityData<>(type, data); } -@@ -181,7 +_,7 @@ - public void addToTooltip(Item.TooltipContext context, Consumer tooltipAdder, TooltipFlag flag, DataComponentGetter componentGetter) { +@@ -182,7 +_,7 @@ + ) { if (this.type.getClass() == EntityType.class) { - EntityType entityType = (EntityType)this.type; -- if (context.isPeaceful() && !entityType.isAllowedInPeaceful()) { -+ if (context.isPeaceful() && !entityType.isTypeAllowedInPeaceful()) { // Paper - do not check entity data for peaceful override - match client as close as possible for compute tooltip api - tooltipAdder.accept(Component.translatable("item.spawn_egg.peaceful").withStyle(ChatFormatting.RED)); + EntityType type = (EntityType)this.type; +- if (context.isPeaceful() && !type.isAllowedInPeaceful()) { ++ if (context.isPeaceful() && !type.isTypeAllowedInPeaceful()) { // Paper - do not check entity data for peaceful override - match client as close as possible for compute tooltip api + consumer.accept(Component.translatable("item.spawn_egg.peaceful").withStyle(ChatFormatting.RED)); } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/UseRemainder.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/UseRemainder.java.patch index a10f28000b40..d7d93ef8cb7b 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/UseRemainder.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/UseRemainder.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/item/component/UseRemainder.java +++ b/net/minecraft/world/item/component/UseRemainder.java -@@ -8,7 +_,7 @@ - public record UseRemainder(ItemStack convertInto) { - public static final Codec CODEC = ItemStack.CODEC.xmap(UseRemainder::new, UseRemainder::convertInto); +@@ -9,7 +_,7 @@ + public record UseRemainder(ItemStackTemplate convertInto) { + public static final Codec CODEC = ItemStackTemplate.CODEC.xmap(UseRemainder::new, UseRemainder::convertInto); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( -- ItemStack.STREAM_CODEC, UseRemainder::convertInto, UseRemainder::new -+ ItemStack.STREAM_CODEC.apply(net.minecraft.network.codec.ByteBufCodecs::increaseDepth), UseRemainder::convertInto, UseRemainder::new // Paper - Track codec depth +- ItemStackTemplate.STREAM_CODEC, UseRemainder::convertInto, UseRemainder::new ++ ItemStackTemplate.STREAM_CODEC.apply(net.minecraft.network.codec.ByteBufCodecs::increaseDepth), UseRemainder::convertInto, UseRemainder::new // Paper - Track codec depth ); - public ItemStack convertIntoRemainder(ItemStack stack, int count, boolean hasInfiniteMaterials, UseRemainder.OnExtraCreatedRemainder onExtraCreated) { + public ItemStack convertIntoRemainder( diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/WrittenBookContent.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/WrittenBookContent.java.patch index b8d4d0c85d0e..d68b69b84b1d 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/WrittenBookContent.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/WrittenBookContent.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/item/component/WrittenBookContent.java +++ b/net/minecraft/world/item/component/WrittenBookContent.java -@@ -93,7 +_,7 @@ +@@ -91,7 +_,7 @@ - public static boolean resolveForItem(ItemStack stack, CommandSourceStack source, @Nullable Player player) { - WrittenBookContent writtenBookContent = stack.get(DataComponents.WRITTEN_BOOK_CONTENT); -- if (writtenBookContent != null && !writtenBookContent.resolved()) { -+ if (io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.resolveSelectorsInBooks && writtenBookContent != null && !writtenBookContent.resolved()) { // Paper - Disable component selector resolving in books by default - WrittenBookContent writtenBookContent1 = writtenBookContent.resolve(source, player); - if (writtenBookContent1 != null) { - stack.set(DataComponents.WRITTEN_BOOK_CONTENT, writtenBookContent1); + public static boolean resolveForItem(final ItemStack itemStack, final ResolutionContext context, final HolderLookup.Provider registries) { + WrittenBookContent content = itemStack.get(DataComponents.WRITTEN_BOOK_CONTENT); +- if (content != null && !content.resolved()) { ++ if (io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.resolveSelectorsInBooks && content != null && !content.resolved()) { // Paper - Disable component selector resolving in books by default + WrittenBookContent resolvedContent = content.resolve(context, registries); + if (resolvedContent != null) { + itemStack.set(DataComponents.WRITTEN_BOOK_CONTENT, resolvedContent); diff --git a/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ApplyStatusEffectsConsumeEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ApplyStatusEffectsConsumeEffect.java.patch index 4d29a75fd94c..412ccec493e3 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ApplyStatusEffectsConsumeEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ApplyStatusEffectsConsumeEffect.java.patch @@ -4,16 +4,16 @@ } @Override -- public boolean apply(Level level, ItemStack stack, LivingEntity entity) { -+ public boolean apply(Level level, ItemStack stack, LivingEntity entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) { // CraftBukkit - if (entity.getRandom().nextFloat() >= this.probability) { +- public boolean apply(final Level level, final ItemStack stack, final LivingEntity user) { ++ public boolean apply(final Level level, final ItemStack stack, final LivingEntity user, final org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) { // CraftBukkit + if (user.getRandom().nextFloat() >= this.probability) { return false; } else { - boolean flag = false; + boolean anyApplied = false; - for (MobEffectInstance mobEffectInstance : this.effects) { -- if (entity.addEffect(new MobEffectInstance(mobEffectInstance))) { -+ if (entity.addEffect(new MobEffectInstance(mobEffectInstance), cause)) { // CraftBukkit - flag = true; + for (MobEffectInstance effect : this.effects) { +- if (user.addEffect(new MobEffectInstance(effect))) { ++ if (user.addEffect(new MobEffectInstance(effect), cause)) { // CraftBukkit + anyApplied = true; } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java.patch index 37064488ba3e..5cec4d4735c9 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java.patch @@ -4,11 +4,11 @@ } @Override -- public boolean apply(Level level, ItemStack stack, LivingEntity entity) { -- return entity.removeAllEffects(); +- public boolean apply(final Level level, final ItemStack stack, final LivingEntity user) { +- return user.removeAllEffects(); + // CraftBukkit start -+ public boolean apply(Level level, ItemStack stack, LivingEntity entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) { -+ return entity.removeAllEffects(cause); ++ public boolean apply(final Level level, final ItemStack stack, final LivingEntity user, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) { ++ return user.removeAllEffects(cause); + // CraftBukkit end } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ConsumeEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ConsumeEffect.java.patch index af59bc597a87..95ff41a54c96 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ConsumeEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/consume_effects/ConsumeEffect.java.patch @@ -4,14 +4,14 @@ ConsumeEffect.Type getType(); -- boolean apply(Level level, ItemStack stack, LivingEntity entity); +- boolean apply(final Level level, final ItemStack stack, final LivingEntity user); + // CraftBukkit start -+ default boolean apply(Level level, ItemStack stack, LivingEntity entity) { -+ return this.apply(level, stack, entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); ++ default boolean apply(final Level level, final ItemStack stack, final LivingEntity user) { ++ return this.apply(level, stack, user, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); + } + -+ default boolean apply(Level level, ItemStack stack, LivingEntity entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) { -+ return this.apply(level, stack, entity); ++ default boolean apply(Level level, ItemStack stack, LivingEntity user, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) { ++ return this.apply(level, stack, user); + } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/item/consume_effects/RemoveStatusEffectsConsumeEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/item/consume_effects/RemoveStatusEffectsConsumeEffect.java.patch index 9cd988de2380..90f417806e9e 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/consume_effects/RemoveStatusEffectsConsumeEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/consume_effects/RemoveStatusEffectsConsumeEffect.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/world/item/consume_effects/RemoveStatusEffectsConsumeEffect.java +++ b/net/minecraft/world/item/consume_effects/RemoveStatusEffectsConsumeEffect.java -@@ -35,11 +_,11 @@ +@@ -33,11 +_,11 @@ } @Override -- public boolean apply(Level level, ItemStack stack, LivingEntity entity) { -+ public boolean apply(Level level, ItemStack stack, LivingEntity entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) { // CraftBukkit - boolean flag = false; +- public boolean apply(final Level level, final ItemStack stack, final LivingEntity user) { ++ public boolean apply(final Level level, final ItemStack stack, final LivingEntity user, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) { // CraftBukkit + boolean hasRemovedAny = false; - for (Holder holder : this.effects) { -- if (entity.removeEffect(holder)) { -+ if (entity.removeEffect(holder, cause)) { // CraftBukkit - flag = true; + for (Holder effect : this.effects) { +- if (user.removeEffect(effect)) { ++ if (user.removeEffect(effect, cause)) { // CraftBukkit + hasRemovedAny = true; } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect.java.patch index 9731540c3630..8e058ad573f6 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect.java.patch @@ -1,17 +1,17 @@ --- a/net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect.java +++ b/net/minecraft/world/item/consume_effects/TeleportRandomlyConsumeEffect.java -@@ -55,7 +_,13 @@ +@@ -54,7 +_,13 @@ } - Vec3 vec3 = entity.position(); -- if (entity.randomTeleport(d, d1, d2, true)) { + Vec3 oldPos = user.position(); +- if (user.randomTeleport(xx, yy, zz, true)) { + // CraftBukkit start - handle canceled status of teleport event -+ java.util.Optional status = entity.randomTeleport(d, d1, d2, true, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.CONSUMABLE_EFFECT); ++ java.util.Optional status = user.randomTeleport(xx, yy, zz, true, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.CONSUMABLE_EFFECT); + + // teleport event was canceled, no more tries + if (status.isEmpty()) break; + if (status.get()) { + // CraftBukkit end - level.gameEvent(GameEvent.TELEPORT, vec3, GameEvent.Context.of(entity)); + level.gameEvent(GameEvent.TELEPORT, oldPos, GameEvent.Context.of(user)); SoundSource soundSource; SoundEvent soundEvent; diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/BlastingRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/BlastingRecipe.java.patch index 320c3c5413b0..55d3d73eb3ee 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/BlastingRecipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/BlastingRecipe.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/crafting/BlastingRecipe.java +++ b/net/minecraft/world/item/crafting/BlastingRecipe.java -@@ -31,4 +_,17 @@ +@@ -45,4 +_,17 @@ case FOOD, MISC -> RecipeBookCategories.BLAST_FURNACE_MISC; }; } @@ -8,9 +8,9 @@ + // CraftBukkit start + @Override + public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) { -+ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result().create()); + -+ org.bukkit.craftbukkit.inventory.CraftBlastingRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftBlastingRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.input()), this.experience(), this.cookingTime()); ++ org.bukkit.craftbukkit.inventory.CraftBlastingRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftBlastingRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.input()), this.experience(), this.cookingTime()); + recipe.setGroup(this.group()); + recipe.setCategory(org.bukkit.craftbukkit.inventory.CraftRecipe.getCategory(this.category())); + diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/CampfireCookingRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/CampfireCookingRecipe.java.patch index 876f5b6f1dc9..f0c982c11cca 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/CampfireCookingRecipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/CampfireCookingRecipe.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/crafting/CampfireCookingRecipe.java +++ b/net/minecraft/world/item/crafting/CampfireCookingRecipe.java -@@ -28,4 +_,17 @@ +@@ -42,4 +_,17 @@ public RecipeBookCategory recipeBookCategory() { return RecipeBookCategories.CAMPFIRE; } @@ -8,9 +8,9 @@ + // CraftBukkit start + @Override + public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) { -+ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result().create()); + -+ org.bukkit.craftbukkit.inventory.CraftCampfireRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftCampfireRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.input()), this.experience(), this.cookingTime()); ++ org.bukkit.craftbukkit.inventory.CraftCampfireRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftCampfireRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.input()), this.experience(), this.cookingTime()); + recipe.setGroup(this.group()); + recipe.setCategory(org.bukkit.craftbukkit.inventory.CraftRecipe.getCategory(this.category())); + diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/CustomRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/CustomRecipe.java.patch index bbf2d6450637..c1039cdf82a9 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/CustomRecipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/CustomRecipe.java.patch @@ -1,22 +1,18 @@ --- a/net/minecraft/world/item/crafting/CustomRecipe.java +++ b/net/minecraft/world/item/crafting/CustomRecipe.java -@@ -30,6 +_,19 @@ +@@ -28,4 +_,15 @@ + @Override public abstract RecipeSerializer getSerializer(); - ++ + // CraftBukkit start + @Override + public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) { -+ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.EMPTY); -+ -+ org.bukkit.craftbukkit.inventory.CraftComplexRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftComplexRecipe(id, result, this); ++ org.bukkit.craftbukkit.inventory.CraftComplexRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftComplexRecipe(id, org.bukkit.inventory.ItemStack.empty(), this); + recipe.setGroup(this.group()); + recipe.setCategory(org.bukkit.craftbukkit.inventory.CraftRecipe.getCategory(this.category())); + + return recipe; + } + // CraftBukkit end -+ - public static class Serializer implements RecipeSerializer { - private final MapCodec codec; - private final StreamCodec streamCodec; + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/DyeRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/DyeRecipe.java.patch new file mode 100644 index 000000000000..71a1c0cb5c80 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/DyeRecipe.java.patch @@ -0,0 +1,18 @@ +--- a/net/minecraft/world/item/crafting/DyeRecipe.java ++++ b/net/minecraft/world/item/crafting/DyeRecipe.java +@@ -148,4 +_,15 @@ + ) + ); + } ++ ++ // CraftBukkit start ++ @Override ++ public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) { ++ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result.create()); ++ org.bukkit.craftbukkit.inventory.CraftComplexRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftComplexRecipe(id, result, this); ++ recipe.setGroup(this.group()); ++ recipe.setCategory(org.bukkit.craftbukkit.inventory.CraftRecipe.getCategory(this.category())); ++ return recipe; ++ } ++ // CraftBukkit end + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/ImbueRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/ImbueRecipe.java.patch new file mode 100644 index 000000000000..5ee63591cb3d --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/ImbueRecipe.java.patch @@ -0,0 +1,18 @@ +--- a/net/minecraft/world/item/crafting/ImbueRecipe.java ++++ b/net/minecraft/world/item/crafting/ImbueRecipe.java +@@ -113,4 +_,15 @@ + ) + ); + } ++ ++ // CraftBukkit start ++ @Override ++ public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) { ++ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result.create()); ++ org.bukkit.craftbukkit.inventory.CraftComplexRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftComplexRecipe(id, result, this); ++ recipe.setGroup(this.group()); ++ recipe.setCategory(org.bukkit.craftbukkit.inventory.CraftRecipe.getCategory(this.category())); ++ return recipe; ++ } ++ // CraftBukkit end + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/Ingredient.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/Ingredient.java.patch index 28c29f80e678..688cfaa2d6ef 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/Ingredient.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/Ingredient.java.patch @@ -1,39 +1,37 @@ --- a/net/minecraft/world/item/crafting/Ingredient.java +++ b/net/minecraft/world/item/crafting/Ingredient.java -@@ -33,6 +_,25 @@ - public static final Codec CODEC = ExtraCodecs.nonEmptyHolderSet(NON_AIR_HOLDER_SET_CODEC) - .xmap(Ingredient::new, ingredient -> ingredient.values); +@@ -33,6 +_,23 @@ + public static final Codec> NON_AIR_HOLDER_SET_CODEC = HolderSetCodec.create(Registries.ITEM, Item.CODEC, false); + public static final Codec CODEC = ExtraCodecs.nonEmptyHolderSet(NON_AIR_HOLDER_SET_CODEC).xmap(Ingredient::new, i -> i.values); public final HolderSet values; + // CraftBukkit start -+ @javax.annotation.Nullable -+ private java.util.List itemStacks; ++ private java.util.@org.jspecify.annotations.Nullable List itemStacks; + + public boolean isExact() { + return this.itemStacks != null; + } + -+ @javax.annotation.Nullable -+ public java.util.List itemStacks() { ++ public java.util.@org.jspecify.annotations.Nullable List itemStacks() { + return this.itemStacks; + } + + public static Ingredient ofStacks(java.util.List stacks) { -+ Ingredient recipe = Ingredient.of(stacks.stream().map(ItemStack::getItem)); -+ recipe.itemStacks = stacks; -+ return recipe; ++ Ingredient ingredient = Ingredient.of(stacks.stream().map(ItemStack::getItem)); ++ ingredient.itemStacks = stacks; ++ return ingredient; + } + // CraftBukkit end - private Ingredient(HolderSet values) { - values.unwrap().ifRight(list -> { + private Ingredient(final HolderSet values) { + values.unwrap().ifRight(directValues -> { @@ -60,6 +_,17 @@ @Override - public boolean test(ItemStack stack) { + public boolean test(final ItemStack input) { + // CraftBukkit start + if (this.isExact()) { -+ for (ItemStack itemstack1 : this.itemStacks()) { -+ if (itemstack1.getItem() == stack.getItem() && ItemStack.isSameItemSameComponents(stack, itemstack1)) { ++ for (ItemStack item : this.itemStacks()) { ++ if (item.getItem() == input.getItem() && ItemStack.isSameItemSameComponents(input, item)) { + return true; + } + } @@ -41,15 +39,15 @@ + return false; + } + // CraftBukkit end - return stack.is(this.values); + return input.is(this.values); } @@ -70,7 +_,7 @@ @Override - public boolean equals(Object other) { -- return other instanceof Ingredient ingredient && Objects.equals(this.values, ingredient.values); -+ return other instanceof Ingredient ingredient && Objects.equals(this.values, ingredient.values) && Objects.equals(this.itemStacks, ingredient.itemStacks); // CraftBukkit + public boolean equals(final Object o) { +- return o instanceof Ingredient other && Objects.equals(this.values, other.values); ++ return o instanceof Ingredient other && Objects.equals(this.values, other.values) && Objects.equals(this.itemStacks, other.itemStacks); // CraftBukkit } - public static Ingredient of(ItemLike item) { + public static Ingredient of(final ItemLike itemLike) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/Recipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/Recipe.java.patch index 9bad69b28f0e..e125c6cda4cf 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/Recipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/Recipe.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/item/crafting/Recipe.java +++ b/net/minecraft/world/item/crafting/Recipe.java -@@ -46,4 +_,6 @@ +@@ -82,4 +_,6 @@ + ByteBufCodecs.BOOL, Recipe.CommonInfo::showNotification, Recipe.CommonInfo::new + ); } - - RecipeBookCategory recipeBookCategory(); + + org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id); // CraftBukkit } diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeHolder.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeHolder.java.patch index a62881aad338..d07c4a802be3 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeHolder.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeHolder.java.patch @@ -5,11 +5,11 @@ ); + // CraftBukkit start -+ public final org.bukkit.inventory.Recipe toBukkitRecipe() { ++ public org.bukkit.inventory.Recipe toBukkitRecipe() { + return this.value.toBukkitRecipe(org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(this.id.identifier())); + } + // CraftBukkit end + @Override - public boolean equals(Object other) { - return this == other || other instanceof RecipeHolder recipeHolder && this.id == recipeHolder.id; + public boolean equals(final Object obj) { + return this == obj || obj instanceof RecipeHolder holder && this.id == holder.id; diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeManager.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeManager.java.patch index 31ec46f411c1..764366ac534e 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeManager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeManager.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/item/crafting/RecipeManager.java +++ b/net/minecraft/world/item/crafting/RecipeManager.java @@ -87,7 +_,26 @@ - LOGGER.info("Loaded {} recipes", object.values().size()); + LOGGER.info("Loaded {} recipes", recipes.values().size()); } + // CraftBukkit start @@ -11,42 +11,42 @@ + this.finalizeRecipeLoading(); + } + -+ private FeatureFlagSet featureflagset; ++ private FeatureFlagSet enabledFlags; + + public void finalizeRecipeLoading() { -+ if (this.featureflagset != null) { -+ this.finalizeRecipeLoading(this.featureflagset); ++ if (this.enabledFlags != null) { ++ this.finalizeRecipeLoading(this.enabledFlags); + + net.minecraft.server.MinecraftServer.getServer().getPlayerList().reloadResources(); + } + } + - public void finalizeRecipeLoading(FeatureFlagSet enabledFeatures) { -+ this.featureflagset = enabledFeatures; + public void finalizeRecipeLoading(final FeatureFlagSet enabledFlags) { ++ this.enabledFlags = enabledFlags; + // CraftBukkit end - List> list = new ArrayList<>(); - List list1 = RECIPE_PROPERTY_SETS.entrySet() + List> stonecutterRecipes = new ArrayList<>(); + List propertySetCollectors = RECIPE_PROPERTY_SETS.entrySet() .stream() -@@ -147,7 +_,10 @@ +@@ -144,7 +_,10 @@ } - public > Optional> getRecipeFor(RecipeType recipeType, I input, Level level) { -- return this.recipes.getRecipesFor(recipeType, input, level).findFirst(); + public > Optional> getRecipeFor(final RecipeType type, final I input, final Level level) { +- return this.recipes.getRecipesFor(type, input, level).findFirst(); + // CraftBukkit start -+ List> list = this.recipes.getRecipesFor(recipeType, input, level).toList(); ++ List> list = this.recipes.getRecipesFor(type, input, level).toList(); + return (list.isEmpty()) ? Optional.empty() : Optional.of(list.getLast()); // CraftBukkit - SPIGOT-4638: last recipe gets priority + // CraftBukkit end } - public Optional> byKey(ResourceKey> key) { -@@ -198,6 +_,22 @@ - Recipe recipe1 = Recipe.CODEC.parse(registries.createSerializationContext(JsonOps.INSTANCE), json).getOrThrow(JsonParseException::new); - return new RecipeHolder<>(recipe, recipe1); + public Optional> byKey(final ResourceKey> recipeId) { +@@ -195,6 +_,22 @@ + Recipe recipe = Recipe.CODEC.parse(registries.createSerializationContext(JsonOps.INSTANCE), object).getOrThrow(JsonParseException::new); + return new RecipeHolder<>(id, recipe); } + + // CraftBukkit start -+ public boolean removeRecipe(ResourceKey> mcKey) { -+ boolean removed = this.recipes.removeRecipe((ResourceKey>) (ResourceKey) mcKey); // Paper - generic fix ++ public boolean removeRecipe(ResourceKey> id) { ++ boolean removed = this.recipes.removeRecipe(id); + if (removed) { + this.finalizeRecipeLoading(); + } @@ -60,5 +60,5 @@ + } + // CraftBukkit end - public static > RecipeManager.CachedCheck createCheck(final RecipeType recipeType) { + public static > RecipeManager.CachedCheck createCheck(final RecipeType type) { return new RecipeManager.CachedCheck() { diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeMap.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeMap.java.patch index b5b71509f500..ce0c7ab496f1 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeMap.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/RecipeMap.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/world/item/crafting/RecipeMap.java +++ b/net/minecraft/world/item/crafting/RecipeMap.java -@@ -30,8 +_,34 @@ - builder1.put(recipeHolder.id(), recipeHolder); +@@ -30,8 +_,32 @@ + byKey.put(recipe.id(), recipe); } -- return new RecipeMap(builder.build(), builder1.build()); +- return new RecipeMap(byType.build(), byKey.build()); - } + // CraftBukkit start - mutable -+ return new RecipeMap(com.google.common.collect.LinkedHashMultimap.create(builder.build()), com.google.common.collect.Maps.newLinkedHashMap(builder1.build())); ++ return new RecipeMap(com.google.common.collect.LinkedHashMultimap.create(byType.build()), com.google.common.collect.Maps.newLinkedHashMap(byKey.build())); + } + + public void addRecipe(RecipeHolder holder) { @@ -23,17 +23,15 @@ + // CraftBukkit end + + // Paper start - replace removeRecipe implementation -+ public boolean removeRecipe(ResourceKey> mcKey) { -+ //noinspection unchecked -+ final RecipeHolder> remove = (RecipeHolder>) this.byKey.remove(mcKey); ++ public boolean removeRecipe(ResourceKey> id) { ++ final RecipeHolder remove = this.byKey.remove(id); + if (remove == null) { + return false; + } -+ final Collection>> recipes = this.byType(remove.value().getType()); ++ final Collection> recipes = this.byType(remove.value().getType()); + return recipes.remove(remove); -+ // Paper end - why are you using a loop??? + } + // Paper end - replace removeRecipe implementation - public > Collection> byType(RecipeType type) { + public > Collection> byType(final RecipeType type) { return (Collection)this.byType.get(type); diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/ShapedRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/ShapedRecipe.java.patch index 075f7f7cf2af..67342cb81923 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/ShapedRecipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/ShapedRecipe.java.patch @@ -1,61 +1,62 @@ --- a/net/minecraft/world/item/crafting/ShapedRecipe.java +++ b/net/minecraft/world/item/crafting/ShapedRecipe.java -@@ -102,6 +_,68 @@ +@@ -93,4 +_,66 @@ + ) ); } - ++ + // CraftBukkit start + @Override + public org.bukkit.inventory.ShapedRecipe toBukkitRecipe(org.bukkit.NamespacedKey id) { -+ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result); ++ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result.create()); + org.bukkit.craftbukkit.inventory.CraftShapedRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftShapedRecipe(id, result, this); -+ recipe.setGroup(this.group); ++ recipe.setGroup(this.group()); + recipe.setCategory(org.bukkit.craftbukkit.inventory.CraftRecipe.getCategory(this.category())); + + switch (this.pattern.height()) { -+ case 1: -+ switch (this.pattern.width()) { + case 1: -+ recipe.shape("a"); ++ switch (this.pattern.width()) { ++ case 1: ++ recipe.shape("a"); ++ break; ++ case 2: ++ recipe.shape("ab"); ++ break; ++ case 3: ++ recipe.shape("abc"); ++ break; ++ } + break; + case 2: -+ recipe.shape("ab"); ++ switch (this.pattern.width()) { ++ case 1: ++ recipe.shape("a", "b"); ++ break; ++ case 2: ++ recipe.shape("ab", "cd"); ++ break; ++ case 3: ++ recipe.shape("abc", "def"); ++ break; ++ } + break; + case 3: -+ recipe.shape("abc"); ++ switch (this.pattern.width()) { ++ case 1: ++ recipe.shape("a", "b", "c"); ++ break; ++ case 2: ++ recipe.shape("ab", "cd", "ef"); ++ break; ++ case 3: ++ recipe.shape("abc", "def", "ghi"); ++ break; ++ } + break; -+ } -+ break; -+ case 2: -+ switch (this.pattern.width()) { -+ case 1: -+ recipe.shape("a","b"); -+ break; -+ case 2: -+ recipe.shape("ab","cd"); -+ break; -+ case 3: -+ recipe.shape("abc","def"); -+ break; -+ } -+ break; -+ case 3: -+ switch (this.pattern.width()) { -+ case 1: -+ recipe.shape("a","b","c"); -+ break; -+ case 2: -+ recipe.shape("ab","cd","ef"); -+ break; -+ case 3: -+ recipe.shape("abc","def","ghi"); -+ break; -+ } -+ break; + } + char c = 'a'; -+ for (Optional list : this.pattern.ingredients()) { -+ org.bukkit.inventory.RecipeChoice choice = org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(list); ++ for (Optional ingredient : this.pattern.ingredients()) { ++ org.bukkit.inventory.RecipeChoice choice = org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(ingredient); + if (choice != org.bukkit.inventory.RecipeChoice.empty()) { // Paper + recipe.setIngredient(c, choice); + } @@ -65,7 +66,4 @@ + return recipe; + } + // CraftBukkit end -+ - public static class Serializer implements RecipeSerializer { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group( + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/ShapelessRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/ShapelessRecipe.java.patch index be840f254289..e3000fc680b5 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/ShapelessRecipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/ShapelessRecipe.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/item/crafting/ShapelessRecipe.java +++ b/net/minecraft/world/item/crafting/ShapelessRecipe.java -@@ -30,6 +_,21 @@ +@@ -47,6 +_,21 @@ this.ingredients = ingredients; } + // CraftBukkit start + @Override + public org.bukkit.inventory.ShapelessRecipe toBukkitRecipe(org.bukkit.NamespacedKey id) { -+ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result); ++ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result.create()); + org.bukkit.craftbukkit.inventory.CraftShapelessRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftShapelessRecipe(id, result, this); -+ recipe.setGroup(this.group); ++ recipe.setGroup(this.group()); + recipe.setCategory(org.bukkit.craftbukkit.inventory.CraftRecipe.getCategory(this.category())); + -+ for (Ingredient list : this.ingredients) { -+ recipe.addIngredient(org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(list)); ++ for (Ingredient ingredient : this.ingredients) { ++ recipe.addIngredient(org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(ingredient)); + } + return recipe; + } @@ -21,4 +21,4 @@ + @Override public RecipeSerializer getSerializer() { - return RecipeSerializer.SHAPELESS_RECIPE; + return SERIALIZER; diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/SmeltingRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/SmeltingRecipe.java.patch index c174cfdcf8b9..2dfb29e06639 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/SmeltingRecipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/SmeltingRecipe.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/crafting/SmeltingRecipe.java +++ b/net/minecraft/world/item/crafting/SmeltingRecipe.java -@@ -32,4 +_,17 @@ +@@ -46,4 +_,17 @@ case MISC -> RecipeBookCategories.FURNACE_MISC; }; } @@ -8,9 +8,9 @@ + // CraftBukkit start + @Override + public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) { -+ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result().create()); + -+ org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.input()), this.experience(), this.cookingTime()); ++ org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.input()), this.experience(), this.cookingTime()); + recipe.setGroup(this.group()); + recipe.setCategory(org.bukkit.craftbukkit.inventory.CraftRecipe.getCategory(this.category())); + diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/SmithingTransformRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/SmithingTransformRecipe.java.patch index 4058cfeaa29d..dd05ac028313 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/SmithingTransformRecipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/SmithingTransformRecipe.java.patch @@ -1,31 +1,45 @@ --- a/net/minecraft/world/item/crafting/SmithingTransformRecipe.java +++ b/net/minecraft/world/item/crafting/SmithingTransformRecipe.java -@@ -20,8 +_,15 @@ - final Optional addition; - final TransmuteResult result; - private @Nullable PlacementInfo placementInfo; -+ final boolean copyDataComponents; // Paper - Option to prevent data components copy +@@ -42,6 +_,7 @@ + private final Ingredient base; + private final Optional addition; + private final ItemStackTemplate result; ++ private final boolean copyDataComponents; // Paper - Option to prevent data components copy - public SmithingTransformRecipe(Optional template, Ingredient base, Optional addition, TransmuteResult result) { + public SmithingTransformRecipe( + final Recipe.CommonInfo commonInfo, +@@ -50,7 +_,21 @@ + final Optional addition, + final ItemStackTemplate result + ) { + // Paper start - Option to prevent data components copy -+ this(template, base, addition, result, true); ++ this(commonInfo, template, base, addition, result, true); + } -+ public SmithingTransformRecipe(Optional template, Ingredient base, Optional addition, TransmuteResult result, boolean copyDataComponents) { ++ ++ public SmithingTransformRecipe( ++ final Recipe.CommonInfo commonInfo, ++ final Optional template, ++ final Ingredient base, ++ final Optional addition, ++ final ItemStackTemplate result, ++ final boolean copyDataComponents ++ ) { + super(commonInfo); + this.copyDataComponents = copyDataComponents; + // Paper end - Option to prevent data components copy this.template = template; this.base = base; this.addition = addition; -@@ -30,7 +_,7 @@ +@@ -59,7 +_,7 @@ @Override - public ItemStack assemble(SmithingRecipeInput input, HolderLookup.Provider registries) { -- return this.result.apply(input.base()); -+ return this.result.apply(input.base(), this.copyDataComponents); // Paper - Option to prevent data components copy + public ItemStack assemble(final SmithingRecipeInput input) { +- return TransmuteRecipe.createWithOriginalComponents(this.result, input.base()); ++ return this.copyDataComponents ? TransmuteRecipe.createWithOriginalComponents(this.result, input.base()) : this.result.create(); // Paper - Option to prevent data components copy } @Override -@@ -74,6 +_,17 @@ +@@ -99,4 +_,13 @@ ) ); } @@ -33,13 +47,9 @@ + // CraftBukkit start + @Override + public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) { -+ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(new ItemStack(this.result.item(), this.result.count(), this.result.components())); -+ -+ org.bukkit.craftbukkit.inventory.CraftSmithingTransformRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftSmithingTransformRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.template), org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.base), org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.addition), this.copyDataComponents); // Paper - Option to prevent data components copy ++ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result.create()); + -+ return recipe; ++ return new org.bukkit.craftbukkit.inventory.CraftSmithingTransformRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.template), org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.base), org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.addition), this.copyDataComponents); + } + // CraftBukkit end - - public static class Serializer implements RecipeSerializer { - private static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/SmithingTrimRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/SmithingTrimRecipe.java.patch index b71e1693cffa..8fd6968d7fbd 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/SmithingTrimRecipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/SmithingTrimRecipe.java.patch @@ -1,58 +1,60 @@ --- a/net/minecraft/world/item/crafting/SmithingTrimRecipe.java +++ b/net/minecraft/world/item/crafting/SmithingTrimRecipe.java -@@ -27,8 +_,15 @@ - final Ingredient addition; - final Holder pattern; - private @Nullable PlacementInfo placementInfo; +@@ -47,11 +_,20 @@ + private final Ingredient base; + private final Ingredient addition; + private final Holder pattern; + final boolean copyDataComponents; // Paper - Option to prevent data components copy - public SmithingTrimRecipe(Ingredient template, Ingredient base, Ingredient addition, Holder pattern) { + public SmithingTrimRecipe( + final Recipe.CommonInfo commonInfo, final Ingredient template, final Ingredient base, final Ingredient addition, final Holder pattern + ) { + // Paper start - Option to prevent data components copy -+ this(template, base, addition, pattern, true); ++ this(commonInfo, template, base, addition, pattern, true); + } -+ public SmithingTrimRecipe(Ingredient template, Ingredient base, Ingredient addition, Holder pattern, final boolean copyDataComponents) { -+ this.copyDataComponents = copyDataComponents; ++ public SmithingTrimRecipe( ++ final Recipe.CommonInfo commonInfo, final Ingredient template, final Ingredient base, final Ingredient addition, final Holder pattern, final boolean copyDataComponents ++ ) { + // Paper end - Option to prevent data components copy + super(commonInfo); ++ this.copyDataComponents = copyDataComponents; // Paper this.template = template; this.base = base; this.addition = addition; -@@ -37,10 +_,15 @@ +@@ -60,10 +_,15 @@ @Override - public ItemStack assemble(SmithingRecipeInput input, HolderLookup.Provider registries) { -- return applyTrim(registries, input.base(), input.addition(), this.pattern); -+ return applyTrim(registries, input.base(), input.addition(), this.pattern, this.copyDataComponents); // Paper start - Option to prevent data components copy + public ItemStack assemble(final SmithingRecipeInput input) { +- return applyTrim(input.base(), input.addition(), this.pattern); ++ return applyTrim(input.base(), input.addition(), this.pattern, this.copyDataComponents); // Paper start - Option to prevent data components copy } - public static ItemStack applyTrim(HolderLookup.Provider registries, ItemStack base, ItemStack addition, Holder pattern) { -+ // Paper start - Option to prevent data components copy -+ return applyTrim(registries, base, addition, pattern, true); + public static ItemStack applyTrim(final ItemStack baseItem, final ItemStack materialItem, final Holder pattern) { ++ // Paper start - Option to prevent data components copy ++ return applyTrim(baseItem, materialItem, pattern, true); + } -+ public static ItemStack applyTrim(HolderLookup.Provider registries, ItemStack base, ItemStack addition, Holder pattern, final boolean copyDataComponents) { -+ // Paper end - Option to prevent data components copy - Optional> fromIngredient = TrimMaterials.getFromIngredient(registries, addition); - if (fromIngredient.isPresent()) { - ArmorTrim armorTrim = base.get(DataComponents.TRIM); -@@ -48,7 +_,7 @@ - if (Objects.equals(armorTrim, armorTrim1)) { ++ public static ItemStack applyTrim(final ItemStack baseItem, final ItemStack materialItem, final Holder pattern, final boolean copyDataComponents) { ++ // Paper end - Option to prevent data components copy + Holder material = materialItem.get(DataComponents.PROVIDES_TRIM_MATERIAL); + if (material != null) { + ArmorTrim existingTrim = baseItem.get(DataComponents.TRIM); +@@ -71,7 +_,7 @@ + if (Objects.equals(existingTrim, newTrim)) { return ItemStack.EMPTY; } else { -- ItemStack itemStack = base.copyWithCount(1); -+ ItemStack itemStack = copyDataComponents ? base.copyWithCount(1) : new ItemStack(base.getItem(), 1); // Paper - Option to prevent data components copy - itemStack.set(DataComponents.TRIM, armorTrim1); - return itemStack; +- ItemStack trimmedItem = baseItem.copyWithCount(1); ++ ItemStack trimmedItem = copyDataComponents ? baseItem.copyWithCount(1) : new ItemStack(baseItem.getItem(), 1); // Paper - Option to prevent data components copy + trimmedItem.set(DataComponents.TRIM, newTrim); + return trimmedItem; } -@@ -101,6 +_,13 @@ +@@ -120,4 +_,10 @@ ) ); } -+ + // CraftBukkit start + @Override + public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) { -+ return new org.bukkit.craftbukkit.inventory.CraftSmithingTrimRecipe(id, org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.template), org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.base), org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.addition), org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern.minecraftHolderToBukkit(this.pattern), this.copyDataComponents); // Paper - Option to prevent data components copy ++ return new org.bukkit.craftbukkit.inventory.CraftSmithingTrimRecipe(id, org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.template), org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.base), org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.addition), org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern.minecraftHolderToBukkit(this.pattern), this.copyDataComponents); // Paper - Option to prevent data components copy + } + // CraftBukkit end - - public static class Serializer implements RecipeSerializer { - private static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/SmokingRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/SmokingRecipe.java.patch index e952bdf1b630..6b61f7778ad1 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/SmokingRecipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/SmokingRecipe.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/crafting/SmokingRecipe.java +++ b/net/minecraft/world/item/crafting/SmokingRecipe.java -@@ -28,4 +_,17 @@ +@@ -42,4 +_,17 @@ public RecipeBookCategory recipeBookCategory() { return RecipeBookCategories.SMOKER_FOOD; } @@ -8,9 +8,9 @@ + // CraftBukkit start + @Override + public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) { -+ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result().create()); + -+ org.bukkit.craftbukkit.inventory.CraftSmokingRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftSmokingRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.input()), this.experience(), this.cookingTime()); ++ org.bukkit.craftbukkit.inventory.CraftSmokingRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftSmokingRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.input()), this.experience(), this.cookingTime()); + recipe.setGroup(this.group()); + recipe.setCategory(org.bukkit.craftbukkit.inventory.CraftRecipe.getCategory(this.category())); + diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/StonecutterRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/StonecutterRecipe.java.patch index daa51ba292ed..255300651973 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/StonecutterRecipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/StonecutterRecipe.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/crafting/StonecutterRecipe.java +++ b/net/minecraft/world/item/crafting/StonecutterRecipe.java -@@ -35,4 +_,16 @@ +@@ -47,4 +_,13 @@ public RecipeBookCategory recipeBookCategory() { return RecipeBookCategories.STONECUTTER; } @@ -8,12 +8,9 @@ + // CraftBukkit start + @Override + public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) { -+ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result()); ++ org.bukkit.craftbukkit.inventory.CraftItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this.result().create()); + -+ org.bukkit.craftbukkit.inventory.CraftStonecuttingRecipe recipe = new org.bukkit.craftbukkit.inventory.CraftStonecuttingRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.input())); -+ recipe.setGroup(this.group()); -+ -+ return recipe; ++ return new org.bukkit.craftbukkit.inventory.CraftStonecuttingRecipe(id, result, org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.input())); + } + // CraftBukkit end } diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/TransmuteRecipe.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/TransmuteRecipe.java.patch index 742d7149df35..7cbaacdf70a0 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/TransmuteRecipe.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/crafting/TransmuteRecipe.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/world/item/crafting/TransmuteRecipe.java +++ b/net/minecraft/world/item/crafting/TransmuteRecipe.java -@@ -84,6 +_,13 @@ - ); +@@ -201,6 +_,13 @@ + return this.materialCount.max().orElse(8); } + // CraftBukkit start + @Override + public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) { -+ return new org.bukkit.craftbukkit.inventory.CraftTransmuteRecipe(id, org.bukkit.craftbukkit.inventory.CraftItemType.minecraftToBukkit(this.result.item().value()), org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.input), org.bukkit.craftbukkit.inventory.CraftRecipe.toBukkit(this.material)); ++ return new org.bukkit.craftbukkit.inventory.CraftTransmuteRecipe(id, org.bukkit.craftbukkit.inventory.CraftItemType.minecraftToBukkit(this.result.item().value()), org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.input), org.bukkit.craftbukkit.inventory.CraftRecipe.toChoice(this.material)); + } + // CraftBukkit end + @Override public RecipeSerializer getSerializer() { - return RecipeSerializer.TRANSMUTE; + return SERIALIZER; diff --git a/paper-server/patches/sources/net/minecraft/world/item/crafting/TransmuteResult.java.patch b/paper-server/patches/sources/net/minecraft/world/item/crafting/TransmuteResult.java.patch deleted file mode 100644 index e261851208fc..000000000000 --- a/paper-server/patches/sources/net/minecraft/world/item/crafting/TransmuteResult.java.patch +++ /dev/null @@ -1,17 +0,0 @@ ---- a/net/minecraft/world/item/crafting/TransmuteResult.java -+++ b/net/minecraft/world/item/crafting/TransmuteResult.java -@@ -45,8 +_,13 @@ - } - - public ItemStack apply(ItemStack stack) { -+ // Paper start - Option to prevent data components copy -+ return apply(stack, true); -+ } -+ public ItemStack apply(ItemStack stack, boolean applyComponents) { -+ // Paper end - Option to prevent data components copy - ItemStack itemStack = stack.transmuteCopy(this.item.value(), this.count); -- itemStack.applyComponents(this.components); -+ if (applyComponents) itemStack.applyComponents(this.components); // Paper - Option to prevent data components copy - return itemStack; - } - diff --git a/paper-server/patches/sources/net/minecraft/world/item/enchantment/EnchantmentHelper.java.patch b/paper-server/patches/sources/net/minecraft/world/item/enchantment/EnchantmentHelper.java.patch index beb8db3ee9fc..710eb1e8c82e 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/enchantment/EnchantmentHelper.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/enchantment/EnchantmentHelper.java.patch @@ -1,18 +1,18 @@ --- a/net/minecraft/world/item/enchantment/EnchantmentHelper.java +++ b/net/minecraft/world/item/enchantment/EnchantmentHelper.java -@@ -52,8 +_,14 @@ +@@ -53,8 +_,14 @@ } - public static ItemEnchantments updateEnchantments(ItemStack stack, Consumer updater) { + public static ItemEnchantments updateEnchantments(final ItemStack itemStack, final Consumer consumer) { + // Paper start - allowing updating enchantments on items without component -+ return updateEnchantments(stack, updater, false); ++ return updateEnchantments(itemStack, consumer, false); + } + -+ public static ItemEnchantments updateEnchantments(ItemStack stack, Consumer updater, final boolean createComponentIfMissing) { ++ public static ItemEnchantments updateEnchantments(final ItemStack itemStack, final Consumer consumer, final boolean createComponentIfMissing) { + // Paper end - allowing updating enchantments on items without component - DataComponentType componentType = getComponentType(stack); -- ItemEnchantments itemEnchantments = stack.get(componentType); -+ ItemEnchantments itemEnchantments = createComponentIfMissing ? stack.getOrDefault(componentType, ItemEnchantments.EMPTY) : stack.get(componentType); // Paper - allowing updating enchantments on items without component - if (itemEnchantments == null) { + DataComponentType componentType = getComponentType(itemStack); +- ItemEnchantments oldEnchantments = itemStack.get(componentType); ++ ItemEnchantments oldEnchantments = createComponentIfMissing ? itemStack.getOrDefault(componentType, ItemEnchantments.EMPTY) : itemStack.get(componentType); // Paper - allowing updating enchantments on items without component + if (oldEnchantments == null) { return ItemEnchantments.EMPTY; } else { diff --git a/paper-server/patches/sources/net/minecraft/world/item/enchantment/ItemEnchantments.java.patch b/paper-server/patches/sources/net/minecraft/world/item/enchantment/ItemEnchantments.java.patch index 69112ac4b10c..d09e74863916 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/enchantment/ItemEnchantments.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/enchantment/ItemEnchantments.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/enchantment/ItemEnchantments.java +++ b/net/minecraft/world/item/enchantment/ItemEnchantments.java -@@ -28,21 +_,24 @@ +@@ -28,19 +_,22 @@ import org.jspecify.annotations.Nullable; public class ItemEnchantments implements TooltipProvider { @@ -14,28 +14,26 @@ .xmap( - map -> new ItemEnchantments(new Object2IntOpenHashMap<>((Map, ? extends Integer>)map)), + map -> new net.minecraft.world.item.enchantment.ItemEnchantments(net.minecraft.util.Util.make(new it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap<>(ENCHANTMENT_ORDER), m -> m.putAll(map))), // Paper - sort enchantments - itemEnchantments -> itemEnchantments.enchantments + enchantments -> enchantments.enchantments ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( -- ByteBufCodecs.map(Object2IntOpenHashMap::new, Enchantment.STREAM_CODEC, ByteBufCodecs.VAR_INT), -+ ByteBufCodecs.map((v) -> new it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap<>(ENCHANTMENT_ORDER), Enchantment.STREAM_CODEC, ByteBufCodecs.VAR_INT), // Paper - itemEnchantments -> itemEnchantments.enchantments, - ItemEnchantments::new +- ByteBufCodecs.map(Object2IntOpenHashMap::new, Enchantment.STREAM_CODEC, ByteBufCodecs.VAR_INT), c -> c.enchantments, ItemEnchantments::new ++ ByteBufCodecs.map((v) -> new it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap<>(ENCHANTMENT_ORDER), Enchantment.STREAM_CODEC, ByteBufCodecs.VAR_INT), c -> c.enchantments, ItemEnchantments::new // Paper ); -- final Object2IntOpenHashMap> enchantments; -+ final it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap> enchantments; // Paper +- private final Object2IntOpenHashMap> enchantments; ++ private final it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap> enchantments; // Paper -- ItemEnchantments(Object2IntOpenHashMap> enchantments) { -+ ItemEnchantments(it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap> enchantments) { // Paper +- private ItemEnchantments(final Object2IntOpenHashMap> enchantments) { ++ private ItemEnchantments(final it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap> enchantments) { // Paper this.enchantments = enchantments; for (Entry> entry : enchantments.object2IntEntrySet()) { -@@ -120,7 +_,7 @@ +@@ -122,7 +_,7 @@ } public static class Mutable { - private final Object2IntOpenHashMap> enchantments = new Object2IntOpenHashMap<>(); + private final it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap> enchantments = new it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap<>(ENCHANTMENT_ORDER); // Paper - public Mutable(ItemEnchantments enchantments) { + public Mutable(final ItemEnchantments enchantments) { this.enchantments.putAll(enchantments.enchantments); diff --git a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ApplyExhaustion.java.patch b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ApplyExhaustion.java.patch index 6c7e2f12762e..097c4e81a4c9 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ApplyExhaustion.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ApplyExhaustion.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/item/enchantment/effects/ApplyExhaustion.java @@ -17,7 +_,7 @@ @Override - public void apply(ServerLevel level, int enchantmentLevel, EnchantedItemInUse item, Entity entity, Vec3 origin) { - if (entity instanceof Player player) { -- player.causeFoodExhaustion(this.amount.calculate(enchantmentLevel)); -+ player.causeFoodExhaustion(this.amount.calculate(enchantmentLevel), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.ENCHANTMENT_EFFECT); // Paper - add ExhaustionReason + public void apply(final ServerLevel serverLevel, final int enchantmentLevel, final EnchantedItemInUse item, final Entity entity, final Vec3 position) { + if (entity instanceof Player livingEntity) { +- livingEntity.causeFoodExhaustion(this.amount.calculate(enchantmentLevel)); ++ livingEntity.causeFoodExhaustion(this.amount.calculate(enchantmentLevel), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.ENCHANTMENT_EFFECT); // Paper - add ExhaustionReason } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ApplyMobEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ApplyMobEffect.java.patch index 47f39569b25f..1fb618eacb35 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ApplyMobEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ApplyMobEffect.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/item/enchantment/effects/ApplyMobEffect.java +++ b/net/minecraft/world/item/enchantment/effects/ApplyMobEffect.java @@ -44,7 +_,7 @@ - int max = Math.max( + int amplifier = Math.max( 0, Math.round(Mth.randomBetween(random, this.minAmplifier.calculate(enchantmentLevel), this.maxAmplifier.calculate(enchantmentLevel))) ); -- livingEntity.addEffect(new MobEffectInstance(randomElement.get(), rounded, max)); -+ livingEntity.addEffect(new MobEffectInstance(randomElement.get(), rounded, max), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit +- living.addEffect(new MobEffectInstance(selected.get(), ticks, amplifier)); ++ living.addEffect(new MobEffectInstance(selected.get(), ticks, amplifier), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit } } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ChangeItemDamage.java.patch b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ChangeItemDamage.java.patch index 0de21ed24c3b..f5077c7f54f7 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ChangeItemDamage.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ChangeItemDamage.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/item/enchantment/effects/ChangeItemDamage.java +++ b/net/minecraft/world/item/enchantment/effects/ChangeItemDamage.java -@@ -21,9 +_,9 @@ - public void apply(ServerLevel level, int enchantmentLevel, EnchantedItemInUse item, Entity entity, Vec3 origin) { +@@ -20,9 +_,9 @@ + public void apply(final ServerLevel serverLevel, final int enchantmentLevel, final EnchantedItemInUse item, final Entity entity, final Vec3 position) { ItemStack itemStack = item.itemStack(); if (itemStack.has(DataComponents.MAX_DAMAGE) && itemStack.has(DataComponents.DAMAGE)) { -- ServerPlayer serverPlayer1 = item.owner() instanceof ServerPlayer serverPlayer ? serverPlayer : null; -+ //ServerPlayer serverPlayer1 = item.owner() instanceof ServerPlayer serverPlayer ? serverPlayer : null; // Paper - EntityDamageItemEvent - always pass in entity - int i = (int)this.amount.calculate(enchantmentLevel); -- itemStack.hurtAndBreak(i, level, serverPlayer1, item.onBreak()); -+ itemStack.hurtAndBreak(i, level, item.owner(), item.onBreak()); // Paper - EntityDamageItemEvent - always pass in entity +- ServerPlayer player = item.owner() instanceof ServerPlayer sp ? sp : null; ++ //ServerPlayer player = item.owner() instanceof ServerPlayer sp ? sp : null; // Paper - EntityDamageItemEvent - always pass in entity + int change = (int)this.amount.calculate(enchantmentLevel); +- itemStack.hurtAndBreak(change, serverLevel, player, item.onBreak()); ++ itemStack.hurtAndBreak(change, serverLevel, item.owner(), item.onBreak()); // Paper - EntityDamageItemEvent - always pass in entity } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/Ignite.java.patch b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/Ignite.java.patch index e1daa636a564..43008d9c39d1 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/Ignite.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/Ignite.java.patch @@ -3,7 +3,7 @@ @@ -15,7 +_,21 @@ @Override - public void apply(ServerLevel level, int enchantmentLevel, EnchantedItemInUse item, Entity entity, Vec3 origin) { + public void apply(final ServerLevel serverLevel, final int enchantmentLevel, final EnchantedItemInUse item, final Entity entity, final Vec3 position) { - entity.igniteForSeconds(this.duration.calculate(enchantmentLevel)); + // CraftBukkit start - Call a combust event when somebody hits with a fire enchanted item + org.bukkit.event.entity.EntityCombustEvent entityCombustEvent; diff --git a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ReplaceBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ReplaceBlock.java.patch index a6bbec7a4699..a963dc2f4136 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ReplaceBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ReplaceBlock.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/item/enchantment/effects/ReplaceBlock.java +++ b/net/minecraft/world/item/enchantment/effects/ReplaceBlock.java @@ -30,7 +_,7 @@ - public void apply(ServerLevel level, int enchantmentLevel, EnchantedItemInUse item, Entity entity, Vec3 origin) { - BlockPos blockPos = BlockPos.containing(origin).offset(this.offset); - if (this.predicate.map(blockPredicate -> blockPredicate.test(level, blockPos)).orElse(true) -- && level.setBlockAndUpdate(blockPos, this.blockState.getState(entity.getRandom(), blockPos))) { -+ && org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, blockPos, this.blockState.getState(entity.getRandom(), blockPos), net.minecraft.world.level.block.Block.UPDATE_ALL, entity, true)) { // CraftBukkit - Call EntityBlockFormEvent - this.triggerGameEvent.ifPresent(holder -> level.gameEvent(entity, (Holder)holder, blockPos)); + public void apply(final ServerLevel serverLevel, final int enchantmentLevel, final EnchantedItemInUse item, final Entity entity, final Vec3 position) { + BlockPos pos = BlockPos.containing(position).offset(this.offset); + if (this.predicate.map(p -> p.test(serverLevel, pos)).orElse(true) +- && serverLevel.setBlockAndUpdate(pos, this.blockState.getState(serverLevel, entity.getRandom(), pos))) { ++ && org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(serverLevel, pos, this.blockState.getState(serverLevel, entity.getRandom(), pos), net.minecraft.world.level.block.Block.UPDATE_ALL, entity, true)) { // CraftBukkit - Call EntityBlockFormEvent + this.triggerGameEvent.ifPresent(event -> serverLevel.gameEvent(entity, (Holder)event, pos)); } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ReplaceDisk.java.patch b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ReplaceDisk.java.patch index 595c641c4775..344581b6bf95 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ReplaceDisk.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/ReplaceDisk.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/item/enchantment/effects/ReplaceDisk.java +++ b/net/minecraft/world/item/enchantment/effects/ReplaceDisk.java @@ -47,7 +_,7 @@ - for (BlockPos blockPos1 : BlockPos.betweenClosed(blockPos.offset(-i, 0, -i), blockPos.offset(i, Math.min(i1 - 1, 0), i))) { - if (blockPos1.distToCenterSqr(origin.x(), blockPos1.getY() + 0.5, origin.z()) < Mth.square(i) - && this.predicate.map(predicate -> predicate.test(level, blockPos1)).orElse(true) -- && level.setBlockAndUpdate(blockPos1, this.blockState.getState(random, blockPos1))) { -+ && org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, blockPos1, this.blockState.getState(random, blockPos1), net.minecraft.world.level.block.Block.UPDATE_ALL, entity, true)) { // CraftBukkit - Call EntityBlockFormEvent for Frost Walker - this.triggerGameEvent.ifPresent(event -> level.gameEvent(entity, (Holder)event, blockPos1)); + for (BlockPos pos : BlockPos.betweenClosed(centerBlock.offset(-dist, 0, -dist), centerBlock.offset(dist, Math.min(height - 1, 0), dist))) { + if (pos.distToCenterSqr(position.x(), pos.getY() + 0.5, position.z()) < Mth.square(dist) + && this.predicate.map(p -> p.test(serverLevel, pos)).orElse(true) +- && serverLevel.setBlockAndUpdate(pos, this.blockState.getState(serverLevel, random, pos))) { ++ && org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(serverLevel, pos, this.blockState.getState(serverLevel, random, pos), net.minecraft.world.level.block.Block.UPDATE_ALL, entity, true)) { // CraftBukkit - Call EntityBlockFormEvent for Frost Walker + this.triggerGameEvent.ifPresent(event -> serverLevel.gameEvent(entity, (Holder)event, pos)); } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/SpawnParticlesEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/SpawnParticlesEffect.java.patch index 68a887a1c965..560a2910998d 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/SpawnParticlesEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/SpawnParticlesEffect.java.patch @@ -1,17 +1,17 @@ --- a/net/minecraft/world/item/enchantment/effects/SpawnParticlesEffect.java +++ b/net/minecraft/world/item/enchantment/effects/SpawnParticlesEffect.java -@@ -58,8 +_,13 @@ - Vec3 knownMovement = entity.getKnownMovement(); +@@ -59,8 +_,13 @@ + Vec3 movement = entity.getKnownMovement(); float bbWidth = entity.getBbWidth(); float bbHeight = entity.getBbHeight(); -- level.sendParticles( +- serverLevel.sendParticles( + // Paper start - Hide soul speed particles for vanished players -+ level.sendParticlesSource( ++ serverLevel.sendParticlesSource( + entity, this.particle, + false, + false, -+ // Paper end - Hide soul speed particles for vanished players - this.horizontalPosition.getCoordinate(origin.x(), origin.x(), bbWidth, random), - this.verticalPosition.getCoordinate(origin.y(), origin.y() + bbHeight / 2.0F, bbHeight, random), - this.horizontalPosition.getCoordinate(origin.z(), origin.z(), bbWidth, random), ++ // Paper end - Hide soul speed particles for vanished players + this.horizontalPosition.getCoordinate(position.x(), position.x(), bbWidth, random), + this.verticalPosition.getCoordinate(position.y(), position.y() + bbHeight / 2.0F, bbHeight, random), + this.horizontalPosition.getCoordinate(position.z(), position.z(), bbWidth, random), diff --git a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/SummonEntityEffect.java.patch b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/SummonEntityEffect.java.patch index cf03e4582d65..6a4eaf8128a1 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/SummonEntityEffect.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/enchantment/effects/SummonEntityEffect.java.patch @@ -2,21 +2,21 @@ +++ b/net/minecraft/world/item/enchantment/effects/SummonEntityEffect.java @@ -34,11 +_,18 @@ if (Level.isInSpawnableBounds(blockPos)) { - Optional>> randomElement = this.entityTypes().getRandomElement(level.getRandom()); - if (!randomElement.isEmpty()) { -- Entity entity1 = randomElement.get().value().spawn(level, blockPos, EntitySpawnReason.TRIGGERED); -+ Entity entity1 = randomElement.get().value().create(level, null, blockPos, EntitySpawnReason.TRIGGERED, false, false); // CraftBukkit - if (entity1 != null) { - if (entity1 instanceof LightningBolt lightningBolt && item.owner() instanceof ServerPlayer serverPlayer) { - lightningBolt.setCause(serverPlayer); + Optional>> entityType = this.entityTypes().getRandomElement(serverLevel.getRandom()); + if (!entityType.isEmpty()) { +- Entity spawned = entityType.get().value().spawn(serverLevel, blockPos, EntitySpawnReason.TRIGGERED); ++ Entity spawned = entityType.get().value().create(serverLevel, null, blockPos, EntitySpawnReason.TRIGGERED, false, false); // CraftBukkit + if (spawned != null) { + if (spawned instanceof LightningBolt lightningBolt && item.owner() instanceof ServerPlayer player) { + lightningBolt.setCause(player); } + // CraftBukkit start -+ if (entity1 instanceof LightningBolt) { -+ level.strikeLightning(entity1, (item.itemStack().is(net.minecraft.world.item.Items.TRIDENT)) ? org.bukkit.event.weather.LightningStrikeEvent.Cause.TRIDENT : org.bukkit.event.weather.LightningStrikeEvent.Cause.ENCHANTMENT); ++ if (spawned instanceof LightningBolt) { ++ serverLevel.strikeLightning(spawned, (item.itemStack().is(net.minecraft.world.item.Items.TRIDENT)) ? org.bukkit.event.weather.LightningStrikeEvent.Cause.TRIDENT : org.bukkit.event.weather.LightningStrikeEvent.Cause.ENCHANTMENT); + } else { -+ level.addFreshEntityWithPassengers(entity1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENCHANTMENT); ++ serverLevel.addFreshEntityWithPassengers(spawned, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENCHANTMENT); + } + // CraftBukkit end if (this.joinTeam && entity.getTeam() != null) { - level.getScoreboard().addPlayerToTeam(entity1.getScoreboardName(), entity.getTeam()); + serverLevel.getScoreboard().addPlayerToTeam(spawned.getScoreboardName(), entity.getTeam()); diff --git a/paper-server/patches/sources/net/minecraft/world/item/trading/Merchant.java.patch b/paper-server/patches/sources/net/minecraft/world/item/trading/Merchant.java.patch index 4e3a8a135e4c..a0db2d2fab78 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/trading/Merchant.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/trading/Merchant.java.patch @@ -8,8 +8,8 @@ + void notifyTrade(MerchantOffer offer); - void notifyTradeUpdated(ItemStack stack); -@@ -49,4 +_,6 @@ + void notifyTradeUpdated(ItemStack itemStack); +@@ -47,4 +_,6 @@ boolean isClientSide(); boolean stillValid(Player player); diff --git a/paper-server/patches/sources/net/minecraft/world/item/trading/MerchantOffer.java.patch b/paper-server/patches/sources/net/minecraft/world/item/trading/MerchantOffer.java.patch index acf9f09b756c..f20ec063cada 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/trading/MerchantOffer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/trading/MerchantOffer.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/item/trading/MerchantOffer.java +++ b/net/minecraft/world/item/trading/MerchantOffer.java @@ -21,6 +_,7 @@ - Codec.INT.lenientOptionalFieldOf("demand", 0).forGetter(merchantOffer -> merchantOffer.demand), - Codec.FLOAT.lenientOptionalFieldOf("priceMultiplier", 0.0F).forGetter(merchantOffer -> merchantOffer.priceMultiplier), - Codec.INT.lenientOptionalFieldOf("xp", 1).forGetter(merchantOffer -> merchantOffer.xp) -+ , Codec.BOOL.lenientOptionalFieldOf("Paper.IgnoreDiscounts", false).forGetter(merchantOffer -> merchantOffer.ignoreDiscounts) // Paper + Codec.INT.lenientOptionalFieldOf("demand", 0).forGetter(o -> o.demand), + Codec.FLOAT.lenientOptionalFieldOf("priceMultiplier", 0.0F).forGetter(o -> o.priceMultiplier), + Codec.INT.lenientOptionalFieldOf("xp", 1).forGetter(o -> o.xp) ++ , Codec.BOOL.lenientOptionalFieldOf("Paper.IgnoreDiscounts", false).forGetter(offer -> offer.ignoreDiscounts) // Paper ) - .apply(instance, MerchantOffer::new) + .apply(i, MerchantOffer::new) ); @@ -37,6 +_,21 @@ public int demand; @@ -15,7 +15,7 @@ + public boolean ignoreDiscounts; // Paper - Add ignore discounts API + + // CraftBukkit start -+ private @javax.annotation.Nullable org.bukkit.craftbukkit.inventory.CraftMerchantRecipe bukkitHandle; ++ private org.bukkit.craftbukkit.inventory.@org.jspecify.annotations.Nullable CraftMerchantRecipe bukkitHandle; + + public org.bukkit.craftbukkit.inventory.CraftMerchantRecipe asBukkit() { + return (this.bukkitHandle == null) ? this.bukkitHandle = new org.bukkit.craftbukkit.inventory.CraftMerchantRecipe(this) : this.bukkitHandle; @@ -29,11 +29,11 @@ + // CraftBukkit end private MerchantOffer( - ItemCost baseCostA, + final ItemCost baseCostA, @@ -49,6 +_,7 @@ - int demand, - float priceMultiplier, - int xp + final int demand, + final float priceMultiplier, + final int xp + , final boolean ignoreDiscounts // Paper ) { this.baseCostA = baseCostA; @@ -45,25 +45,25 @@ + this.ignoreDiscounts = ignoreDiscounts; // Paper } - public MerchantOffer(ItemCost baseCostA, ItemStack result, int maxUses, int xp, float priceMultiplier) { -@@ -75,7 +_,7 @@ - } - - public MerchantOffer(ItemCost baseCostA, Optional costB, ItemStack result, int _uses, int maxUses, int xp, float priceMultiplier, int demand) { -- this(baseCostA, costB, result, _uses, maxUses, true, 0, demand, priceMultiplier, xp); -+ this(baseCostA, costB, result, _uses, maxUses, true, 0, demand, priceMultiplier, xp, false); // Paper + public MerchantOffer(final ItemCost buy, final ItemStack result, final int maxUses, final int xp, final float priceMultiplier) { +@@ -94,7 +_,7 @@ + final float priceMultiplier, + final int demand + ) { +- this(baseCostA, costB, result, uses, maxUses, true, 0, demand, priceMultiplier, xp); ++ this(baseCostA, costB, result, uses, maxUses, true, 0, demand, priceMultiplier, xp, false); // Paper } - private MerchantOffer(MerchantOffer other) { -@@ -90,6 +_,7 @@ - other.demand, - other.priceMultiplier, - other.xp -+ , other.ignoreDiscounts // Paper + private MerchantOffer(final MerchantOffer offer) { +@@ -109,6 +_,7 @@ + offer.demand, + offer.priceMultiplier, + offer.xp ++ , offer.ignoreDiscounts // Paper ); } -@@ -125,6 +_,7 @@ +@@ -144,6 +_,7 @@ public void updateDemand() { this.demand = this.demand + this.uses - (this.maxUses - this.uses); @@ -71,16 +71,16 @@ } public ItemStack assemble() { -@@ -205,7 +_,11 @@ - if (!this.satisfiedBy(playerOfferA, playerOfferB)) { +@@ -222,7 +_,11 @@ + if (!this.satisfiedBy(buyA, buyB)) { return false; } else { -- playerOfferA.shrink(this.getCostA().getCount()); +- buyA.shrink(this.getCostA().getCount()); + // CraftBukkit start + if (!this.getCostA().isEmpty()) { -+ playerOfferA.shrink(this.getCostA().getCount()); ++ buyA.shrink(this.getCostA().getCount()); + } + // CraftBukkit end if (!this.getCostB().isEmpty()) { - playerOfferB.shrink(this.getCostB().getCount()); + buyB.shrink(this.getCostB().getCount()); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/BaseCommandBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/BaseCommandBlock.java.patch index d8d9ad1a80df..c7a01f579a58 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/BaseCommandBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/BaseCommandBlock.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/BaseCommandBlock.java +++ b/net/minecraft/world/level/BaseCommandBlock.java @@ -30,6 +_,10 @@ - @Nullable Component lastOutput; + private @Nullable Component lastOutput; private String command = ""; private @Nullable Component customName; + // CraftBukkit start @@ -11,10 +11,10 @@ public int getSuccessCount() { return this.successCount; -@@ -106,7 +_,13 @@ - this.successCount++; - } - }); +@@ -107,7 +_,13 @@ + this.successCount++; + } + }); - level.getServer().getCommands().performPrefixedCommand(commandSourceStack, this.command); + // Paper start - ServerCommandEvent + org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(commandSourceStack.getBukkitSender(), net.minecraft.commands.Commands.trimOptionalPrefix(this.command)); @@ -25,19 +25,19 @@ + // Paper end - ServerCommandEvent } } catch (Throwable var7) { - CrashReport crashReport = CrashReport.forThrowable(var7, "Executing command block"); -@@ -127,8 +_,8 @@ + CrashReport report = CrashReport.forThrowable(var7, "Executing command block"); +@@ -128,8 +_,8 @@ } } -- private BaseCommandBlock.@Nullable CloseableCommandBlockSource createSource(ServerLevel level) { +- private BaseCommandBlock.@Nullable CloseableCommandBlockSource createSource(final ServerLevel level) { - return this.trackOutput ? new BaseCommandBlock.CloseableCommandBlockSource(level) : null; -+ public BaseCommandBlock.CloseableCommandBlockSource createSource(ServerLevel level) { // Paper - public, remove nullable ++ public BaseCommandBlock.CloseableCommandBlockSource createSource(final ServerLevel level) { // Paper - public, remove nullable + return new BaseCommandBlock.CloseableCommandBlockSource(level, this.trackOutput); // Paper - add back source when output disabled } public Component getName() { -@@ -161,13 +_,21 @@ +@@ -162,15 +_,23 @@ public abstract boolean isValid(); @@ -51,6 +51,8 @@ + // Paper start - add back source when output disabled + private final boolean trackOutput; + public CloseableCommandBlockSource(final ServerLevel level, final boolean trackOutput) { + Objects.requireNonNull(BaseCommandBlock.this); + super(); this.level = level; + this.trackOutput = trackOutput; + } @@ -61,7 +63,7 @@ } @Override -@@ -177,7 +_,7 @@ +@@ -180,7 +_,7 @@ @Override public boolean acceptsFailure() { @@ -70,17 +72,17 @@ } @Override -@@ -187,7 +_,8 @@ +@@ -190,7 +_,8 @@ @Override - public void sendSystemMessage(Component message) { + public void sendSystemMessage(final Component message) { - if (!this.closed) { + if (this.trackOutput && !this.closed) { // Paper - add back source when output disabled + org.spigotmc.AsyncCatcher.catchOp("sendSystemMessage to a command block"); // Paper - Don't broadcast messages to command blocks BaseCommandBlock.this.lastOutput = Component.literal("[" + TIME_FORMAT.format(ZonedDateTime.now()) + "] ").append(message); BaseCommandBlock.this.onUpdated(this.level); } -@@ -197,5 +_,12 @@ +@@ -200,5 +_,12 @@ public void close() throws Exception { this.closed = true; } diff --git a/paper-server/patches/sources/net/minecraft/world/level/BaseSpawner.java.patch b/paper-server/patches/sources/net/minecraft/world/level/BaseSpawner.java.patch index e2911c4452ff..df4955477bdd 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/BaseSpawner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/BaseSpawner.java.patch @@ -6,21 +6,21 @@ public int spawnRange = 4; + private int tickDelay = 0; // Paper - Configurable mob spawner tick rate - public void setEntityId(EntityType type, @Nullable Level level, RandomSource random, BlockPos pos) { + public void setEntityId(final EntityType type, final @Nullable Level level, final RandomSource random, final BlockPos pos) { this.getOrCreateNextSpawnData(level, random, pos).getEntityToSpawn().putString("id", BuiltInRegistries.ENTITY_TYPE.getKey(type).toString()); + this.spawnPotentials = WeightedList.of(); // CraftBukkit - SPIGOT-3496, MC-92282 } - public boolean isNearPlayer(Level level, BlockPos pos) { + public boolean isNearPlayer(final Level level, final BlockPos pos) { - return level.hasNearbyAlivePlayer(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, this.requiredPlayerRange); + return level.hasNearbyAlivePlayerThatAffectsSpawning(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, this.requiredPlayerRange); // Paper - Affects Spawning API } - public void clientTick(Level level, BlockPos pos) { + public void clientTick(final Level level, final BlockPos pos) { @@ -81,13 +_,19 @@ } - public void serverTick(ServerLevel level, BlockPos pos) { + public void serverTick(final ServerLevel level, final BlockPos pos) { + if (spawnCount <= 0 || maxNearbyEntities <= 0) return; // Paper - Ignore impossible spawn tick + // Paper start - Configurable mob spawner tick rate + if (spawnDelay > 0 && --tickDelay > 0) return; @@ -37,7 +37,7 @@ - this.spawnDelay--; + this.spawnDelay -= tickDelay; // Paper - Configurable mob spawner tick rate } else { - boolean flag = false; + boolean delay = false; RandomSource random = level.getRandom(); @@ -125,6 +_,21 @@ continue; @@ -45,12 +45,12 @@ + // Paper start - PreCreatureSpawnEvent + com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent event = new com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent( -+ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(vec3, level), -+ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(optional.get()), ++ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(spawnPos, level), ++ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(entityType.get()), + org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level) + ); + if (!event.callEvent()) { -+ flag = true; ++ delay = true; + if (event.shouldAbortSpawn()) { + break; + } @@ -58,10 +58,10 @@ + } + // Paper end - PreCreatureSpawnEvent + - Entity entity = EntityType.loadEntityRecursive(valueInput, level, EntitySpawnReason.SPAWNER, entity1 -> { - entity1.snapTo(vec3.x, vec3.y, vec3.z, entity1.getYRot(), entity1.getXRot()); - return entity1; -@@ -158,9 +_,22 @@ + Entity entity = EntityType.loadEntityRecursive(input, level, EntitySpawnReason.SPAWNER, e -> { + e.snapTo(spawnPos.x, spawnPos.y, spawnPos.z, e.getYRot(), e.getXRot()); + return e; +@@ -159,9 +_,22 @@ } nextSpawnData.getEquipment().ifPresent(mob::equip); @@ -76,7 +76,7 @@ + // Paper start + entity.spawnedViaMobSpawner = true; + entity.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER; -+ flag = true; ++ delay = true; + if (org.bukkit.craftbukkit.event.CraftEventFactory.callSpawnerSpawnEvent(entity, pos).isCancelled()) { + continue; + } @@ -85,22 +85,22 @@ this.delay(level, pos); return; } -@@ -171,7 +_,7 @@ +@@ -172,7 +_,7 @@ ((Mob)entity).spawnAnim(); } -- flag = true; -+ //flag = true; // Paper - moved up above cancellable event +- delay = true; ++ // delay = true; // Paper - moved up above cancellable event } } } -@@ -198,12 +_,14 @@ +@@ -199,12 +_,14 @@ } - public void load(@Nullable Level level, BlockPos pos, ValueInput input) { + public void load(final @Nullable Level level, final BlockPos pos, final ValueInput input) { - this.spawnDelay = input.getShortOr("Delay", (short)20); + this.spawnDelay = input.getIntOr("Paper.Delay", input.getShortOr("Delay", (short) 20)); // Paper - use int if set - input.read("SpawnData", SpawnData.CODEC).ifPresent(spawnData -> this.setNextSpawnData(level, pos, spawnData)); + input.read("SpawnData", SpawnData.CODEC).ifPresent(nextSpawnData -> this.setNextSpawnData(level, pos, nextSpawnData)); this.spawnPotentials = input.read("SpawnPotentials", SpawnData.LIST_CODEC) .orElseGet(() -> WeightedList.of(this.nextSpawnData != null ? this.nextSpawnData : new SpawnData())); - this.minSpawnDelay = input.getIntOr("MinSpawnDelay", 200); @@ -112,10 +112,10 @@ this.spawnCount = input.getIntOr("SpawnCount", 4); this.maxNearbyEntities = input.getIntOr("MaxNearbyEntities", 6); this.requiredPlayerRange = input.getIntOr("RequiredPlayerRange", 16); -@@ -212,9 +_,19 @@ +@@ -213,9 +_,19 @@ } - public void save(ValueOutput output) { + public void save(final ValueOutput output) { - output.putShort("Delay", (short)this.spawnDelay); - output.putShort("MinSpawnDelay", (short)this.minSpawnDelay); - output.putShort("MaxSpawnDelay", (short)this.maxSpawnDelay); diff --git a/paper-server/patches/sources/net/minecraft/world/level/BlockGetter.java.patch b/paper-server/patches/sources/net/minecraft/world/level/BlockGetter.java.patch index aedc95f317ab..8de5af2e6bd0 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/BlockGetter.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/BlockGetter.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/level/BlockGetter.java @@ -31,6 +_,17 @@ - BlockState getBlockState(BlockPos pos); + BlockState getBlockState(final BlockPos pos); + // Paper start - if loaded util + @Nullable BlockState getBlockStateIfLoaded(BlockPos pos); @@ -17,53 +17,47 @@ + FluidState getFluidState(BlockPos pos); - default int getLightEmission(BlockPos pos) { -@@ -64,10 +_,25 @@ - ); + default int getLightEmission(final BlockPos pos) { +@@ -63,9 +_,26 @@ } -- default BlockHitResult clip(ClipContext context) { -- return traverseBlocks(context.getFrom(), context.getTo(), context, (traverseContext, traversePos) -> { -- BlockState blockState = this.getBlockState(traversePos); -- FluidState fluidState = this.getFluidState(traversePos); -+ // CraftBukkit start - moved block handling into separate method for use by Block#rayTrace -+ default BlockHitResult clip(ClipContext traverseContext, BlockPos traversePos) { + default BlockHitResult clip(final ClipContext c) { +- return traverseBlocks(c.getFrom(), c.getTo(), c, (context, pos) -> { +- BlockState blockState = this.getBlockState(pos); +- FluidState fluidState = this.getFluidState(pos); + // Paper start - Add predicate for blocks when raytracing -+ return clip(traverseContext, traversePos, null); ++ return clip(c, (java.util.function.Predicate) null); + } + -+ default BlockHitResult clip(ClipContext traverseContext, BlockPos traversePos, java.util.function.@Nullable Predicate canCollide) { -+ // Paper end - Add predicate for blocks when raytracing -+ // Paper start - Prevent raytrace from loading chunks -+ BlockState blockState = this.getBlockStateIfLoaded(traversePos); ++ default BlockHitResult clip(ClipContext c, BlockPos pos) { ++ return clip(c, pos, null); ++ } ++ ++ default BlockHitResult clip(ClipContext c, BlockPos pos, java.util.function.@Nullable Predicate canCollide) { ++ // Prevent raytrace from loading chunks ++ ClipContext context = c; ++ BlockState blockState = this.getBlockStateIfLoaded(pos); + if (blockState == null) { + // copied the last function parameter (listed below) -+ Vec3 vec3d = traverseContext.getFrom().subtract(traverseContext.getTo()); -+ -+ return BlockHitResult.miss(traverseContext.getTo(), Direction.getApproximateNearest(vec3d.x, vec3d.y, vec3d.z), BlockPos.containing(traverseContext.getTo())); ++ Vec3 delta = context.getFrom().subtract(context.getTo()); ++ return BlockHitResult.miss(context.getTo(), Direction.getApproximateNearest(delta.x, delta.y, delta.z), BlockPos.containing(c.getTo())); + } + // Paper end - Prevent raytrace from loading chunks -+ if (blockState.isAir() || (canCollide != null && this instanceof LevelAccessor levelAccessor && !canCollide.test(org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, traversePos)))) return null; // Paper - Perf: optimise air cases & check canCollide predicate ++ if (blockState.isAir() || (canCollide != null && this instanceof LevelAccessor levelAccessor && !canCollide.test(org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, pos)))) return null; // Paper - Perf: optimise air cases & check canCollide predicate + FluidState fluidState = blockState.getFluidState(); // Paper - Perf: don't need to go to world state again - Vec3 from = traverseContext.getFrom(); - Vec3 to = traverseContext.getTo(); - VoxelShape blockShape = traverseContext.getBlockShape(blockState, this, traversePos); -@@ -77,6 +_,18 @@ - double d = blockHitResult == null ? Double.MAX_VALUE : traverseContext.getFrom().distanceToSqr(blockHitResult.getLocation()); - double d1 = blockHitResult1 == null ? Double.MAX_VALUE : traverseContext.getFrom().distanceToSqr(blockHitResult1.getLocation()); - return d <= d1 ? blockHitResult : blockHitResult1; -+ } -+ // CraftBukkit end -+ -+ default BlockHitResult clip(ClipContext context) { -+ // Paper start - Add predicate for blocks when raytracing -+ return clip(context, (java.util.function.Predicate) null); + Vec3 from = context.getFrom(); + Vec3 to = context.getTo(); + VoxelShape blockShape = context.getBlockShape(blockState, this, pos); +@@ -75,6 +_,12 @@ + double blockDistanceSquared = blockResult == null ? Double.MAX_VALUE : context.getFrom().distanceToSqr(blockResult.getLocation()); + double liquidDistanceSquared = liquidResult == null ? Double.MAX_VALUE : context.getFrom().distanceToSqr(liquidResult.getLocation()); + return blockDistanceSquared <= liquidDistanceSquared ? blockResult : liquidResult; + } + -+ default BlockHitResult clip(ClipContext context, java.util.function.@Nullable Predicate canCollide) { ++ default BlockHitResult clip(ClipContext c, java.util.function.@Nullable Predicate canCollide) { + // Paper end - Add predicate for blocks when raytracing -+ return (BlockHitResult) BlockGetter.traverseBlocks(context.getFrom(), context.getTo(), context, (raytrace1, blockposition) -> { -+ return this.clip(raytrace1, blockposition, canCollide); // CraftBukkit - moved into separate method // Paper - Add predicate for blocks when raytracing - }, failContext -> { - Vec3 vec3 = failContext.getFrom().subtract(failContext.getTo()); - return BlockHitResult.miss(failContext.getTo(), Direction.getApproximateNearest(vec3.x, vec3.y, vec3.z), BlockPos.containing(failContext.getTo())); ++ return traverseBlocks(c.getFrom(), c.getTo(), c, (context, pos) -> { ++ return this.clip(context, pos, canCollide); // CraftBukkit - moved into separate method // Paper - Add predicate for blocks when raytracing + }, context -> { + Vec3 delta = context.getFrom().subtract(context.getTo()); + return BlockHitResult.miss(context.getTo(), Direction.getApproximateNearest(delta.x, delta.y, delta.z), BlockPos.containing(context.getTo())); diff --git a/paper-server/patches/sources/net/minecraft/world/level/ChunkPos.java.patch b/paper-server/patches/sources/net/minecraft/world/level/ChunkPos.java.patch index beab64a5ad72..7b6feb28b42c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/ChunkPos.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/ChunkPos.java.patch @@ -1,39 +1,29 @@ --- a/net/minecraft/world/level/ChunkPos.java +++ b/net/minecraft/world/level/ChunkPos.java -@@ -47,6 +_,7 @@ - public static final int REGION_MAX_INDEX = 31; - public final int x; - public final int z; -+ public final long longKey; // Paper - private static final int HASH_A = 1664525; - private static final int HASH_C = 1013904223; - private static final int HASH_Z_XOR = -559038737; -@@ -54,16 +_,19 @@ - public ChunkPos(int x, int z) { - this.x = x; - this.z = z; -+ this.longKey = asLong(this.x, this.z); // Paper - } - - public ChunkPos(BlockPos pos) { - this.x = SectionPos.blockToSectionCoord(pos.getX()); - this.z = SectionPos.blockToSectionCoord(pos.getZ()); -+ this.longKey = asLong(this.x, this.z); // Paper - } - - public ChunkPos(long packedPos) { - this.x = (int)packedPos; - this.z = (int)(packedPos >> 32); -+ this.longKey = asLong(this.x, this.z); // Paper - } +@@ -17,7 +_,16 @@ + import net.minecraft.world.level.chunk.status.ChunkStatus; + import org.jspecify.annotations.Nullable; - public static ChunkPos minFromRegion(int chunkX, int chunkZ) { -@@ -83,7 +_,7 @@ +-public record ChunkPos(int x, int z) { ++// Paper start ++public record ChunkPos(int x, int z, long longKey) { ++ public ChunkPos(int x, int z) { ++ this(x, z, pack(x, z)); ++ } ++ ++ public ChunkPos { ++ longKey = pack(x, z); // override if people mess around with longKey ++ } ++ // Paper end + public static final Codec CODEC = Codec.INT_STREAM + .comapFlatMap(input -> Util.fixedSize(input, 2).map(ints -> new ChunkPos(ints[0], ints[1])), pos -> IntStream.of(pos.x, pos.z)) + .stable(); +@@ -72,7 +_,7 @@ } - public long toLong() { -- return asLong(this.x, this.z); + public long pack() { +- return pack(this.x, this.z); + return this.longKey; // Paper } - public static long asLong(int x, int z) { + public static long pack(final int x, final int z) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/ClipContext.java.patch b/paper-server/patches/sources/net/minecraft/world/level/ClipContext.java.patch index b5967d32843e..d7ddcef18266 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/ClipContext.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/ClipContext.java.patch @@ -3,9 +3,9 @@ @@ -26,7 +_,7 @@ private final CollisionContext collisionContext; - public ClipContext(Vec3 from, Vec3 to, ClipContext.Block block, ClipContext.Fluid fluid, Entity entity) { + public ClipContext(final Vec3 from, final Vec3 to, final ClipContext.Block block, final ClipContext.Fluid fluid, final Entity entity) { - this(from, to, block, fluid, CollisionContext.of(entity)); + this(from, to, block, fluid, (entity == null) ? CollisionContext.empty() : CollisionContext.of(entity)); // CraftBukkit } - public ClipContext(Vec3 from, Vec3 to, ClipContext.Block block, ClipContext.Fluid fluid, CollisionContext collisionContext) { + public ClipContext(final Vec3 from, final Vec3 to, final ClipContext.Block block, final ClipContext.Fluid fluid, final CollisionContext collisionContext) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/EmptyBlockAndTintGetter.java.patch b/paper-server/patches/sources/net/minecraft/world/level/EmptyBlockAndTintGetter.java.patch deleted file mode 100644 index 282491c93436..000000000000 --- a/paper-server/patches/sources/net/minecraft/world/level/EmptyBlockAndTintGetter.java.patch +++ /dev/null @@ -1,21 +0,0 @@ ---- a/net/minecraft/world/level/EmptyBlockAndTintGetter.java -+++ b/net/minecraft/world/level/EmptyBlockAndTintGetter.java -@@ -38,6 +_,18 @@ - return Blocks.AIR.defaultBlockState(); - } - -+ // Paper start -+ @Override -+ public @Nullable BlockState getBlockStateIfLoaded(final BlockPos pos) { -+ return null; -+ } -+ -+ @Override -+ public @Nullable FluidState getFluidIfLoaded(final BlockPos pos) { -+ return null; -+ } -+ // Paper end -+ - @Override - public FluidState getFluidState(BlockPos pos) { - return Fluids.EMPTY.defaultFluidState(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/EmptyBlockGetter.java.patch b/paper-server/patches/sources/net/minecraft/world/level/EmptyBlockGetter.java.patch index 690108e9358e..06020d84243e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/EmptyBlockGetter.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/EmptyBlockGetter.java.patch @@ -17,5 +17,5 @@ + // Paper end + @Override - public BlockState getBlockState(BlockPos pos) { + public BlockState getBlockState(final BlockPos pos) { return Blocks.AIR.defaultBlockState(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/EntityGetter.java.patch b/paper-server/patches/sources/net/minecraft/world/level/EntityGetter.java.patch index 1ae5d9e064ff..e5eb23c97b83 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/EntityGetter.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/EntityGetter.java.patch @@ -5,45 +5,41 @@ } + // Paper start - Affects Spawning API -+ default @Nullable Player findNearbyPlayer(Entity entity, double maxDistance, @Nullable Predicate predicate) { -+ return this.getNearestPlayer(entity.getX(), entity.getY(), entity.getZ(), maxDistance, predicate); ++ default @Nullable Player findNearbyPlayer(Entity entity, double range, @Nullable Predicate predicate) { ++ return this.getNearestPlayer(entity.getX(), entity.getY(), entity.getZ(), range, predicate); + } + // Paper end - Affects Spawning API + - default @Nullable Player getNearestPlayer(double x, double y, double z, double distance, @Nullable Predicate predicate) { - double d = -1.0; - Player player = null; -@@ -88,6 +_,28 @@ - return player; + default @Nullable Player getNearestPlayer(final double x, final double y, final double z, final double range, final @Nullable Predicate predicate) { + double best = -1.0; + Player result = null; +@@ -88,6 +_,24 @@ + return result; } + // Paper start -+ default List findNearbyBukkitPlayers(double x, double y, double z, double radius, boolean notSpectator) { -+ return findNearbyBukkitPlayers(x, y, z, radius, notSpectator ? EntitySelector.NO_SPECTATORS : net.minecraft.world.entity.EntitySelector.NO_CREATIVE_OR_SPECTATOR); -+ } -+ -+ default List findNearbyBukkitPlayers(double x, double y, double z, double radius, @Nullable Predicate predicate) { -+ com.google.common.collect.ImmutableList.Builder builder = com.google.common.collect.ImmutableList.builder(); ++ default List findNearbyBukkitPlayers(double x, double y, double z, double range, @Nullable Predicate predicate) { ++ ImmutableList.Builder players = ImmutableList.builder(); + -+ for (Player human : this.players()) { -+ if (predicate == null || predicate.test(human)) { -+ double distanceSquared = human.distanceToSqr(x, y, z); ++ for (Player player : this.players()) { ++ if (predicate == null || predicate.test(player)) { ++ double dist = player.distanceToSqr(x, y, z); + -+ if (radius < 0.0D || distanceSquared < radius * radius) { -+ builder.add(human.getBukkitEntity()); ++ if (range < 0.0 || dist < range * range) { ++ players.add(player.getBukkitEntity()); + } + } + } + -+ return builder.build(); ++ return players.build(); + } + // Paper end + - default @Nullable Player getNearestPlayer(Entity entity, double distance) { - return this.getNearestPlayer(entity.getX(), entity.getY(), entity.getZ(), distance, false); + default @Nullable Player getNearestPlayer(final Entity source, final double maxDist) { + return this.getNearestPlayer(source.getX(), source.getY(), source.getZ(), maxDist, false); } @@ -97,6 +_,25 @@ - return this.getNearestPlayer(x, y, z, distance, predicate); + return this.getNearestPlayer(x, y, z, maxDist, predicate); } + // Paper start - Affects Spawning API @@ -56,7 +52,7 @@ + for (Player player : this.players()) { + if (EntitySelector.PLAYER_AFFECTS_SPAWNING.test(player)) { // combines NO_SPECTATORS and LIVING_ENTITY_STILL_ALIVE with an "affects spawning" check + double playerDist = player.distanceToSqr(x, y, z); -+ if (range < 0.0D || playerDist < range * range) { ++ if (range < 0.0 || playerDist < range * range) { + return true; + } + } @@ -65,7 +61,7 @@ + } + // Paper end - Affects Spawning API + - default boolean hasNearbyAlivePlayer(double x, double y, double z, double distance) { + default boolean hasNearbyAlivePlayer(final double x, final double y, final double z, final double range) { for (Player player : this.players()) { if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player)) { @@ -120,4 +_,11 @@ diff --git a/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch b/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch index 6311623ded6a..c298ce198584 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java -@@ -86,6 +_,16 @@ +@@ -91,6 +_,16 @@ import org.apache.commons.lang3.mutable.MutableBoolean; import org.jspecify.annotations.Nullable; @@ -17,7 +17,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public static final Codec> RESOURCE_KEY_CODEC = ResourceKey.codec(Registries.DIMENSION); public static final ResourceKey OVERWORLD = ResourceKey.create(Registries.DIMENSION, Identifier.withDefaultNamespace("overworld")); -@@ -127,6 +_,56 @@ +@@ -132,6 +_,55 @@ private final PalettedContainerFactory palettedContainerFactory; private long subTickCount; @@ -42,7 +42,6 @@ + // Paper end - add paper world config + + public static @Nullable BlockPos lastPhysicsProblem; // Spigot -+ private int tileTickPosition; + public final Map explosionDensityCache = new java.util.HashMap<>(); // Paper - Optimize explosions + public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Faster redstone torch rapid clock removal; Move from Map in BlockRedstoneTorch to here + @@ -72,21 +71,20 @@ + public abstract ResourceKey getTypeKey(); + protected Level( - WritableLevelData levelData, - ResourceKey dimension, -@@ -135,8 +_,24 @@ - boolean isClientSide, - boolean isDebug, - long biomeZoomSeed, -- int maxChainedNeighborUpdates -+ int maxChainedNeighborUpdates, + final WritableLevelData levelData, + final ResourceKey dimension, +@@ -141,7 +_,24 @@ + final boolean isDebug, + final long biomeZoomSeed, + final int maxChainedNeighborUpdates ++ , String bukkitName, // Paper + org.bukkit.generator.@Nullable ChunkGenerator generator, // Paper + org.bukkit.generator.@Nullable BiomeProvider biomeProvider, // Paper + org.bukkit.World.Environment environment, // Paper + java.util.function.Function paperWorldConfigCreator // Paper - create paper world config ) { -+ this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot ++ this.spigotConfig = new org.spigotmc.SpigotWorldConfig(bukkitName); // Spigot + this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config + this.generator = generator; + this.world = new CraftWorld((ServerLevel) this, generator, biomeProvider, environment); @@ -100,7 +98,7 @@ this.levelData = levelData; this.dimensionTypeRegistration = dimensionTypeRegistration; this.dimension = dimension; -@@ -150,6 +_,46 @@ +@@ -155,6 +_,46 @@ this.damageSources = new DamageSources(registryAccess); } @@ -147,7 +145,7 @@ @Override public boolean isClientSide() { return this.isClientSide; -@@ -160,8 +_,15 @@ +@@ -165,8 +_,15 @@ return null; } @@ -158,35 +156,35 @@ + } + // Paper end + - public boolean isInWorldBounds(BlockPos pos) { -- return !this.isOutsideBuildHeight(pos) && isInWorldBoundsHorizontal(pos); + public boolean isInWorldBounds(final BlockPos pos) { +- return this.isInsideBuildHeight(pos) && isInWorldBoundsHorizontal(pos); + return pos.isInsideBuildHeightAndWorldBoundsHorizontal(this); // Paper - Perf: Optimize isInWorldBounds } - public boolean isInValidBounds(BlockPos blockPos) { -@@ -173,7 +_,7 @@ + public boolean isInValidBounds(final BlockPos pos) { +@@ -178,7 +_,7 @@ } - private static boolean isInWorldBoundsHorizontal(BlockPos pos) { + private static boolean isInWorldBoundsHorizontal(final BlockPos pos) { - return pos.getX() >= -30000000 && pos.getZ() >= -30000000 && pos.getX() < 30000000 && pos.getZ() < 30000000; + return pos.getX() >= -30000000 && pos.getZ() >= -30000000 && pos.getX() < 30000000 && pos.getZ() < 30000000; // Paper - Diff on change warnUnsafeChunk() and isInsideBuildHeightAndWorldBoundsHorizontal } - private static boolean isInValidBoundsHorizontal(BlockPos blockPos) { -@@ -186,14 +_,79 @@ + private static boolean isInValidBoundsHorizontal(final BlockPos pos) { +@@ -191,14 +_,79 @@ return y < -20000000 || y >= 20000000; } -- public LevelChunk getChunkAt(BlockPos pos) { -+ public final LevelChunk getChunkAt(BlockPos pos) { // Paper - help inline +- public LevelChunk getChunkAt(final BlockPos pos) { ++ public final LevelChunk getChunkAt(final BlockPos pos) { // Paper - help inline return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); } @Override -- public LevelChunk getChunk(int chunkX, int chunkZ) { +- public LevelChunk getChunk(final int chunkX, final int chunkZ) { - return (LevelChunk)this.getChunk(chunkX, chunkZ, ChunkStatus.FULL); - } -+ public final LevelChunk getChunk(int chunkX, int chunkZ) { // Paper - final to help inline ++ public final LevelChunk getChunk(final int chunkX, final int chunkZ) { // Paper - final to help inline + // Paper start - Perf: make sure loaded chunks get the inlined variant of this function + net.minecraft.server.level.ServerChunkCache cps = ((ServerLevel)this).getChunkSource(); + LevelChunk ifLoaded = cps.getChunkAtIfLoadedImmediately(chunkX, chunkZ); @@ -234,11 +232,11 @@ + + @Override + public final boolean hasChunkAt(BlockPos pos) { -+ return getChunkIfLoaded(pos.getX() >> 4, pos.getZ() >> 4) != null; // Paper - Perf: Optimize Level.hasChunkAt(BlockPosition)Z ++ return this.getChunkIfLoaded(pos.getX() >> 4, pos.getZ() >> 4) != null; // Paper - Perf: Optimize LevelReader.hasChunkAt(BlockPos)Z + } + + public final boolean isLoadedAndInBounds(BlockPos pos) { // Paper - final for inline -+ return getWorldBorder().isWithinBounds(pos) && getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4) != null; ++ return this.getWorldBorder().isWithinBounds(pos) && this.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4) != null; + } + + public @Nullable LevelChunk getChunkIfLoaded(int x, int z) { // Overridden in ServerLevel for ABI compat which has final @@ -251,16 +249,16 @@ + + // reduces need to do isLoaded before getType + public final @Nullable BlockState getBlockStateIfLoadedAndInBounds(BlockPos pos) { -+ return getWorldBorder().isWithinBounds(pos) ? getBlockStateIfLoaded(pos) : null; ++ return this.getWorldBorder().isWithinBounds(pos) ? this.getBlockStateIfLoaded(pos) : null; + } + // Paper end @Override - public @Nullable ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk) { -@@ -212,6 +_,22 @@ + public @Nullable ChunkAccess getChunk(final int chunkX, final int chunkZ, final ChunkStatus status, final boolean loadOrGenerate) { +@@ -217,6 +_,22 @@ @Override - public boolean setBlock(BlockPos pos, BlockState state, @Block.UpdateFlags int flags, int recursionLeft) { + public boolean setBlock(final BlockPos pos, final BlockState blockState, @Block.UpdateFlags final int updateFlags, final int updateLimit) { + // CraftBukkit start - tree generation + if (this.captureTreeGeneration) { + // Paper start - Protect Bedrock and End Portal/Frames from being destroyed @@ -269,21 +267,21 @@ + // Paper end - Protect Bedrock and End Portal/Frames from being destroyed + CraftBlockState blockstate = this.capturedBlockStates.get(pos); + if (blockstate == null) { -+ blockstate = org.bukkit.craftbukkit.block.CapturedBlockState.getTreeBlockState(this, pos, flags); ++ blockstate = org.bukkit.craftbukkit.block.CapturedBlockState.getTreeBlockState(this, pos, updateFlags); + this.capturedBlockStates.put(pos.immutable(), blockstate); + } -+ blockstate.setData(state); -+ blockstate.setFlags(flags); ++ blockstate.setBlock(blockState); ++ blockstate.setFlags(updateFlags); + return true; + } + // CraftBukkit end if (!this.isInValidBounds(pos)) { return false; } else if (!this.isClientSide() && this.isDebug()) { -@@ -219,11 +_,31 @@ +@@ -224,11 +_,31 @@ } else { - LevelChunk chunkAt = this.getChunkAt(pos); - Block block = state.getBlock(); + LevelChunk chunk = this.getChunkAt(pos); + Block block = blockState.getBlock(); + // CraftBukkit start - capture blockstates + boolean captured = false; + if (this.captureBlockStates) { @@ -295,11 +293,11 @@ + } else { + snapshot = this.capturedBlockStates.get(pos); + } -+ snapshot.setFlags(flags); // Paper - always set the flag of the most recent call to mitigate issues with multiple update at the same pos with different flags ++ snapshot.setFlags(updateFlags); // Paper - always set the flag of the most recent call to mitigate issues with multiple update at the same pos with different flags + } - BlockState blockState = chunkAt.setBlockState(pos, state, flags); -+ // CraftBukkit end - if (blockState == null) { ++ // CraftBukkit end - capture blockstates + BlockState oldState = chunk.setBlockState(pos, blockState, updateFlags); + if (oldState == null) { + // CraftBukkit start - remove blockstate if failed (or the same) + if (this.captureBlockStates && captured) { + this.capturedBlockStates.remove(pos); @@ -307,23 +305,23 @@ + // CraftBukkit end return false; } else { - BlockState blockState1 = this.getBlockState(pos); -+ /* - if (blockState1 == state) { - if (blockState != blockState1) { - this.setBlocksDirty(pos, blockState, blockState1); -@@ -251,12 +_,68 @@ + BlockState newState = this.getBlockState(pos); ++ /* // CraftBukkit + if (newState == blockState) { + if (oldState != newState) { + this.setBlocksDirty(pos, oldState, newState); +@@ -256,12 +_,69 @@ - this.updatePOIOnBlockStateChange(pos, blockState, blockState1); + this.updatePOIOnBlockStateChange(pos, oldState, newState); } -+ */ ++ */ // CraftBukkit + + // CraftBukkit start + if (!this.captureBlockStates) { // Don't notify clients or update physics while capturing blockstates + // Modularize client and physic updates + // Spigot start + try { -+ this.notifyAndUpdatePhysics(pos, chunkAt, blockState, state, blockState1, flags, recursionLeft); ++ this.notifyAndUpdatePhysics(pos, chunk, oldState, blockState, newState, updateFlags, updateLimit); + } catch (StackOverflowError ex) { + Level.lastPhysicsProblem = pos.immutable(); + } @@ -364,7 +362,7 @@ + blockState.updateIndirectNeighbourShapes(this, pos, i, recursionLeft - 1); // Don't call an event for the old block to limit event spam + boolean cancelledUpdates = false; // Paper - Fix block place logic + if (((ServerLevel)this).hasPhysicsEvent) { // Paper - BlockPhysicsEvent -+ org.bukkit.event.block.BlockPhysicsEvent event = new org.bukkit.event.block.BlockPhysicsEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this, pos), CraftBlockData.fromData(state)); ++ org.bukkit.event.block.BlockPhysicsEvent event = new org.bukkit.event.block.BlockPhysicsEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this, pos), state.asBlockData()); + cancelledUpdates = !event.callEvent(); // Paper - Fix block place logic + } + // CraftBukkit end @@ -378,10 +376,18 @@ + } + } + // CraftBukkit end - public void updatePOIOnBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) { ++ + public void updatePOIOnBlockStateChange(final BlockPos pos, final BlockState oldState, final BlockState newState) { } -@@ -273,13 +_,31 @@ +@@ -272,19 +_,37 @@ + } + + @Override +- public boolean destroyBlock(final BlockPos pos, final boolean dropResources, final @Nullable Entity breaker, final int updateLimit) { ++ public boolean destroyBlock(final BlockPos pos, boolean dropResources, final @Nullable Entity breaker, final int updateLimit) { // Paper - make dropResources non-final + BlockState blockState = this.getBlockState(pos); + if (blockState.isAir()) { return false; } else { FluidState fluidState = this.getFluidState(pos); @@ -394,13 +400,13 @@ + BlockState effectType = blockState; + int xp = blockState.getBlock().getExpDrop(blockState, (ServerLevel) this, pos, ItemStack.EMPTY, true); + if (com.destroystokyo.paper.event.block.BlockDestroyEvent.getHandlerList().getRegisteredListeners().length > 0) { -+ com.destroystokyo.paper.event.block.BlockDestroyEvent event = new com.destroystokyo.paper.event.block.BlockDestroyEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this, pos), fluidState.createLegacyBlock().createCraftBlockData(), effectType.createCraftBlockData(), xp, dropBlock); ++ com.destroystokyo.paper.event.block.BlockDestroyEvent event = new com.destroystokyo.paper.event.block.BlockDestroyEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this, pos), fluidState.createLegacyBlock().asBlockData(), effectType.asBlockData(), xp, dropResources); + if (!event.callEvent()) { + return false; + } + effectType = ((CraftBlockData) event.getEffectBlock()).getState(); + playEffect = event.playEffect(); -+ dropBlock = event.willDrop(); ++ dropResources = event.willDrop(); + xp = event.getExpToDrop(); + } + // Paper end - BlockDestroyEvent @@ -408,18 +414,18 @@ + this.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, pos, Block.getId(effectType)); // Paper - BlockDestroyEvent } - if (dropBlock) { + if (dropResources) { BlockEntity blockEntity = blockState.hasBlockEntity() ? this.getBlockEntity(pos) : null; -- Block.dropResources(blockState, this, pos, blockEntity, entity, ItemStack.EMPTY); -+ Block.dropResources(blockState, this, pos, blockEntity, entity, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping -+ blockState.getBlock().popExperience((ServerLevel) this, pos, xp, entity); // Paper - Properly handle xp dropping; custom amount +- Block.dropResources(blockState, this, pos, blockEntity, breaker, ItemStack.EMPTY); ++ Block.dropResources(blockState, this, pos, blockEntity, breaker, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping ++ blockState.getBlock().popExperience((ServerLevel) this, pos, xp, breaker); // Paper - Properly handle xp dropping; custom amount } - boolean flag = this.setBlock(pos, fluidState.createLegacyBlock(), Block.UPDATE_ALL, recursionLeft); -@@ -345,10 +_,18 @@ + boolean destroyed = this.setBlock(pos, fluidState.createLegacyBlock(), Block.UPDATE_ALL, updateLimit); +@@ -359,10 +_,18 @@ @Override - public BlockState getBlockState(BlockPos pos) { + public BlockState getBlockState(final BlockPos pos) { + // CraftBukkit start - tree generation + if (this.captureTreeGeneration) { + CraftBlockState previous = this.capturedBlockStates.get(pos); // Paper @@ -436,42 +442,43 @@ return chunk.getBlockState(pos); } } -@@ -447,31 +_,47 @@ +@@ -533,31 +_,48 @@ this.pendingBlockEntityTickers.clear(); } - Iterator iterator = this.blockEntityTickers.iterator(); -+ // Spigot start - boolean runsNormally = this.tickRateManager().runsNormally(); ++ // Paper - Fix MC-117075 use removeAll - remove iterator in favour of indexed for loop, ensuring compile error if something uses iter incorrectly + boolean tickBlockEntities = this.tickRateManager().runsNormally(); - while (iterator.hasNext()) { -- TickingBlockEntity tickingBlockEntity = iterator.next(); -+ var toRemove = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet(); // Paper - Fix MC-117075; use removeAll -+ toRemove.add(null); // Paper - Fix MC-117075 -+ for (this.tileTickPosition = 0; this.tileTickPosition < this.blockEntityTickers.size(); this.tileTickPosition++) { // Paper - Disable tick limiters -+ TickingBlockEntity tickingBlockEntity = this.blockEntityTickers.get(this.tileTickPosition); -+ // Spigot end - if (tickingBlockEntity.isRemoved()) { +- TickingBlockEntity ticker = iterator.next(); ++ // Paper start - Fix MC-117075 use removeAll ++ final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<@Nullable TickingBlockEntity> toRemove = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); ++ toRemove.add(null); ++ for (int tickerIndex = 0; tickerIndex < this.blockEntityTickers.size(); tickerIndex++) { ++ final TickingBlockEntity ticker = this.blockEntityTickers.get(tickerIndex); ++ // Paper end - Fix MC-117075 use removeAll + if (ticker.isRemoved()) { - iterator.remove(); -+ toRemove.add(tickingBlockEntity); // Paper - Fix MC-117075; use removeAll - } else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { - tickingBlockEntity.tick(); ++ toRemove.add(ticker); // Paper - Fix MC-117075 use removeAll + } else if (tickBlockEntities && this.shouldTickBlocksAt(ticker.getPos())) { + ticker.tick(); } } -+ this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075 ++ this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075 use removeAll this.tickingBlockEntities = false; + this.spigotConfig.currentPrimedTnt = 0; // Spigot } - public void guardEntityTick(Consumer action, T entity) { + public void guardEntityTick(final Consumer tick, final T entity) { try { - action.accept(entity); + tick.accept(entity); } catch (Throwable var6) { -- CrashReport crashReport = CrashReport.forThrowable(var6, "Ticking entity"); -- CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being ticked"); -- entity.fillCrashReportCategory(crashReportCategory); -- throw new ReportedException(crashReport); +- CrashReport report = CrashReport.forThrowable(var6, "Ticking entity"); +- CrashReportCategory category = report.addCategory("Entity being ticked"); +- entity.fillCrashReportCategory(category); +- throw new ReportedException(report); + // Paper start - Prevent block entity and entity crashes + final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); + MinecraftServer.LOGGER.error(msg, var6); @@ -490,12 +497,12 @@ + } + // Paper end - Option to prevent armor stands from doing entity lookups - public boolean shouldTickDeath(Entity entity) { + public boolean shouldTickDeath(final Entity entity) { return true; -@@ -595,6 +_,12 @@ +@@ -691,6 +_,12 @@ @Override - public @Nullable BlockEntity getBlockEntity(BlockPos pos) { + public @Nullable BlockEntity getBlockEntity(final BlockPos pos) { + // Paper start - Perf: Optimize capturedTileEntities lookup + net.minecraft.world.level.block.entity.BlockEntity blockEntity; + if (!this.capturedTileEntities.isEmpty() && (blockEntity = this.capturedTileEntities.get(pos)) != null) { @@ -505,20 +512,20 @@ if (!this.isInValidBounds(pos)) { return null; } else { -@@ -607,6 +_,12 @@ - public void setBlockEntity(BlockEntity blockEntity) { - BlockPos blockPos = blockEntity.getBlockPos(); - if (this.isInValidBounds(blockPos)) { +@@ -703,6 +_,12 @@ + public void setBlockEntity(final BlockEntity blockEntity) { + BlockPos pos = blockEntity.getBlockPos(); + if (this.isInValidBounds(pos)) { + // CraftBukkit start + if (this.captureBlockStates) { -+ this.capturedTileEntities.put(blockPos.immutable(), blockEntity); ++ this.capturedTileEntities.put(pos.immutable(), blockEntity); + return; + } + // CraftBukkit end - this.getChunkAt(blockPos).addAndRegisterBlockEntity(blockEntity); + this.getChunkAt(pos).addAndRegisterBlockEntity(blockEntity); } } -@@ -1011,7 +_,8 @@ +@@ -1110,7 +_,8 @@ BLOCK("block"), MOB("mob"), TNT("tnt"), @@ -528,7 +535,7 @@ public static final Codec CODEC = StringRepresentable.fromEnum(Level.ExplosionInteraction::values); private final String id; -@@ -1025,4 +_,16 @@ +@@ -1124,4 +_,16 @@ return this.id; } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/LevelAccessor.java.patch b/paper-server/patches/sources/net/minecraft/world/level/LevelAccessor.java.patch index 8f8e291a66b5..6ca294681955 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/LevelAccessor.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/LevelAccessor.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/LevelAccessor.java +++ b/net/minecraft/world/level/LevelAccessor.java -@@ -97,4 +_,6 @@ - default void gameEvent(ResourceKey gameEvent, BlockPos pos, GameEvent.Context context) { +@@ -102,4 +_,6 @@ + default void gameEvent(final ResourceKey gameEvent, final BlockPos pos, final GameEvent.Context context) { this.gameEvent(this.registryAccess().lookupOrThrow(Registries.GAME_EVENT).getOrThrow(gameEvent), pos, context); } + diff --git a/paper-server/patches/sources/net/minecraft/world/level/LevelReader.java.patch b/paper-server/patches/sources/net/minecraft/world/level/LevelReader.java.patch index cdaaa11078c6..3af49d08cfa6 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/LevelReader.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/LevelReader.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/LevelReader.java +++ b/net/minecraft/world/level/LevelReader.java @@ -26,6 +_,9 @@ - public interface LevelReader extends BlockAndTintGetter, CollisionGetter, SignalGetter, BiomeManager.NoiseBiomeSource { - @Nullable ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk); + public interface LevelReader extends BlockAndLightGetter, CollisionGetter, SignalGetter, BiomeManager.NoiseBiomeSource { + @Nullable ChunkAccess getChunk(final int chunkX, final int chunkZ, final ChunkStatus targetStatus, final boolean loadOrGenerate); + @Nullable ChunkAccess getChunkIfLoadedImmediately(int x, int z); // Paper - ifLoaded api (we need this since current impl blocks if the chunk is loading) -+ @Nullable default ChunkAccess getChunkIfLoadedImmediately(BlockPos pos) { return this.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4);} ++ default @Nullable ChunkAccess getChunkIfLoadedImmediately(BlockPos pos) { return this.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4); } + @Deprecated boolean hasChunk(int chunkX, int chunkZ); diff --git a/paper-server/patches/sources/net/minecraft/world/level/LevelSettings.java.patch b/paper-server/patches/sources/net/minecraft/world/level/LevelSettings.java.patch new file mode 100644 index 000000000000..70bf9a58d5ab --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/LevelSettings.java.patch @@ -0,0 +1,19 @@ +--- a/net/minecraft/world/level/LevelSettings.java ++++ b/net/minecraft/world/level/LevelSettings.java +@@ -51,6 +_,16 @@ + return new LevelSettings(this.levelName, this.gameType, this.difficultySettings, this.allowCommands, this.dataConfiguration); + } + ++ // Paper start ++ public LevelSettings withLevelName(String name) { ++ return new LevelSettings(name, this.gameType, this.difficultySettings, this.allowCommands, this.dataConfiguration); ++ } ++ ++ public LevelSettings withHardcore(boolean hardcore) { ++ return new LevelSettings(this.levelName, this.gameType, new LevelSettings.DifficultySettings(this.difficultySettings.difficulty(), hardcore, this.difficultySettings.locked()), this.allowCommands, this.dataConfiguration); ++ } ++ // Paper end ++ + public record DifficultySettings(Difficulty difficulty, boolean hardcore, boolean locked) { + public static final LevelSettings.DifficultySettings DEFAULT = new LevelSettings.DifficultySettings(Difficulty.NORMAL, false, false); + public static final Codec CODEC = RecordCodecBuilder.create( diff --git a/paper-server/patches/sources/net/minecraft/world/level/LevelWriter.java.patch b/paper-server/patches/sources/net/minecraft/world/level/LevelWriter.java.patch index a132dab9b9ad..cc4fdad99784 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/LevelWriter.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/LevelWriter.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/LevelWriter.java +++ b/net/minecraft/world/level/LevelWriter.java @@ -28,4 +_,10 @@ - default boolean addFreshEntity(Entity entity) { + default boolean addFreshEntity(final Entity entity) { return false; } + diff --git a/paper-server/patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch b/paper-server/patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch index fba73eea2467..d9abff23dc29 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java -@@ -76,6 +_,13 @@ +@@ -77,6 +_,13 @@ if (!(entity instanceof Mob mob && (mob.isPersistenceRequired() || mob.requiresCustomPersistence()))) { MobCategory category = entity.getType().getCategory(); if (category != MobCategory.MISC) { @@ -11,21 +11,21 @@ + continue; + } + // Paper end - Only count natural spawns - BlockPos blockPos = entity.blockPosition(); - chunkGetter.query(ChunkPos.asLong(blockPos), chunk -> { - MobSpawnSettings.MobSpawnCost mobSpawnCost = getRoughBiome(blockPos, chunk).getMobSettings().getMobSpawnCost(entity.getType()); -@@ -100,17 +_,34 @@ + BlockPos pos = entity.blockPosition(); + chunkGetter.query(ChunkPos.pack(pos), chunk -> { + MobSpawnSettings.MobSpawnCost mobSpawnCost = getRoughBiome(pos, chunk).getMobSettings().getMobSpawnCost(entity.getType()); +@@ -101,17 +_,34 @@ return chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value(); } + // CraftBukkit start - add server public static List getFilteredSpawningCategories( -- NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnEnemies, boolean spawnPassives -+ NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnEnemies, boolean spawnPassives, ServerLevel level +- final NaturalSpawner.SpawnState state, final boolean spawnFriendlies, final boolean spawnEnemies, final boolean spawnPersistent ++ final NaturalSpawner.SpawnState state, final boolean spawnFriendlies, final boolean spawnEnemies, final boolean spawnPersistent, final ServerLevel level ) { + net.minecraft.world.level.storage.LevelData worlddata = level.getLevelData(); // CraftBukkit - Other mob type spawn tick rate + // CraftBukkit end - List list = new ArrayList<>(SPAWNING_CATEGORIES.length); + List spawningCategories = new ArrayList<>(SPAWNING_CATEGORIES.length); for (MobCategory mobCategory : SPAWNING_CATEGORIES) { + // CraftBukkit start - Use per-world spawn limits @@ -43,16 +43,16 @@ + if ((spawnFriendlies || !mobCategory.isFriendly()) && (spawnEnemies || mobCategory.isFriendly()) - && (spawnPassives || !mobCategory.isPersistent()) -- && spawnState.canSpawnForCategoryGlobal(mobCategory)) { -+ && spawnState.canSpawnForCategoryGlobal(mobCategory, limit)) { // Paper - Optional per player mob spawns; remove global check, check later during the local one - list.add(mobCategory); + && (spawnPersistent || !mobCategory.isPersistent()) +- && state.canSpawnForCategoryGlobal(mobCategory)) { ++ && state.canSpawnForCategoryGlobal(mobCategory, limit)) { // Paper - Optional per player mob spawns; remove global check, check later during the local one + spawningCategories.add(mobCategory); + // CraftBukkit end } } -@@ -130,6 +_,16 @@ - profilerFiller.pop(); +@@ -133,6 +_,16 @@ + profiler.pop(); } + // Paper start - Add mobcaps commands @@ -66,64 +66,64 @@ + // Paper end - Add mobcaps commands + public static void spawnCategoryForChunk( - MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback - ) { -@@ -155,8 +_,8 @@ + final MobCategory mobCategory, + final ServerLevel level, +@@ -162,8 +_,8 @@ StructureManager structureManager = level.structureManager(); ChunkGenerator generator = level.getChunkSource().getGenerator(); - int y = pos.getY(); -- BlockState blockState = chunk.getBlockState(pos); -- if (!blockState.isRedstoneConductor(chunk, pos)) { -+ BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn -+ if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn - BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); - int i = 0; + int yStart = start.getY(); +- BlockState state = chunk.getBlockState(start); +- if (!state.isRedstoneConductor(chunk, start)) { ++ BlockState state = level.getBlockStateIfLoadedAndInBounds(start); // Paper - don't load chunks for mob spawn ++ if (state != null && !state.isRedstoneConductor(chunk, start)) { // Paper - don't load chunks for mob spawn + BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); + int clusterSize = 0; -@@ -178,7 +_,7 @@ - Player nearestPlayer = level.getNearestPlayer(d, y, d1, -1.0, false); +@@ -185,7 +_,7 @@ + Player nearestPlayer = level.getNearestPlayer(xx, yStart, zz, -1.0, false); if (nearestPlayer != null) { - double d2 = nearestPlayer.distanceToSqr(d, y, d1); -- if (isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { -+ if (level.isLoadedAndInBounds(mutableBlockPos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn - if (spawnerData == null) { - Optional randomSpawnMobAt = getRandomSpawnMobAt( - level, structureManager, generator, category, level.random, mutableBlockPos -@@ -191,7 +_,13 @@ - ceil = spawnerData.minCount() + level.random.nextInt(1 + spawnerData.maxCount() - spawnerData.minCount()); + double nearestPlayerDistanceSqr = nearestPlayer.distanceToSqr(xx, yStart, zz); +- if (isRightDistanceToPlayerAndSpawnPoint(level, chunk, pos, nearestPlayerDistanceSqr)) { ++ if (level.isLoadedAndInBounds(pos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, pos, nearestPlayerDistanceSqr)) { // Paper - don't load chunks for mob spawn + if (currentSpawnData == null) { + Optional nextSpawnData = getRandomSpawnMobAt( + level, structureManager, generator, mobCategory, level.random, pos +@@ -198,7 +_,13 @@ + max = currentSpawnData.minCount() + level.random.nextInt(1 + currentSpawnData.maxCount() - currentSpawnData.minCount()); } -- if (isValidSpawnPostitionForType(level, category, structureManager, generator, spawnerData, mutableBlockPos, d2) +- if (isValidSpawnPostitionForType(level, mobCategory, structureManager, generator, currentSpawnData, pos, nearestPlayerDistanceSqr) + // Paper start - PreCreatureSpawnEvent -+ PreSpawnStatus doSpawning = isValidSpawnPostitionForType(level, category, structureManager, generator, spawnerData, mutableBlockPos, d2); ++ PreSpawnStatus doSpawning = isValidSpawnPostitionForType(level, mobCategory, structureManager, generator, currentSpawnData, pos, nearestPlayerDistanceSqr); + if (doSpawning == PreSpawnStatus.ABORT) { + return; + } + if (doSpawning == PreSpawnStatus.SUCCESS + // Paper end - PreCreatureSpawnEvent - && filter.test(spawnerData.type(), mutableBlockPos, chunk)) { - Mob mobForSpawn = getMobForSpawn(level, spawnerData.type()); - if (mobForSpawn == null) { -@@ -203,10 +_,15 @@ - spawnGroupData = mobForSpawn.finalizeSpawn( - level, level.getCurrentDifficultyAt(mobForSpawn.blockPosition()), EntitySpawnReason.NATURAL, spawnGroupData + && extraTest.test(currentSpawnData.type(), pos, chunk)) { + Mob mob = getMobForSpawn(level, currentSpawnData.type()); + if (mob == null) { +@@ -210,10 +_,15 @@ + groupData = mob.finalizeSpawn( + level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.NATURAL, groupData ); -- i++; -- i3++; -- level.addFreshEntityWithPassengers(mobForSpawn); -- callback.run(mobForSpawn, chunk); +- clusterSize++; +- groupSize++; +- level.addFreshEntityWithPassengers(mob); +- spawnCallback.run(mob, chunk); + // CraftBukkit start + // SPIGOT-7045: Give ocelot babies back their special spawn reason. Note: This is the only modification required as ocelots count as monsters which means they only spawn during normal chunk ticking and do not spawn during chunk generation as starter mobs. -+ level.addFreshEntityWithPassengers(mobForSpawn, (mobForSpawn instanceof net.minecraft.world.entity.animal.feline.Ocelot && !((org.bukkit.entity.Ageable) mobForSpawn.getBukkitEntity()).isAdult()) ? org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.OCELOT_BABY : org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); -+ if (!mobForSpawn.isRemoved()) { -+ ++i; -+ ++i3; -+ callback.run(mobForSpawn, chunk); ++ level.addFreshEntityWithPassengers(mob, (mob instanceof net.minecraft.world.entity.animal.feline.Ocelot && !((org.bukkit.entity.Ageable) mob.getBukkitEntity()).isAdult()) ? org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.OCELOT_BABY : org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); ++ if (!mob.isRemoved()) { ++ clusterSize++; ++ groupSize++; ++ spawnCallback.run(mob, chunk); + } + // CraftBukkit end - if (i >= mobForSpawn.getMaxSpawnClusterSize()) { + if (clusterSize >= mob.getMaxSpawnClusterSize()) { return; } -@@ -238,7 +_,15 @@ +@@ -247,7 +_,15 @@ } } @@ -137,18 +137,19 @@ + } + private static PreSpawnStatus isValidSpawnPostitionForType( + // Paper end - PreCreatureSpawnEvent - ServerLevel level, - MobCategory category, - StructureManager structureManager, -@@ -248,7 +_,19 @@ - double distance + final ServerLevel level, + final MobCategory mobCategory, + final StructureManager structureManager, +@@ -256,14 +_,27 @@ + final BlockPos.MutableBlockPos pos, + final double nearestPlayerDistanceSqr ) { - EntityType entityType = data.type(); -- return entityType.getCategory() != MobCategory.MISC + // Paper start - PreCreatureSpawnEvent + EntityType type = currentSpawnData.type(); +- return type.getCategory() != MobCategory.MISC + com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent( + org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level), -+ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(entityType), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL ++ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(type), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL + ); + if (!event.callEvent()) { + if (event.shouldAbortSpawn()) { @@ -156,28 +157,27 @@ + } + return PreSpawnStatus.CANCELLED; + } -+ final boolean success = entityType.getCategory() != MobCategory.MISC ++ final boolean success = type.getCategory() != MobCategory.MISC + // Paper end - PreCreatureSpawnEvent - && ( - entityType.canSpawnFarFromPlayer() - || !(distance > entityType.getCategory().getDespawnDistance() * entityType.getCategory().getDespawnDistance()) -@@ -258,6 +_,7 @@ - && SpawnPlacements.isSpawnPositionOk(entityType, level, pos) - && SpawnPlacements.checkSpawnRules(entityType, level, EntitySpawnReason.NATURAL, pos, level.random) - && level.noCollision(entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5)); + && (type.canSpawnFarFromPlayer() || !(nearestPlayerDistanceSqr > type.getCategory().getDespawnDistance() * type.getCategory().getDespawnDistance())) + && type.canSummon() + && canSpawnMobAt(level, structureManager, generator, mobCategory, currentSpawnData, pos) + && SpawnPlacements.isSpawnPositionOk(type, level, pos) + && SpawnPlacements.checkSpawnRules(type, level, EntitySpawnReason.NATURAL, pos, level.random) + && level.noCollision(type.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5)); + return success ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL; // Paper - PreCreatureSpawnEvent } - private static @Nullable Mob getMobForSpawn(ServerLevel level, EntityType entityType) { -@@ -269,6 +_,7 @@ - LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(entityType)); + private static @Nullable Mob getMobForSpawn(final ServerLevel level, final EntityType type) { +@@ -275,6 +_,7 @@ + LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(type)); } catch (Exception var4) { LOGGER.warn("Failed to create mob", (Throwable)var4); + com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(var4); // Paper - ServerExceptionEvent } return null; -@@ -375,6 +_,7 @@ +@@ -402,6 +_,7 @@ entity = spawnerData.type().create(level.getLevel(), EntitySpawnReason.NATURAL); } catch (Exception var27) { LOGGER.warn("Failed to create mob", (Throwable)var27); @@ -185,25 +185,25 @@ continue; } -@@ -389,7 +_,7 @@ - spawnGroupData = mob.finalizeSpawn( - level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.CHUNK_GENERATION, spawnGroupData +@@ -416,7 +_,7 @@ + groupSpawnData = mob.finalizeSpawn( + level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.CHUNK_GENERATION, groupSpawnData ); - level.addFreshEntityWithPassengers(mob); + level.addFreshEntityWithPassengers(mob, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - flag = true; + success = true; } } -@@ -507,8 +_,10 @@ +@@ -534,8 +_,10 @@ return this.unmodifiableMobCategoryCounts; } -- boolean canSpawnForCategoryGlobal(MobCategory category) { -- int i = category.getMaxInstancesPerChunk() * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER; +- private boolean canSpawnForCategoryGlobal(final MobCategory mobCategory) { +- int maxMobCount = mobCategory.getMaxInstancesPerChunk() * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER; + // CraftBukkit start -+ boolean canSpawnForCategoryGlobal(MobCategory category, int limit) { -+ int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER; ++ private boolean canSpawnForCategoryGlobal(final MobCategory mobCategory, final int limit) { ++ int maxMobCount = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER; + // CraftBukkit end - return this.mobCategoryCounts.getInt(category) < i; + return this.mobCategoryCounts.getInt(mobCategory) < maxMobCount; } diff --git a/paper-server/patches/sources/net/minecraft/world/level/PathNavigationRegion.java.patch b/paper-server/patches/sources/net/minecraft/world/level/PathNavigationRegion.java.patch index 8d1de8e39400..fd876791f3b9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/PathNavigationRegion.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/PathNavigationRegion.java.patch @@ -1,15 +1,15 @@ --- a/net/minecraft/world/level/PathNavigationRegion.java +++ b/net/minecraft/world/level/PathNavigationRegion.java @@ -66,13 +_,41 @@ - private ChunkAccess getChunk(int x, int z) { - int i = x - this.centerX; - int i1 = z - this.centerZ; -- if (i >= 0 && i < this.chunks.length && i1 >= 0 && i1 < this.chunks[i].length) { -+ if (i >= 0 && i < this.chunks.length && i1 >= 0 && i1 < this.chunks[i].length) { // Paper - if this changes, update getChunkIfLoaded below - ChunkAccess chunkAccess = this.chunks[i][i1]; - return (ChunkAccess)(chunkAccess != null ? chunkAccess : new EmptyLevelChunk(this.level, new ChunkPos(x, z), this.plains.get())); + private ChunkAccess getChunk(final int chunkX, final int chunkZ) { + int xc = chunkX - this.centerX; + int zc = chunkZ - this.centerZ; +- if (xc >= 0 && xc < this.chunks.length && zc >= 0 && zc < this.chunks[xc].length) { ++ if (xc >= 0 && xc < this.chunks.length && zc >= 0 && zc < this.chunks[xc].length) { // Paper - if this changes, update getChunkIfLoaded below + ChunkAccess chunk = this.chunks[xc][zc]; + return (ChunkAccess)(chunk != null ? chunk : new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ), this.plains.get())); } else { - return new EmptyLevelChunk(this.level, new ChunkPos(x, z), this.plains.get()); + return new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ), this.plains.get()); } } + diff --git a/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch b/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch index 13b279e590cf..4da082778cff 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch @@ -1,20 +1,5 @@ --- a/net/minecraft/world/level/ServerExplosion.java +++ b/net/minecraft/world/level/ServerExplosion.java -@@ -36,6 +_,14 @@ - import net.minecraft.world.phys.Vec3; - import org.jspecify.annotations.Nullable; - -+// CraftBukkit start -+import net.minecraft.world.entity.boss.enderdragon.EnderDragonPart; -+import net.minecraft.world.entity.boss.enderdragon.EnderDragon; -+import org.bukkit.craftbukkit.event.CraftEventFactory; -+import org.bukkit.craftbukkit.util.CraftLocation; -+import org.bukkit.Location; -+// CraftBukkit end -+ - public class ServerExplosion implements Explosion { - private static final ExplosionDamageCalculator EXPLOSION_DAMAGE_CALCULATOR = new ExplosionDamageCalculator(); - private static final int MAX_DROPS_PER_COMBINED_STACK = 16; @@ -49,6 +_,11 @@ private final DamageSource damageSource; private final ExplosionDamageCalculator damageCalculator; @@ -26,7 +11,7 @@ + public boolean excludeSourceFromDamage = true; // Paper - Allow explosions to damage source public ServerExplosion( - ServerLevel level, + final ServerLevel level, @@ -68,6 +_,10 @@ this.blockInteraction = blockInteraction; this.damageSource = damageSource == null ? level.damageSources().explosion(this) : damageSource; @@ -37,52 +22,52 @@ + // Paper end - add yield } - private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) { + private ExplosionDamageCalculator makeDamageCalculator(final @Nullable Entity source) { @@ -141,7 +_,8 @@ - for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) { - BlockPos blockPos = BlockPos.containing(d3, d4, d5); - BlockState blockState = this.level.getBlockState(blockPos); -- FluidState fluidState = this.level.getFluidState(blockPos); -+ if (!blockState.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed -+ FluidState fluidState = blockState.getFluidState(); // Paper - Perf: Optimize call to getFluid for explosions - if (!this.level.isInWorldBounds(blockPos)) { + for (float stepSize = 0.3F; remainingPower > 0.0F; remainingPower -= 0.22500001F) { + BlockPos pos = BlockPos.containing(xp, yp, zp); + BlockState block = this.level.getBlockState(pos); +- FluidState fluid = this.level.getFluidState(pos); ++ if (!block.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed ++ FluidState fluid = block.getFluidState(); // Paper - Perf: Optimize call to getFluid for explosions + if (!this.level.isInWorldBounds(pos)) { break; } -@@ -154,6 +_,15 @@ +@@ -153,6 +_,15 @@ - if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockPos, blockState, f)) { - set.add(blockPos); + if (remainingPower > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, pos, block, remainingPower)) { + toBlowSet.add(pos); + // Paper start - prevent headless pistons from forming -+ if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowHeadlessPistons && blockState.is(net.minecraft.world.level.block.Blocks.MOVING_PISTON)) { -+ net.minecraft.world.level.block.entity.BlockEntity extension = this.level.getBlockEntity(blockPos); ++ if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowHeadlessPistons && block.is(net.minecraft.world.level.block.Blocks.MOVING_PISTON)) { ++ net.minecraft.world.level.block.entity.BlockEntity extension = this.level.getBlockEntity(pos); + if (extension instanceof net.minecraft.world.level.block.piston.PistonMovingBlockEntity blockEntity && blockEntity.isSourcePiston()) { -+ net.minecraft.core.Direction direction = blockState.getValue(net.minecraft.world.level.block.piston.PistonHeadBlock.FACING); -+ set.add(blockPos.relative(direction.getOpposite())); ++ net.minecraft.core.Direction direction = block.getValue(net.minecraft.world.level.block.piston.PistonHeadBlock.FACING); ++ toBlowSet.add(pos.relative(direction.getOpposite())); + } + } + // Paper end - prevent headless pistons from forming } - d3 += d * 0.3F; -@@ -177,8 +_,8 @@ - int floor3 = Mth.floor(this.center.y + f + 1.0); - int floor4 = Mth.floor(this.center.z - f - 1.0); - int floor5 = Mth.floor(this.center.z + f + 1.0); -- -- for (Entity entity : this.level.getEntities(this.source, new AABB(floor, floor2, floor4, floor1, floor3, floor5))) { -+ List list = this.level.getEntities(this.excludeSourceFromDamage ? this.source : null, new AABB(floor, floor2, floor4, floor1, floor3, floor5), entity -> entity.isAlive() && !entity.isSpectator()); // Paper - Fix lag from explosions processing dead entities, Allow explosions to damage source + xp += xd * 0.3F; +@@ -176,8 +_,9 @@ + int y1 = Mth.floor(this.center.y + doubleRadius + 1.0); + int z0 = Mth.floor(this.center.z - doubleRadius - 1.0); + int z1 = Mth.floor(this.center.z + doubleRadius + 1.0); ++ List list = this.level.getEntities(this.excludeSourceFromDamage ? this.source : null, new AABB(x0, y0, z0, x1, y1, z1), entity -> entity.isAlive() && !entity.isSpectator()); // Paper - Fix lag from explosions processing dead entities, Allow explosions to damage source + +- for (Entity entity : this.level.getEntities(this.source, new AABB(x0, y0, z0, x1, y1, z1))) { + for (Entity entity : list) { // Paper - used in loop if (!entity.ignoreExplosion(this)) { - double d = Math.sqrt(entity.distanceToSqr(this.center)) / f; - if (!(d > 1.0)) { -@@ -186,20 +_,56 @@ - Vec3 vec31 = vec3.subtract(this.center).normalize(); + double dist = Math.sqrt(entity.distanceToSqr(this.center)) / doubleRadius; + if (!(dist > 1.0)) { +@@ -185,20 +_,56 @@ + Vec3 direction = entityOrigin.subtract(this.center).normalize(); boolean shouldDamageEntity = this.damageCalculator.shouldDamageEntity(this, entity); float knockbackMultiplier = this.damageCalculator.getKnockbackMultiplier(entity); -- float f1 = !shouldDamageEntity && knockbackMultiplier == 0.0F ? 0.0F : getSeenPercent(this.center, entity); -+ float f1 = !shouldDamageEntity && knockbackMultiplier == 0.0F ? 0.0F : this.getBlockDensity(this.center, entity); // Paper - Optimize explosions +- float exposure = !shouldDamageEntity && knockbackMultiplier == 0.0F ? 0.0F : getSeenPercent(this.center, entity); ++ float exposure = !shouldDamageEntity && knockbackMultiplier == 0.0F ? 0.0F : this.getBlockDensity(this.center, entity); // Paper - Optimize explosions if (shouldDamageEntity) { -- entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f1)); +- entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, exposure)); + // CraftBukkit start + + // Special case ender dragon only give knockback if no damage is cancelled @@ -91,21 +76,21 @@ + // - Damaging EnderDragonPart while forward the damage to EnderDragon + // - Damaging EnderDragon does nothing + // - EnderDragon hitbox always covers the other parts and is therefore always present -+ if (entity instanceof EnderDragonPart) { ++ if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragonPart) { + continue; + } + + entity.lastDamageCancelled = false; + -+ if (entity instanceof EnderDragon) { -+ for (EnderDragonPart dragonPart : ((EnderDragon) entity).getSubEntities()) { ++ if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon enderDragon) { ++ for (net.minecraft.world.entity.boss.enderdragon.EnderDragonPart dragonPart : enderDragon.getSubEntities()) { + // Calculate damage separately for each EntityComplexPart + if (list.contains(dragonPart)) { -+ dragonPart.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, dragonPart, f1)); ++ dragonPart.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, dragonPart, exposure)); + } + } + } else { -+ entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f1)); ++ entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, exposure)); + } + + if (entity.lastDamageCancelled) { // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Skip entity if damage event was cancelled @@ -114,37 +99,37 @@ + // CraftBukkit end } - double d1 = entity instanceof LivingEntity livingEntity + double knockbackResistance = entity instanceof LivingEntity livingEntity ? livingEntity.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE) : 0.0; -- double d2 = (1.0 - d) * f1 * knockbackMultiplier * (1.0 - d1); -+ double d2 = entity instanceof Player && this.level.paperConfig().environment.disableExplosionKnockback ? 0 : (1.0 - d) * f1 * knockbackMultiplier * (1.0 - d1); // Paper - Vec3 vec32 = vec31.scale(d2); +- double knockbackPower = (1.0 - dist) * exposure * knockbackMultiplier * (1.0 - knockbackResistance); ++ double knockbackPower = entity instanceof Player && this.level.paperConfig().environment.disableExplosionKnockback ? 0 : (1.0 - dist) * exposure * knockbackMultiplier * (1.0 - knockbackResistance); // Paper + Vec3 knockback = direction.scale(knockbackPower); + // CraftBukkit start - Call EntityKnockbackEvent + if (entity instanceof LivingEntity) { + // Paper start - knockback events -+ io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity) entity.getBukkitEntity(), this.source, this.damageSource.getEntity() != null ? this.damageSource.getEntity() : this.source, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.EXPLOSION, d2, vec32); -+ vec32 = event.isCancelled() ? Vec3.ZERO : org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getKnockback()); ++ io.papermc.paper.event.entity.EntityKnockbackEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity) entity.getBukkitEntity(), this.source, this.damageSource.getEntity() != null ? this.damageSource.getEntity() : this.source, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.EXPLOSION, knockbackPower, knockback); ++ knockback = event.isCancelled() ? Vec3.ZERO : org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getKnockback()); + // Paper end - knockback events + } + // CraftBukkit end - entity.push(vec32); - if (entity.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE) && entity instanceof Projectile projectile) { + entity.push(knockback); + if (entity.is(EntityTypeTags.REDIRECTABLE_PROJECTILE) && entity instanceof Projectile projectile) { projectile.setOwner(this.damageSource.getEntity()); - } else if (entity instanceof Player player && !player.isSpectator() && (!player.isCreative() || !player.getAbilities().flying)) { + } else if (entity instanceof Player player && !player.isSpectator() && (!player.isCreative() || !player.getAbilities().flying) && !level.paperConfig().environment.disableExplosionKnockback) { // Paper - Option to disable explosion knockback - this.hitPlayers.put(player, vec32); + this.hitPlayers.put(player, knockback); } -@@ -214,7 +_,56 @@ - List list = new ArrayList<>(); - Util.shuffle(blocks, this.level.random); +@@ -213,7 +_,56 @@ + List stacks = new ArrayList<>(); + Util.shuffle(targetBlocks, this.level.random); + // CraftBukkit start -+ Location location = CraftLocation.toBukkit(this.center, this.level); ++ org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.center, this.level); + List blockList = new ObjectArrayList<>(); -+ for (int i1 = blocks.size() - 1; i1 >= 0; i1--) { -+ org.bukkit.block.Block bblock = org.bukkit.craftbukkit.block.CraftBlock.at(this.level, blocks.get(i1)); ++ for (int i1 = targetBlocks.size() - 1; i1 >= 0; i1--) { ++ org.bukkit.block.Block bblock = org.bukkit.craftbukkit.block.CraftBlock.at(this.level, targetBlocks.get(i1)); + if (!bblock.getType().isAir()) { + blockList.add(bblock); + } @@ -153,22 +138,22 @@ + List bukkitBlocks; + + if (this.source != null) { -+ org.bukkit.event.entity.EntityExplodeEvent event = CraftEventFactory.callEntityExplodeEvent(this.source, blockList, this.yield, this.getBlockInteraction()); ++ org.bukkit.event.entity.EntityExplodeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityExplodeEvent(this.source, blockList, this.yield, this.getBlockInteraction()); + this.wasCanceled = event.isCancelled(); + bukkitBlocks = event.blockList(); + this.yield = event.getYield(); + } else { + org.bukkit.block.Block block = location.getBlock(); + org.bukkit.block.BlockState blockState = (this.damageSource.causingBlockSnapshot() != null) ? this.damageSource.causingBlockSnapshot() : block.getState(); -+ org.bukkit.event.block.BlockExplodeEvent event = CraftEventFactory.callBlockExplodeEvent(block, blockState, blockList, this.yield, this.getBlockInteraction()); ++ org.bukkit.event.block.BlockExplodeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockExplodeEvent(block, blockState, blockList, this.yield, this.getBlockInteraction()); + this.wasCanceled = event.isCancelled(); + bukkitBlocks = event.blockList(); + this.yield = event.getYield(); + } + -+ blocks.clear(); ++ targetBlocks.clear(); + for (org.bukkit.block.Block bblock : bukkitBlocks) { -+ blocks.add(((org.bukkit.craftbukkit.block.CraftBlock) bblock).getPosition()); ++ targetBlocks.add(((org.bukkit.craftbukkit.block.CraftBlock) bblock).getPosition()); + } + + if (this.wasCanceled) { @@ -176,42 +161,41 @@ + } + // CraftBukkit end + - for (BlockPos blockPos : blocks) { + for (BlockPos pos : targetBlocks) { + // CraftBukkit start - TNTPrimeEvent -+ BlockState state = this.level.getBlockState(blockPos); ++ BlockState state = this.level.getBlockState(pos); + Block block = state.getBlock(); + if (level.getGameRules().get(GameRules.TNT_EXPLODES) && block instanceof net.minecraft.world.level.block.TntBlock) { + Entity sourceEntity = this.source == null ? null : this.source; + BlockPos sourceBlock = sourceEntity == null ? BlockPos.containing(this.center) : null; -+ if (!CraftEventFactory.callTNTPrimeEvent(this.level, blockPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.EXPLOSION, sourceEntity, sourceBlock)) { -+ this.level.sendBlockUpdated(blockPos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(), state, Block.UPDATE_ALL); // Update the block on the client ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(this.level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.EXPLOSION, sourceEntity, sourceBlock)) { ++ this.level.sendBlockUpdated(pos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(), state, Block.UPDATE_ALL); // Update the block on the client + continue; + } + } + // CraftBukkit end + - this.level - .getBlockState(blockPos) - .onExplosionHit(this.level, blockPos, this, (itemStack, blockPos1) -> addOrAppendStack(list, itemStack, blockPos1)); -@@ -228,7 +_,11 @@ - private void createFire(List blocks) { - for (BlockPos blockPos : blocks) { - if (this.level.random.nextInt(3) == 0 && this.level.getBlockState(blockPos).isAir() && this.level.getBlockState(blockPos.below()).isSolidRender()) { -- this.level.setBlockAndUpdate(blockPos, BaseFireBlock.getState(this.level, blockPos)); + this.level.getBlockState(pos).onExplosionHit(this.level, pos, this, (stackx, position) -> addOrAppendStack(stacks, stackx, position)); + } + +@@ -225,7 +_,11 @@ + private void createFire(final List targetBlocks) { + for (BlockPos pos : targetBlocks) { + if (this.level.random.nextInt(3) == 0 && this.level.getBlockState(pos).isAir() && this.level.getBlockState(pos.below()).isSolidRender()) { +- this.level.setBlockAndUpdate(pos, BaseFireBlock.getState(this.level, pos)); + // CraftBukkit start - Ignition by explosion -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level, blockPos, this).isCancelled()) { -+ this.level.setBlockAndUpdate(blockPos, BaseFireBlock.getState(this.level, blockPos)); ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level, pos, this).isCancelled()) { ++ this.level.setBlockAndUpdate(pos, BaseFireBlock.getState(this.level, pos)); + } + // CraftBukkit end } } } -@@ -326,4 +_,86 @@ +@@ -323,4 +_,85 @@ } } } + -+ + // Paper start - Optimize explosions + private float getBlockDensity(Vec3 vec3d, Entity entity) { + if (!this.level.paperConfig().environment.optimizeExplosions) { @@ -291,5 +275,5 @@ + return result; + } + } -+ // Paper end ++ // Paper end - Optimize explosions } diff --git a/paper-server/patches/sources/net/minecraft/world/level/ServerLevelAccessor.java.patch b/paper-server/patches/sources/net/minecraft/world/level/ServerLevelAccessor.java.patch index 0f1af218f263..5183763f07cb 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/ServerLevelAccessor.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/ServerLevelAccessor.java.patch @@ -3,7 +3,7 @@ @@ -11,6 +_,17 @@ DifficultyInstance getCurrentDifficultyAt(BlockPos pos); - default void addFreshEntityWithPassengers(Entity entity) { + default void addFreshEntityWithPassengers(final Entity entity) { - entity.getSelfAndPassengers().forEach(this::addFreshEntity); - } + // CraftBukkit start diff --git a/paper-server/patches/sources/net/minecraft/world/level/StructureManager.java.patch b/paper-server/patches/sources/net/minecraft/world/level/StructureManager.java.patch index 690024da9c8e..45e3bbb678d0 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/StructureManager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/StructureManager.java.patch @@ -3,47 +3,47 @@ @@ -48,7 +_,13 @@ } - public List startsForStructure(ChunkPos chunkPos, Predicate structurePredicate) { -- Map allReferences = this.level.getChunk(chunkPos.x, chunkPos.z, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences(); + public List startsForStructure(final ChunkPos pos, final Predicate matcher) { +- Map allReferences = this.level.getChunk(pos.x(), pos.z(), ChunkStatus.STRUCTURE_REFERENCES).getAllReferences(); + // Paper start - Fix swamp hut cat generation deadlock -+ return this.startsForStructure(chunkPos, structurePredicate, null); ++ return this.startsForStructure(pos, matcher, null); + } + -+ public List startsForStructure(ChunkPos chunkPos, Predicate structurePredicate, @Nullable ServerLevelAccessor levelAccessor) { -+ Map allReferences = (levelAccessor == null ? this.level : levelAccessor).getChunk(chunkPos.x, chunkPos.z, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences(); ++ public List startsForStructure(final ChunkPos pos, final Predicate matcher, final @Nullable ServerLevelAccessor levelAccessor) { ++ Map allReferences = (levelAccessor == null ? this.level : levelAccessor).getChunk(pos.x(), pos.z(), ChunkStatus.STRUCTURE_REFERENCES).getAllReferences(); + // Paper end - Fix swamp hut cat generation deadlock - Builder builder = ImmutableList.builder(); + Builder result = ImmutableList.builder(); for (Entry entry : allReferences.entrySet()) { -@@ -113,14 +_,29 @@ +@@ -111,14 +_,29 @@ } - public StructureStart getStructureWithPieceAt(BlockPos pos, HolderSet structures) { -- return this.getStructureWithPieceAt(pos, structures::contains); -+ // Paper start - Fix swamp hut cat generation deadlock -+ return this.getStructureWithPieceAt(pos, structures, null); + public StructureStart getStructureWithPieceAt(final BlockPos blockPos, final HolderSet structures) { +- return this.getStructureWithPieceAt(blockPos, structures::contains); ++ // Paper start - Fix swamp hut cat generation deadlock ++ return this.getStructureWithPieceAt(blockPos, structures, null); + } -+ public StructureStart getStructureWithPieceAt(BlockPos pos, HolderSet structures, final @Nullable ServerLevelAccessor levelAccessor) { -+ return this.getStructureWithPieceAt(pos, structures::contains, levelAccessor); -+ // Paper end - Fix swamp hut cat generation deadlock ++ public StructureStart getStructureWithPieceAt(final BlockPos blockPos, final HolderSet structures, final @Nullable ServerLevelAccessor levelAccessor) { ++ return this.getStructureWithPieceAt(blockPos, structures::contains, levelAccessor); ++ // Paper end - Fix swamp hut cat generation deadlock } - public StructureStart getStructureWithPieceAt(BlockPos pos, Predicate> predicate) { + public StructureStart getStructureWithPieceAt(final BlockPos blockPos, final Predicate> predicate) { + // Paper start - Fix swamp hut cat generation deadlock -+ return this.getStructureWithPieceAt(pos, predicate, null); ++ return this.getStructureWithPieceAt(blockPos, predicate, null); + } + -+ public StructureStart getStructureWithPieceAt(BlockPos pos, TagKey tag, @Nullable ServerLevelAccessor levelAccessor) { -+ return this.getStructureWithPieceAt(pos, structure -> structure.is(tag), levelAccessor); ++ public StructureStart getStructureWithPieceAt(final BlockPos blockPos, final TagKey structureTag, final @Nullable ServerLevelAccessor levelAccessor) { ++ return this.getStructureWithPieceAt(blockPos, structure -> structure.is(structureTag), levelAccessor); + } + -+ public StructureStart getStructureWithPieceAt(BlockPos pos, Predicate> predicate, @Nullable ServerLevelAccessor levelAccessor) { ++ public StructureStart getStructureWithPieceAt(final BlockPos blockPos, final Predicate> predicate, final @Nullable ServerLevelAccessor levelAccessor) { + // Paper end - Fix swamp hut cat generation deadlock - Registry registry = this.registryAccess().lookupOrThrow(Registries.STRUCTURE); + Registry structures = this.registryAccess().lookupOrThrow(Registries.STRUCTURE); for (StructureStart structureStart : this.startsForStructure( -- new ChunkPos(pos), structure -> registry.get(registry.getId(structure)).map(predicate::test).orElse(false) -+ new ChunkPos(pos), structure -> registry.get(registry.getId(structure)).map(predicate::test).orElse(false), levelAccessor // Paper - Fix swamp hut cat generation deadlock +- ChunkPos.containing(blockPos), s -> structures.get(structures.getId(s)).map(predicate::test).orElse(false) ++ ChunkPos.containing(blockPos), s -> structures.get(structures.getId(s)).map(predicate::test).orElse(false), levelAccessor // Paper - Fix swamp hut cat generation deadlock )) { - if (this.structureHasPieceAt(pos, structureStart)) { + if (this.structureHasPieceAt(blockPos, structureStart)) { return structureStart; diff --git a/paper-server/patches/sources/net/minecraft/world/level/TicketStorage.java.patch b/paper-server/patches/sources/net/minecraft/world/level/TicketStorage.java.patch index 9d1bfb3fb2ca..0687c68c610e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/TicketStorage.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/TicketStorage.java.patch @@ -1,15 +1,15 @@ --- a/net/minecraft/world/level/TicketStorage.java +++ b/net/minecraft/world/level/TicketStorage.java -@@ -177,7 +_,7 @@ +@@ -179,7 +_,7 @@ } - private static boolean isTicketSameTypeAndLevel(Ticket first, Ticket second) { -- return second.getType() == first.getType() && second.getTicketLevel() == first.getTicketLevel(); -+ return second.getType() == first.getType() && second.getTicketLevel() == first.getTicketLevel() && java.util.Objects.equals(second.getIdentifier(), first.getIdentifier()); // Paper - add identifier + private static boolean isTicketSameTypeAndLevel(final Ticket ticket, final Ticket t) { +- return t.getType() == ticket.getType() && t.getTicketLevel() == ticket.getTicketLevel(); ++ return t.getType() == ticket.getType() && t.getTicketLevel() == ticket.getTicketLevel() && java.util.Objects.equals(t.getIdentifier(), ticket.getIdentifier()); // Paper - add identifier } - public int getTicketLevelAt(long chunkPos, boolean requireSimulation) { -@@ -298,7 +_,7 @@ + public int getTicketLevelAt(final long key, final boolean simulation) { +@@ -300,7 +_,7 @@ } public void deactivateTicketsOnClosing() { @@ -17,8 +17,8 @@ + this.removeTicketIf((ticket, chunkPos) -> ticket.getType() != TicketType.UNKNOWN && ticket.getType() != TicketType.CHUNK_LOAD && ticket.getType() != TicketType.FUTURE_AWAIT, this.deactivatedTickets); } - public void removeTicketIf(TicketStorage.TicketPredicate predicate, @Nullable Long2ObjectOpenHashMap> tickets) { -@@ -408,4 +_,20 @@ + public void removeTicketIf(final TicketStorage.TicketPredicate predicate, final @Nullable Long2ObjectOpenHashMap> removedTickets) { +@@ -410,4 +_,20 @@ public interface TicketPredicate { boolean test(Ticket ticket, long chunkPos); } @@ -26,16 +26,16 @@ + // Paper start + public boolean addPluginRegionTicket(final ChunkPos pos, final org.bukkit.plugin.Plugin value) { + // Keep inline with force loading -+ return addTicket(pos.toLong(), new Ticket(TicketType.PLUGIN_TICKET, ChunkMap.FORCED_TICKET_LEVEL, value)); ++ return addTicket(pos.pack(), new Ticket(TicketType.PLUGIN_TICKET, ChunkMap.FORCED_TICKET_LEVEL, value)); + } + + public boolean removePluginRegionTicket(final ChunkPos pos, final org.bukkit.plugin.Plugin value) { + // Keep inline with force loading -+ return removeTicket(pos.toLong(), new Ticket(TicketType.PLUGIN_TICKET, ChunkMap.FORCED_TICKET_LEVEL, value)); ++ return removeTicket(pos.pack(), new Ticket(TicketType.PLUGIN_TICKET, ChunkMap.FORCED_TICKET_LEVEL, value)); + } + + public void removeAllPluginRegionTickets(TicketType ticketType, int ticketLevel, org.bukkit.plugin.Plugin ticketIdentifier) { -+ removeTicketIf((ticket, chunkKey) -> ticket.getType() == ticketType && ticket.getTicketLevel() == ticketLevel && ticket.getIdentifier() == ticketIdentifier, null); ++ removeTicketIf((ticket, _) -> ticket.getType() == ticketType && ticket.getTicketLevel() == ticketLevel && ticket.getIdentifier() == ticketIdentifier, null); + } + // Paper end } diff --git a/paper-server/patches/sources/net/minecraft/world/level/biome/MobSpawnSettings.java.patch b/paper-server/patches/sources/net/minecraft/world/level/biome/MobSpawnSettings.java.patch index 1c762eb3f28b..b9f1404094d7 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/biome/MobSpawnSettings.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/biome/MobSpawnSettings.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/biome/MobSpawnSettings.java +++ b/net/minecraft/world/level/biome/MobSpawnSettings.java -@@ -69,9 +_,33 @@ +@@ -67,9 +_,33 @@ } public static class Builder { @@ -28,8 +28,8 @@ + } + } private final Map> spawners = Util.makeEnumMap( -- MobCategory.class, mobCategory -> WeightedList.builder() -+ MobCategory.class, mobCategory -> new MobListBuilder<>() +- MobCategory.class, c -> WeightedList.builder() ++ MobCategory.class, c -> new MobListBuilder<>() ); + // Paper end - Perf: keep track of data in a pair set to give O(1) contains calls private final Map, MobSpawnSettings.MobSpawnCost> mobSpawnCosts = Maps.newLinkedHashMap(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/AbstractCandleBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/AbstractCandleBlock.java.patch index 45e20e27aabc..792875602be6 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/AbstractCandleBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/AbstractCandleBlock.java.patch @@ -2,13 +2,13 @@ +++ b/net/minecraft/world/level/block/AbstractCandleBlock.java @@ -44,6 +_,11 @@ @Override - protected void onProjectileHit(Level level, BlockState state, BlockHitResult hit, Projectile projectile) { + protected void onProjectileHit(final Level level, final BlockState state, final BlockHitResult blockHit, final Projectile projectile) { if (!level.isClientSide() && projectile.isOnFire() && this.canBeLit(state)) { + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, hit.getBlockPos(), projectile).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, blockHit.getBlockPos(), projectile).isCancelled()) { + return; + } + // CraftBukkit end - setLit(level, state, hit.getBlockPos(), true); + setLit(level, state, blockHit.getBlockPos(), true); } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch index 84b3f16b693b..aa79ed5f8219 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/block/AbstractCauldronBlock.java +++ b/net/minecraft/world/level/block/AbstractCauldronBlock.java -@@ -57,7 +_,7 @@ - ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult +@@ -63,7 +_,7 @@ + final BlockHitResult hitResult ) { - CauldronInteraction cauldronInteraction = this.interactions.map().get(stack.getItem()); -- return cauldronInteraction.interact(state, level, pos, player, hand, stack); -+ return cauldronInteraction.interact(state, level, pos, player, hand, stack, hitResult.getDirection()); // Paper - pass hit direction + CauldronInteraction behavior = this.interactions.get(itemStack); +- return behavior.interact(state, level, pos, player, hand, itemStack); ++ return behavior.interact(state, level, pos, player, hand, itemStack, hitResult.getDirection()); // Paper - pass hit direction } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/AnvilBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/AnvilBlock.java.patch index 881ac03b5221..bb6e8332e912 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/AnvilBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/AnvilBlock.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/level/block/AnvilBlock.java +++ b/net/minecraft/world/level/block/AnvilBlock.java -@@ -57,8 +_,9 @@ - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { +@@ -59,8 +_,9 @@ + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { if (!level.isClientSide()) { - player.openMenu(state.getMenuProvider(level, pos)); + if (player.openMenu(state.getMenuProvider(level, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BambooSaplingBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BambooSaplingBlock.java.patch index 0d3b02199c7b..ac54e89726fc 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BambooSaplingBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BambooSaplingBlock.java.patch @@ -3,7 +3,7 @@ @@ -38,7 +_,7 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { - if (random.nextInt(3) == 0 && level.isEmptyBlock(pos.above()) && level.getRawBrightness(pos.above(), 0) >= 9) { + if (random.nextFloat() < (level.spigotConfig.bambooModifier / (100.0F * 3)) && level.isEmptyBlock(pos.above()) && level.getRawBrightness(pos.above(), 0) >= 9) { // Spigot - SPIGOT-7159: Better modifier resolution this.growBamboo(level, pos); @@ -12,8 +12,8 @@ @@ -89,6 +_,6 @@ } - protected void growBamboo(Level level, BlockPos state) { -- level.setBlock(state.above(), Blocks.BAMBOO.defaultBlockState().setValue(BambooStalkBlock.LEAVES, BambooLeaves.SMALL), Block.UPDATE_ALL); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, state, state.above(), Blocks.BAMBOO.defaultBlockState().setValue(BambooStalkBlock.LEAVES, BambooLeaves.SMALL), Block.UPDATE_ALL); // CraftBukkit - BlockSpreadEvent + protected void growBamboo(final Level level, final BlockPos pos) { +- level.setBlock(pos.above(), Blocks.BAMBOO.defaultBlockState().setValue(BambooStalkBlock.LEAVES, BambooLeaves.SMALL), Block.UPDATE_ALL); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, pos.above(), Blocks.BAMBOO.defaultBlockState().setValue(BambooStalkBlock.LEAVES, BambooLeaves.SMALL), Block.UPDATE_ALL); // CraftBukkit - BlockSpreadEvent } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BambooStalkBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BambooStalkBlock.java.patch index 7d3f8ebef722..8f2b6833f266 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BambooStalkBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BambooStalkBlock.java.patch @@ -2,84 +2,85 @@ +++ b/net/minecraft/world/level/block/BambooStalkBlock.java @@ -119,9 +_,9 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (state.getValue(STAGE) == 0) { - if (random.nextInt(3) == 0 && level.isEmptyBlock(pos.above()) && level.getRawBrightness(pos.above(), 0) >= 9) { + if (random.nextFloat() < (level.spigotConfig.bambooModifier / (100.0F * 3)) && level.isEmptyBlock(pos.above()) && level.getRawBrightness(pos.above(), 0) >= 9) { // Spigot - SPIGOT-7159: Better modifier resolution - int i = this.getHeightBelowUpToMax(level, pos) + 1; -- if (i < 16) { -+ if (i < level.paperConfig().maxGrowthHeight.bamboo.max) { // Paper - Configurable cactus/bamboo/reed growth height - this.growBamboo(state, level, pos, random, i); + int height = this.getHeightBelowUpToMax(level, pos) + 1; +- if (height < 16) { ++ if (height < level.paperConfig().maxGrowthHeight.bamboo.max) { // Paper - Configurable cactus/bamboo/reed growth height + this.growBamboo(state, level, pos, random, height); } } -@@ -157,7 +_,7 @@ - public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) { - int heightAboveUpToMax = this.getHeightAboveUpToMax(level, pos); - int heightBelowUpToMax = this.getHeightBelowUpToMax(level, pos); -- return heightAboveUpToMax + heightBelowUpToMax + 1 < 16 && level.getBlockState(pos.above(heightAboveUpToMax)).getValue(STAGE) != 1; -+ return heightAboveUpToMax + heightBelowUpToMax + 1 < ((Level) level).paperConfig().maxGrowthHeight.bamboo.max && level.getBlockState(pos.above(heightAboveUpToMax)).getValue(STAGE) != 1; // Paper - Configurable cactus/bamboo/reed growth height - } - - @Override -@@ -175,7 +_,7 @@ - for (int i2 = 0; i2 < i1; i2++) { - BlockPos blockPos = pos.above(heightAboveUpToMax); - BlockState blockState = level.getBlockState(blockPos); -- if (i >= 16 || blockState.getValue(STAGE) == 1 || !level.isEmptyBlock(blockPos.above())) { -+ if (i >= level.paperConfig().maxGrowthHeight.bamboo.max || !blockState.is(Blocks.BAMBOO) || blockState.getValue(BambooStalkBlock.STAGE) == 1 || !level.isEmptyBlock(blockPos.above())) { // CraftBukkit - If the BlockSpreadEvent was cancelled, we have no bamboo here // Paper - Configurable cactus/bamboo/reed growth height +@@ -158,7 +_,7 @@ + int heightAbove = this.getHeightAboveUpToMax(level, pos); + int heightBelow = this.getHeightBelowUpToMax(level, pos); + BlockPos growthPos = pos.above(heightAbove + 1); +- return heightAbove + heightBelow + 1 < 16 ++ return heightAbove + heightBelow + 1 < ((Level) level).paperConfig().maxGrowthHeight.bamboo.max // Paper - Configurable cactus/bamboo/reed growth height + && level.getBlockState(pos.above(heightAbove)).getValue(STAGE) != 1 + && level.isInsideBuildHeight(growthPos) + && level.isEmptyBlock(growthPos); +@@ -180,7 +_,7 @@ + BlockPos topPos = pos.above(heightAbove); + BlockState topState = level.getBlockState(topPos); + BlockPos growthPos = topPos.above(); +- if (totalHeight >= 16 || topState.getValue(STAGE) == 1 || !level.isEmptyBlock(growthPos) || level.isOutsideBuildHeight(growthPos)) { ++ if (totalHeight >= level.paperConfig().maxGrowthHeight.bamboo.max || !topState.is(Blocks.BAMBOO) || topState.getValue(STAGE) == 1 || !level.isEmptyBlock(growthPos) || level.isOutsideBuildHeight(growthPos)) { // CraftBukkit - If the BlockSpreadEvent was cancelled, we have no bamboo here // Paper - Configurable cactus/bamboo/reed growth height return; } -@@ -190,27 +_,38 @@ - BlockPos blockPos = pos.below(2); - BlockState blockState1 = level.getBlockState(blockPos); - BambooLeaves bambooLeaves = BambooLeaves.NONE; +@@ -195,27 +_,39 @@ + BlockPos twoBelowPos = pos.below(2); + BlockState twoBelowState = level.getBlockState(twoBelowPos); + BambooLeaves leaves = BambooLeaves.NONE; + boolean shouldUpdateOthers = false; // CraftBukkit - if (age >= 1) { - if (!blockState.is(Blocks.BAMBOO) || blockState.getValue(LEAVES) == BambooLeaves.NONE) { - bambooLeaves = BambooLeaves.SMALL; - } else if (blockState.is(Blocks.BAMBOO) && blockState.getValue(LEAVES) != BambooLeaves.NONE) { - bambooLeaves = BambooLeaves.LARGE; - if (blockState1.is(Blocks.BAMBOO)) { -- level.setBlock(pos.below(), blockState.setValue(LEAVES, BambooLeaves.SMALL), Block.UPDATE_ALL); -- level.setBlock(blockPos, blockState1.setValue(LEAVES, BambooLeaves.NONE), Block.UPDATE_ALL); + if (height >= 1) { + if (!belowState.is(Blocks.BAMBOO) || belowState.getValue(LEAVES) == BambooLeaves.NONE) { + leaves = BambooLeaves.SMALL; + } else if (belowState.is(Blocks.BAMBOO) && belowState.getValue(LEAVES) != BambooLeaves.NONE) { + leaves = BambooLeaves.LARGE; + if (twoBelowState.is(Blocks.BAMBOO)) { +- level.setBlock(pos.below(), belowState.setValue(LEAVES, BambooLeaves.SMALL), Block.UPDATE_ALL); +- level.setBlock(twoBelowPos, twoBelowState.setValue(LEAVES, BambooLeaves.NONE), Block.UPDATE_ALL); + // CraftBukkit start - moved down -+ // level.setBlock(pos.below(), blockState.setValue(LEAVES, BambooLeaves.SMALL), Block.UPDATE_ALL); -+ // level.setBlock(blockPos, blockState1.setValue(LEAVES, BambooLeaves.NONE), Block.UPDATE_ALL); ++ // level.setBlock(pos.below(), belowState.setValue(LEAVES, BambooLeaves.SMALL), Block.UPDATE_ALL); ++ // level.setBlock(twoBelowPos, twoBelowState.setValue(LEAVES, BambooLeaves.NONE), Block.UPDATE_ALL); + shouldUpdateOthers = true; + // CraftBukkit end } } } - int i = state.getValue(AGE) != 1 && !blockState1.is(Blocks.BAMBOO) ? 0 : 1; -- int i1 = (age < 11 || !(random.nextFloat() < 0.25F)) && age != 15 ? 0 : 1; -- level.setBlock(pos.above(), this.defaultBlockState().setValue(AGE, i).setValue(LEAVES, bambooLeaves).setValue(STAGE, i1), Block.UPDATE_ALL); -+ int i1 = (age < level.paperConfig().maxGrowthHeight.bamboo.min || random.nextFloat() >= 0.25F) && age != (level.paperConfig().maxGrowthHeight.bamboo.max - 1) ? 0 : 1; // Paper - Configurable cactus/bamboo/reed growth height + int age = state.getValue(AGE) != 1 && !twoBelowState.is(Blocks.BAMBOO) ? 0 : 1; +- int stage = (height < 11 || !(random.nextFloat() < 0.25F)) && height != 15 ? 0 : 1; +- level.setBlock(pos.above(), this.defaultBlockState().setValue(AGE, age).setValue(LEAVES, leaves).setValue(STAGE, stage), Block.UPDATE_ALL); ++ int stage = (height < level.paperConfig().maxGrowthHeight.bamboo.min || !(random.nextFloat() < 0.25F)) && height != (level.paperConfig().maxGrowthHeight.bamboo.max - 1) ? 0 : 1; // Paper - Configurable cactus/bamboo/reed growth height ++ // level.setBlock(pos.above(), this.defaultBlockState().setValue(AGE, age).setValue(LEAVES, leaves).setValue(STAGE, stage), Block.UPDATE_ALL); + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, pos.above(), this.defaultBlockState().setValue(AGE, i).setValue(LEAVES, bambooLeaves).setValue(STAGE, i1), Block.UPDATE_ALL)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, pos.above(), this.defaultBlockState().setValue(AGE, age).setValue(LEAVES, leaves).setValue(STAGE, stage), Block.UPDATE_ALL)) { + if (shouldUpdateOthers) { -+ level.setBlock(pos.below(), blockState.setValue(BambooStalkBlock.LEAVES, BambooLeaves.SMALL), Block.UPDATE_ALL); -+ level.setBlock(blockPos, blockState1.setValue(BambooStalkBlock.LEAVES, BambooLeaves.NONE), Block.UPDATE_ALL); ++ level.setBlock(pos.below(), belowState.setValue(LEAVES, BambooLeaves.SMALL), Block.UPDATE_ALL); ++ level.setBlock(twoBelowPos, twoBelowState.setValue(LEAVES, BambooLeaves.NONE), Block.UPDATE_ALL); + } + } + // CraftBukkit end } - protected int getHeightAboveUpToMax(BlockGetter level, BlockPos pos) { - int i = 0; + protected int getHeightAboveUpToMax(final BlockGetter level, final BlockPos pos) { + int height = 0; -- while (i < 16 && level.getBlockState(pos.above(i + 1)).is(Blocks.BAMBOO)) { -+ while (i < ((Level) level).paperConfig().maxGrowthHeight.bamboo.max && level.getBlockState(pos.above(i + 1)).is(Blocks.BAMBOO)) { // Paper - Configurable cactus/bamboo/reed growth height - i++; +- while (height < 16 && level.getBlockState(pos.above(height + 1)).is(Blocks.BAMBOO)) { ++ while (height < ((Level) level).paperConfig().maxGrowthHeight.bamboo.max && level.getBlockState(pos.above(height + 1)).is(Blocks.BAMBOO)) { // Paper - Configurable cactus/bamboo/reed growth height + height++; } -@@ -220,7 +_,7 @@ - protected int getHeightBelowUpToMax(BlockGetter level, BlockPos pos) { - int i = 0; +@@ -225,7 +_,7 @@ + protected int getHeightBelowUpToMax(final BlockGetter level, final BlockPos pos) { + int height = 0; -- while (i < 16 && level.getBlockState(pos.below(i + 1)).is(Blocks.BAMBOO)) { -+ while (i < ((Level) level).paperConfig().maxGrowthHeight.bamboo.max && level.getBlockState(pos.below(i + 1)).is(Blocks.BAMBOO)) { // Paper - Configurable cactus/bamboo/reed growth height - i++; +- while (height < 16 && level.getBlockState(pos.below(height + 1)).is(Blocks.BAMBOO)) { ++ while (height < ((Level) level).paperConfig().maxGrowthHeight.bamboo.max && level.getBlockState(pos.below(height + 1)).is(Blocks.BAMBOO)) { // Paper - Configurable cactus/bamboo/reed growth height + height++; } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BarrelBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BarrelBlock.java.patch index 23b0b9718c2f..de2dcc6bf900 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BarrelBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BarrelBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/BarrelBlock.java +++ b/net/minecraft/world/level/block/BarrelBlock.java -@@ -41,8 +_,7 @@ - - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { +@@ -43,8 +_,7 @@ + protected InteractionResult useWithoutItem( + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { - if (level instanceof ServerLevel serverLevel && level.getBlockEntity(pos) instanceof BarrelBlockEntity barrelBlockEntity) { - player.openMenu(barrelBlockEntity); + if (level instanceof ServerLevel serverLevel && level.getBlockEntity(pos) instanceof BarrelBlockEntity barrelBlockEntity && player.openMenu(barrelBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BaseFireBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BaseFireBlock.java.patch index ad725ef8b45c..051186dfaaaf 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BaseFireBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BaseFireBlock.java.patch @@ -1,21 +1,21 @@ --- a/net/minecraft/world/level/block/BaseFireBlock.java +++ b/net/minecraft/world/level/block/BaseFireBlock.java -@@ -129,12 +_,13 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -136,12 +_,13 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent effectApplier.apply(InsideBlockEffectType.CLEAR_FREEZE); effectApplier.apply(InsideBlockEffectType.FIRE_IGNITE); - effectApplier.runAfter(InsideBlockEffectType.FIRE_IGNITE, entity1 -> entity1.hurt(entity1.level().damageSources().inFire(), this.fireDamage)); + effectApplier.runAfter(InsideBlockEffectType.FIRE_IGNITE, e -> e.hurt(e.level().damageSources().inFire(), this.fireDamage)); } -- public static void fireIgnite(Entity entity) { -+ public static void fireIgnite(Entity entity, BlockPos pos) { // Paper - track position inside effect was triggered on +- public static void fireIgnite(final Entity entity) { ++ public static void fireIgnite(final Entity entity, BlockPos pos) { // Paper - track position inside effect was triggered on if (!entity.fireImmune()) { if (entity.getRemainingFireTicks() < 0) { entity.setRemainingFireTicks(entity.getRemainingFireTicks() + 1); -@@ -144,30 +_,41 @@ +@@ -151,30 +_,41 @@ } if (entity.getRemainingFireTicks() >= 0) { @@ -37,14 +37,14 @@ } @Override -- protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { -+ protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston, net.minecraft.world.item.context.UseOnContext context) { // CraftBukkit - context +- protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) { ++ protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston, net.minecraft.world.item.context.UseOnContext context) { // CraftBukkit - context if (!oldState.is(state.getBlock())) { if (inPortalDimension(level)) { - Optional optional = PortalShape.findEmptyPortalShape(level, pos, Direction.Axis.X); - if (optional.isPresent()) { -- optional.get().createPortalBlocks(level); -+ optional.get().createPortalBlocks(level, (context == null) ? null : context.getPlayer()); // CraftBukkit - player + Optional optionalShape = PortalShape.findEmptyPortalShape(level, pos, Direction.Axis.X); + if (optionalShape.isPresent()) { +- optionalShape.get().createPortalBlocks(level); ++ optionalShape.get().createPortalBlocks(level, (context == null) ? null : context.getPlayer()); // CraftBukkit - player return; } } @@ -56,13 +56,13 @@ } } - private static boolean inPortalDimension(Level level) { + private static boolean inPortalDimension(final Level level) { - return level.dimension() == Level.OVERWORLD || level.dimension() == Level.NETHER; + return level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.OVERWORLD || level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER; // CraftBukkit - getTypeKey() } @Override -@@ -212,4 +_,12 @@ +@@ -219,4 +_,12 @@ } } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BasePressurePlateBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BasePressurePlateBlock.java.patch index 81437c45bd61..11b14b18d76e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BasePressurePlateBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BasePressurePlateBlock.java.patch @@ -1,41 +1,41 @@ --- a/net/minecraft/world/level/block/BasePressurePlateBlock.java +++ b/net/minecraft/world/level/block/BasePressurePlateBlock.java -@@ -82,6 +_,7 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -90,6 +_,7 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (!level.isClientSide()) { - int signalForState = this.getSignalForState(state); - if (signalForState == 0) { -@@ -94,6 +_,16 @@ - int signalStrength = this.getSignalStrength(level, pos); - boolean flag = currentSignal > 0; - boolean flag1 = signalStrength > 0; + int signal = this.getSignalForState(state); + if (signal == 0) { +@@ -102,6 +_,16 @@ + int signal = this.getSignalStrength(level, pos); + boolean wasPressed = oldSignal > 0; + boolean isPressed = signal > 0; + + // CraftBukkit start - Interact Pressure Plate -+ if (flag != flag1) { -+ org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), currentSignal, signalStrength); ++ if (wasPressed != isPressed) { ++ org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), oldSignal, signal); + eventRedstone.callEvent(); + -+ flag1 = eventRedstone.getNewCurrent() > 0; -+ signalStrength = eventRedstone.getNewCurrent(); ++ isPressed = eventRedstone.getNewCurrent() > 0; ++ signal = eventRedstone.getNewCurrent(); + } + // CraftBukkit end - if (currentSignal != signalStrength) { - BlockState blockState = this.setSignalForState(state, signalStrength); - level.setBlock(pos, blockState, Block.UPDATE_CLIENTS); -@@ -142,7 +_,13 @@ + if (oldSignal != signal) { + BlockState newState = this.setSignalForState(state, signal); + level.setBlock(pos, newState, Block.UPDATE_CLIENTS); +@@ -150,7 +_,13 @@ } - protected static int getEntityCount(Level level, AABB box, Class entityClass) { -- return level.getEntitiesOfClass(entityClass, box, EntitySelector.NO_SPECTATORS.and(entity -> !entity.isIgnoringBlockTriggers())).size(); + protected static int getEntityCount(final Level level, final AABB entityDetectionBox, final Class entityClass) { +- return level.getEntitiesOfClass(entityClass, entityDetectionBox, EntitySelector.NO_SPECTATORS.and(e -> !e.isIgnoringBlockTriggers())).size(); + // CraftBukkit start -+ return BasePressurePlateBlock.getEntities(level, box, entityClass).size(); ++ return BasePressurePlateBlock.getEntities(level, entityDetectionBox, entityClass).size(); + } + + protected static java.util.List getEntities(Level level, AABB box, Class entityClass) { -+ return level.getEntitiesOfClass(entityClass, box, EntitySelector.NO_SPECTATORS.and(entity -> !entity.isIgnoringBlockTriggers())); ++ return level.getEntitiesOfClass(entityClass, box, EntitySelector.NO_SPECTATORS.and(e -> !e.isIgnoringBlockTriggers())); + // CraftBukkit end } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BeaconBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BeaconBlock.java.patch index 07ff52d43be4..439cead433ab 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BeaconBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BeaconBlock.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/level/block/BeaconBlock.java +++ b/net/minecraft/world/level/block/BeaconBlock.java -@@ -45,8 +_,7 @@ - - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { -- if (!level.isClientSide() && level.getBlockEntity(pos) instanceof BeaconBlockEntity beaconBlockEntity) { -- player.openMenu(beaconBlockEntity); -+ if (!level.isClientSide() && level.getBlockEntity(pos) instanceof BeaconBlockEntity beaconBlockEntity && player.openMenu(beaconBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation +@@ -47,8 +_,7 @@ + protected InteractionResult useWithoutItem( + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { +- if (!level.isClientSide() && level.getBlockEntity(pos) instanceof BeaconBlockEntity beacon) { +- player.openMenu(beacon); ++ if (!level.isClientSide() && level.getBlockEntity(pos) instanceof BeaconBlockEntity beacon && player.openMenu(beacon).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation player.awardStat(Stats.INTERACT_WITH_BEACON); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch index 9b9473adc698..112f8088bfe2 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch @@ -6,16 +6,16 @@ BedRule bedRule = level.environmentAttributes().getValue(EnvironmentAttributes.BED_RULE, pos); - if (bedRule.explodes()) { + if (false && bedRule.explodes()) { // CraftBukkit - moved world and biome check into Player - bedRule.errorMessage().ifPresent(component -> player.displayClientMessage(component, true)); + bedRule.errorMessage().ifPresent(player::sendOverlayMessage); level.removeBlock(pos, false); BlockPos blockPos = pos.relative(state.getValue(FACING).getOpposite()); @@ -103,22 +_,61 @@ - level.explode(null, level.damageSources().badRespawnPointExplosion(center), null, center, 5.0F, true, Level.ExplosionInteraction.BLOCK); + level.explode(null, level.damageSources().badRespawnPointExplosion(boomPos), null, boomPos, 5.0F, true, Level.ExplosionInteraction.BLOCK); return InteractionResult.SUCCESS_SERVER; } else if (state.getValue(OCCUPIED)) { + if (bedRule.explodes()) return this.explodeBed(state, level, pos); // Paper - check explode first if (!this.kickVillagerOutOfBed(level, pos)) { - player.displayClientMessage(Component.translatable("block.minecraft.bed.occupied"), true); + player.sendOverlayMessage(Component.translatable("block.minecraft.bed.occupied")); } return InteractionResult.SUCCESS_SERVER; @@ -24,14 +24,14 @@ + final BlockState finalBlockState = state; + final BlockPos finalBlockPos = pos; + // CraftBukkit end - player.startSleepInBed(pos).ifLeft(bedSleepingProblem -> { -- if (bedSleepingProblem.message() != null) { + player.startSleepInBed(pos).ifLeft(problem -> { +- if (problem.message() != null) { + // Paper start - PlayerBedFailEnterEvent -+ if (false && bedSleepingProblem.message() != null) { // Moved down - player.displayClientMessage(bedSleepingProblem.message(), true); ++ if (false && problem.message() != null) { // Moved down + player.sendOverlayMessage(problem.message()); } -+ if (bedSleepingProblem != null) { -+ io.papermc.paper.event.player.PlayerBedFailEnterEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBedFailEnterEvent(player, finalBlockPos, bedSleepingProblem); ++ if (problem != null) { ++ io.papermc.paper.event.player.PlayerBedFailEnterEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBedFailEnterEvent(player, finalBlockPos, problem); + if (event.isCancelled()) { + return; + } @@ -44,7 +44,7 @@ + // Paper start - PlayerBedFailEnterEvent + final net.kyori.adventure.text.Component message = event.getMessage(); + if (message != null) { -+ player.displayClientMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), true); ++ player.sendOverlayMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message)); + } + } + // Paper end - PlayerBedFailEnterEvent @@ -69,13 +69,13 @@ + } + // CraftBukkit end + - private boolean kickVillagerOutOfBed(Level level, BlockPos pos) { - List entitiesOfClass = level.getEntitiesOfClass(Villager.class, new AABB(pos), LivingEntity::isSleeping); - if (entitiesOfClass.isEmpty()) { -@@ -296,6 +_,11 @@ + private boolean kickVillagerOutOfBed(final Level level, final BlockPos pos) { + List villagers = level.getEntitiesOfClass(Villager.class, new AABB(pos), LivingEntity::isSleeping); + if (villagers.isEmpty()) { +@@ -298,6 +_,11 @@ if (!level.isClientSide()) { - BlockPos blockPos = pos.relative(state.getValue(FACING)); - level.setBlock(blockPos, state.setValue(PART, BedPart.HEAD), Block.UPDATE_ALL); + BlockPos otherPos = pos.relative(state.getValue(FACING)); + level.setBlock(otherPos, state.setValue(PART, BedPart.HEAD), Block.UPDATE_ALL); + // CraftBukkit start - SPIGOT-7315: Don't updated if we capture block states + if (level.captureBlockStates) { + return; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BeehiveBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BeehiveBlock.java.patch index 3dfcabb0c0a9..4173f2362fd8 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BeehiveBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BeehiveBlock.java.patch @@ -1,61 +1,61 @@ --- a/net/minecraft/world/level/block/BeehiveBlock.java +++ b/net/minecraft/world/level/block/BeehiveBlock.java -@@ -87,8 +_,8 @@ - } - - @Override -- public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack stack) { -- super.playerDestroy(level, player, pos, state, blockEntity, stack); -+ public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack stack, boolean includeDrops, boolean dropExp) { // Paper - fix drops not preventing stats/food exhaustion -+ super.playerDestroy(level, player, pos, state, blockEntity, stack, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion +@@ -94,8 +_,9 @@ + final BlockState state, + final @Nullable BlockEntity blockEntity, + final ItemStack destroyedWith ++ , boolean includeDrops, boolean dropExp // Paper - fix drops not preventing stats/food exhaustion + ) { +- super.playerDestroy(level, player, pos, state, blockEntity, destroyedWith); ++ super.playerDestroy(level, player, pos, state, blockEntity, destroyedWith, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion if (!level.isClientSide() && blockEntity instanceof BeehiveBlockEntity beehiveBlockEntity) { - if (!EnchantmentHelper.hasTag(stack, EnchantmentTags.PREVENTS_BEE_SPAWNS_WHEN_MINING)) { + if (!EnchantmentHelper.hasTag(destroyedWith, EnchantmentTags.PREVENTS_BEE_SPAWNS_WHEN_MINING)) { beehiveBlockEntity.emptyAllLivingFromHive(player, state, BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY); -@@ -96,7 +_,7 @@ +@@ -103,7 +_,7 @@ this.angerNearbyBees(level, pos); } -- CriteriaTriggers.BEE_NEST_DESTROYED.trigger((ServerPlayer)player, state, stack, beehiveBlockEntity.getOccupantCount()); -+ // CriteriaTriggers.BEE_NEST_DESTROYED.trigger((ServerPlayer)player, state, stack, beehiveBlockEntity.getOccupantCount()); // Paper - Trigger bee_nest_destroyed trigger in the correct place; moved until after items are dropped +- CriteriaTriggers.BEE_NEST_DESTROYED.trigger((ServerPlayer)player, state, destroyedWith, beehiveBlockEntity.getOccupantCount()); ++ // CriteriaTriggers.BEE_NEST_DESTROYED.trigger((ServerPlayer)player, state, destroyedWith, beehiveBlockEntity.getOccupantCount()); // Paper - Trigger bee_nest_destroyed trigger in the correct place; moved until after items are dropped } } -@@ -118,7 +_,7 @@ - for (Bee bee : entitiesOfClass) { +@@ -127,7 +_,7 @@ + for (Bee bee : beesToAnger) { if (bee.getTarget() == null) { - Player player = Util.getRandom(entitiesOfClass1, level.random); -- bee.setTarget(player); -+ bee.setTarget(player, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER); // CraftBukkit + Player angerTarget = Util.getRandom(playersToBeAngryAt, level.getRandom()); +- bee.setTarget(angerTarget); ++ bee.setTarget(angerTarget, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER); // CraftBukkit } } } -@@ -128,7 +_,7 @@ - ServerLevel level, ItemStack stack, BlockState state, @Nullable BlockEntity blockEntity, @Nullable Entity entity, BlockPos pos +@@ -142,7 +_,7 @@ + final BlockPos pos ) { dropFromBlockInteractLootTable( -- level, BuiltInLootTables.HARVEST_BEEHIVE, state, blockEntity, stack, entity, (serverLevel, itemStack) -> popResource(serverLevel, pos, itemStack) -+ level, BuiltInLootTables.HARVEST_BEEHIVE, state, blockEntity, stack, entity, (serverLevel, itemStack) -> popResource(serverLevel, pos, itemStack) // Paper - diff on change - copied below +- level, BuiltInLootTables.HARVEST_BEEHIVE, blockState, blockEntity, tool, entity, (serverLevel, stack) -> popResource(serverLevel, pos, stack) ++ level, BuiltInLootTables.HARVEST_BEEHIVE, blockState, blockEntity, tool, entity, (serverLevel, stack) -> popResource(serverLevel, pos, stack) // Paper - diff on change - copied below ); } -@@ -141,7 +_,19 @@ - if (honeyLevelValue >= 5) { - Item item = stack.getItem(); - if (level instanceof ServerLevel serverLevel && stack.is(Items.SHEARS)) { -- dropHoneycomb(serverLevel, stack, state, level.getBlockEntity(pos), player, pos); +@@ -161,7 +_,19 @@ + if (honeyLevel >= 5) { + Item item = itemStack.getItem(); + if (level instanceof ServerLevel serverLevel && itemStack.is(Items.SHEARS)) { +- dropHoneycomb(serverLevel, itemStack, state, level.getBlockEntity(pos), player, pos); + // Paper start - Add PlayerShearBlockEvent + List drops = new java.util.ArrayList<>(); + dropFromBlockInteractLootTable( -+ serverLevel, BuiltInLootTables.HARVEST_BEEHIVE, state, level.getBlockEntity(pos), stack, player, (serverLevel1, itemStack) -> drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack)) ++ serverLevel, BuiltInLootTables.HARVEST_BEEHIVE, state, level.getBlockEntity(pos), itemStack, player, (_, stack) -> drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack)) + ); -+ io.papermc.paper.event.block.PlayerShearBlockEvent event = new io.papermc.paper.event.block.PlayerShearBlockEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), drops); ++ io.papermc.paper.event.block.PlayerShearBlockEvent event = new io.papermc.paper.event.block.PlayerShearBlockEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), drops); + if (!event.callEvent()) { + return InteractionResult.PASS; + } -+ for (org.bukkit.inventory.ItemStack itemStack : event.getDrops()) { -+ popResource(serverLevel, pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(itemStack)); ++ for (org.bukkit.inventory.ItemStack stack : event.getDrops()) { ++ popResource(serverLevel, pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(stack)); + } + // Paper end - Add PlayerShearBlockEvent level.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.BEEHIVE_SHEAR, SoundSource.BLOCKS, 1.0F, 1.0F); - stack.hurtAndBreak(1, player, hand.asEquipmentSlot()); - flag = true; + itemStack.hurtAndBreak(1, player, hand.asEquipmentSlot()); + hiveEmptied = true; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BellBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BellBlock.java.patch index 2a870fa798e5..9632592e47d2 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BellBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BellBlock.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/level/block/BellBlock.java +++ b/net/minecraft/world/level/block/BellBlock.java -@@ -137,6 +_,11 @@ +@@ -143,6 +_,11 @@ direction = level.getBlockState(pos).getValue(FACING); } + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBellRingEvent(level, pos, direction, entity)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBellRingEvent(level, pos, direction, ringingEntity)) { + return false; + } + // CraftBukkit end ((BellBlockEntity)blockEntity).onHit(direction); level.playSound(null, pos, SoundEvents.BELL_BLOCK, SoundSource.BLOCKS, 2.0F, 1.0F); - level.gameEvent(entity, GameEvent.BLOCK_CHANGE, pos); + level.gameEvent(ringingEntity, GameEvent.BLOCK_CHANGE, pos); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BigDripleafBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BigDripleafBlock.java.patch index c1d513fdc879..cdec491e2988 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BigDripleafBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BigDripleafBlock.java.patch @@ -1,18 +1,18 @@ --- a/net/minecraft/world/level/block/BigDripleafBlock.java +++ b/net/minecraft/world/level/block/BigDripleafBlock.java -@@ -126,7 +_,7 @@ +@@ -127,7 +_,7 @@ @Override - protected void onProjectileHit(Level level, BlockState state, BlockHitResult hit, Projectile projectile) { -- this.setTiltAndScheduleTick(state, level, hit.getBlockPos(), Tilt.FULL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN); -+ this.setTiltAndScheduleTick(state, level, hit.getBlockPos(), Tilt.FULL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN, projectile); // CraftBukkit + protected void onProjectileHit(final Level level, final BlockState state, final BlockHitResult blockHit, final Projectile projectile) { +- this.setTiltAndScheduleTick(state, level, blockHit.getBlockPos(), Tilt.FULL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN); ++ this.setTiltAndScheduleTick(state, level, blockHit.getBlockPos(), Tilt.FULL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN, projectile); // CraftBukkit } @Override -@@ -189,9 +_,23 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -195,9 +_,23 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (!level.isClientSide()) { if (state.getValue(TILT) == Tilt.NONE && canEntityTilt(pos, entity) && !level.hasNeighborSignal(pos)) { @@ -34,7 +34,7 @@ } } } -@@ -203,9 +_,9 @@ +@@ -209,9 +_,9 @@ } else { Tilt tilt = state.getValue(TILT); if (tilt == Tilt.UNSTABLE) { @@ -46,13 +46,13 @@ } else if (tilt == Tilt.FULL) { resetTilt(state, level, pos); } -@@ -228,8 +_,11 @@ +@@ -236,8 +_,11 @@ return entity.onGround() && entity.position().y > pos.getY() + 0.6875F; } -- private void setTiltAndScheduleTick(BlockState state, Level level, BlockPos pos, Tilt tilt, @Nullable SoundEvent sound) { +- private void setTiltAndScheduleTick(final BlockState state, final Level level, final BlockPos pos, final Tilt tilt, final @Nullable SoundEvent sound) { - setTilt(state, level, pos, tilt); -+ private void setTiltAndScheduleTick(BlockState state, Level level, BlockPos pos, Tilt tilt, @Nullable SoundEvent sound, @Nullable Entity entity) { ++ private void setTiltAndScheduleTick(final BlockState state, final Level level, final BlockPos pos, final Tilt tilt, final @Nullable SoundEvent sound, @Nullable Entity entity) { + if (!setTilt(state, level, pos, tilt, entity)) { + return; + } @@ -60,10 +60,10 @@ if (sound != null) { playTiltSound(level, pos, sound); } -@@ -241,18 +_,26 @@ +@@ -249,18 +_,26 @@ } - private static void resetTilt(BlockState state, Level level, BlockPos pos) { + private static void resetTilt(final BlockState state, final Level level, final BlockPos pos) { - setTilt(state, level, pos, Tilt.NONE); + setTilt(state, level, pos, Tilt.NONE, null); // CraftBukkit if (state.getValue(TILT) != Tilt.NONE) { @@ -71,18 +71,18 @@ } } -- private static void setTilt(BlockState state, Level level, BlockPos pos, Tilt tilt) { +- private static void setTilt(final BlockState state, final Level level, final BlockPos pos, final Tilt tilt) { + // CraftBukkit start -+ private static boolean setTilt(BlockState state, Level level, BlockPos pos, Tilt tilt, @Nullable Entity entity) { ++ private static boolean setTilt(final BlockState state, final Level level, final BlockPos pos, final Tilt tilt, @Nullable Entity entity) { + if (entity != null) { + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, state.setValue(BigDripleafBlock.TILT, tilt))) { + return false; + } + } + // CraftBukkit end - Tilt tilt1 = state.getValue(TILT); + Tilt previousTilt = state.getValue(TILT); level.setBlock(pos, state.setValue(TILT, tilt), Block.UPDATE_CLIENTS); - if (tilt.causesVibration() && tilt != tilt1) { + if (tilt.causesVibration() && tilt != previousTilt) { level.gameEvent(null, GameEvent.BLOCK_CHANGE, pos); } + return true; // CraftBukkit diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BlastFurnaceBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BlastFurnaceBlock.java.patch index 26f43d832a90..bb2f89f0f519 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BlastFurnaceBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BlastFurnaceBlock.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/level/block/BlastFurnaceBlock.java @@ -44,8 +_,7 @@ @Override - protected void openContainer(Level level, BlockPos pos, Player player) { + protected void openContainer(final Level level, final BlockPos pos, final Player player) { BlockEntity blockEntity = level.getBlockEntity(pos); - if (blockEntity instanceof BlastFurnaceBlockEntity) { - player.openMenu((MenuProvider)blockEntity); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/Block.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/Block.java.patch index ed028c654c30..c2808c1ed09f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/Block.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/Block.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/Block.java +++ b/net/minecraft/world/level/block/Block.java -@@ -88,29 +_,46 @@ +@@ -89,29 +_,46 @@ return !Shapes.joinIsNotEmpty(Shapes.block(), shape, BooleanOp.NOT_SAME); } }); @@ -65,19 +65,19 @@ private @Nullable Item item; private static final int CACHE_SIZE = 256; private static final ThreadLocal> OCCLUSION_CACHE = ThreadLocal.withInitial(() -> { -@@ -366,6 +_,27 @@ - return state.getDrops(builder); +@@ -382,6 +_,27 @@ + return state.getDrops(params); } + // Paper start - Add BlockBreakBlockEvent -+ public static boolean dropResources(BlockState state, LevelAccessor levelAccessor, BlockPos pos, @Nullable BlockEntity blockEntity, BlockPos source) { -+ if (levelAccessor instanceof ServerLevel serverLevel) { ++ public static boolean dropResources(final BlockState state, final Level level, final BlockPos pos, final @Nullable BlockEntity blockEntity, final BlockPos source) { ++ if (level instanceof ServerLevel serverLevel) { + List items = new java.util.ArrayList<>(); + for (ItemStack drop : Block.getDrops(state, serverLevel, pos, blockEntity)) { + items.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(drop)); + } + Block block = state.getBlock(); // Paper - Properly handle xp dropping -+ io.papermc.paper.event.block.BlockBreakBlockEvent event = new io.papermc.paper.event.block.BlockBreakBlockEvent(org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, pos), org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, source), items); ++ io.papermc.paper.event.block.BlockBreakBlockEvent event = new io.papermc.paper.event.block.BlockBreakBlockEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.craftbukkit.block.CraftBlock.at(level, source), items); + event.setExpToDrop(block.getExpDrop(state, serverLevel, pos, net.minecraft.world.item.ItemStack.EMPTY, true)); // Paper - Properly handle xp dropping + event.callEvent(); + for (org.bukkit.inventory.ItemStack drop : event.getDrops()) { @@ -90,41 +90,51 @@ + } + // Paper end - Add BlockBreakBlockEvent + - public static void dropResources(BlockState state, Level level, BlockPos pos) { - if (level instanceof ServerLevel) { - getDrops(state, (ServerLevel)level, pos, null).forEach(stack -> popResource(level, pos, stack)); -@@ -381,9 +_,14 @@ + public static void dropResources(final BlockState state, final Level level, final BlockPos pos) { + if (level instanceof ServerLevel serverLevel) { + getDrops(state, serverLevel, pos, null).forEach(stack -> popResource(level, pos, stack)); +@@ -396,6 +_,12 @@ + } } - public static void dropResources(BlockState state, Level level, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool) { -+ // Paper start - Properly handle xp dropping -+ dropResources(state, level, pos, blockEntity, entity, tool, true); ++ // Paper start - Properly handle xp dropping ++ public static void dropResources(final BlockState state, final Level level, final BlockPos pos, final @Nullable BlockEntity blockEntity, final @Nullable Entity breaker, final ItemStack tool) { ++ dropResources(state, level, pos, blockEntity, breaker, tool, true); + } -+ public static void dropResources(BlockState state, Level level, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool, boolean dropExperience) { -+ // Paper end - Properly handle xp dropping - if (level instanceof ServerLevel) { - getDrops(state, (ServerLevel)level, pos, blockEntity, entity, tool).forEach(stack -> popResource(level, pos, stack)); -- state.spawnAfterBreak((ServerLevel)level, pos, tool, true); -+ state.spawnAfterBreak((ServerLevel)level, pos, tool, dropExperience); // Paper - Properly handle xp dropping ++ // Paper end - Properly handle xp dropping ++ + public static void dropResources( + final BlockState state, + final Level level, +@@ -403,10 +_,11 @@ + final @Nullable BlockEntity blockEntity, + final @Nullable Entity breaker, + final ItemStack tool ++ , final boolean dropExperience // Paper - Properly handle xp dropping + ) { + if (level instanceof ServerLevel serverLevel) { + getDrops(state, serverLevel, pos, blockEntity, breaker, tool).forEach(stack -> popResource(level, pos, stack)); +- state.spawnAfterBreak(serverLevel, pos, tool, true); ++ state.spawnAfterBreak(serverLevel, pos, tool, dropExperience); // Paper - Properly handle xp dropping } } -@@ -414,13 +_,25 @@ - if (level instanceof ServerLevel serverLevel && !stack.isEmpty() && serverLevel.getGameRules().get(GameRules.BLOCK_DROPS)) { - ItemEntity itemEntity = itemEntitySupplier.get(); - itemEntity.setDefaultPickUpDelay(); -- level.addFreshEntity(itemEntity); +@@ -439,13 +_,25 @@ + if (level instanceof ServerLevel serverLevel && !itemStack.isEmpty() && serverLevel.getGameRules().get(GameRules.BLOCK_DROPS)) { + ItemEntity entity = entityFactory.get(); + entity.setDefaultPickUpDelay(); +- level.addFreshEntity(entity); + // CraftBukkit start + if (level.captureDrops != null) { -+ level.captureDrops.add(itemEntity); ++ level.captureDrops.add(entity); + } else { -+ level.addFreshEntity(itemEntity); ++ level.addFreshEntity(entity); + } + // CraftBukkit end } } - public void popExperience(ServerLevel level, BlockPos pos, int amount) { + public void popExperience(final ServerLevel level, final BlockPos pos, final int amount) { + // Paper start - add entity parameter + popExperience(level, pos, amount, null); + } @@ -137,53 +147,61 @@ } } -@@ -438,10 +_,19 @@ +@@ -463,6 +_,13 @@ return this.defaultBlockState(); } -+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - fix drops not preventing stats/food exhaustion - public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) { -+ // Paper start - fix drops not preventing stats/food exhaustion -+ this.playerDestroy(level, player, pos, state, blockEntity, tool, true, true); ++ // Paper start - fix drops not preventing stats/food exhaustion ++ @Deprecated @io.papermc.paper.annotation.DoNotUse ++ public void playerDestroy(final Level level, final Player player, final BlockPos pos, final BlockState state, final @Nullable BlockEntity blockEntity, final ItemStack destroyedWith) { ++ playerDestroy(level, player, pos, state, blockEntity, destroyedWith, true, true); + } ++ // Paper end - fix drops not preventing stats/food exhaustion + -+ public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool, boolean includeDrops, boolean dropExp) { -+ // Paper end - fix drops not preventing stats/food exhaustion + public void playerDestroy( + final Level level, + final Player player, +@@ -470,10 +_,13 @@ + final BlockState state, + final @Nullable BlockEntity blockEntity, + final ItemStack destroyedWith ++ , final boolean includeDrops, final boolean dropExp // Paper - fix drops not preventing stats/food exhaustion + ) { player.awardStat(Stats.BLOCK_MINED.get(this)); - player.causeFoodExhaustion(0.005F); -- dropResources(state, level, pos, blockEntity, player, tool); +- dropResources(state, level, pos, blockEntity, player, destroyedWith); + player.causeFoodExhaustion(0.005F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.BLOCK_MINED); // CraftBukkit - EntityExhaustionEvent + if (includeDrops) { // Paper - fix drops not preventing stats/food exhaustion -+ Block.dropResources(state, level, pos, blockEntity, player, tool, dropExp); // Paper - Properly handle xp dropping ++ dropResources(state, level, pos, blockEntity, player, destroyedWith, dropExp); // Paper - Properly handle xp dropping + } // Paper - fix drops not preventing stats/food exhaustion } - public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { -@@ -580,12 +_,20 @@ + public void setPlacedBy(final Level level, final BlockPos pos, final BlockState state, final @Nullable LivingEntity by, final ItemStack itemStack) { +@@ -613,12 +_,20 @@ return this.builtInRegistryHolder; } -- protected void tryDropExperience(ServerLevel level, BlockPos pos, ItemStack heldItem, IntProvider amount) { -+ protected int tryDropExperience(ServerLevel level, BlockPos pos, ItemStack heldItem, IntProvider amount) { // CraftBukkit - int i = EnchantmentHelper.processBlockExperience(level, heldItem, amount.sample(level.getRandom())); - if (i > 0) { -- this.popExperience(level, pos, i); +- protected void tryDropExperience(final ServerLevel level, final BlockPos pos, final ItemStack tool, final IntProvider xpRange) { ++ protected int tryDropExperience(final ServerLevel level, final BlockPos pos, final ItemStack tool, final IntProvider xpRange) { // CraftBukkit + int experience = EnchantmentHelper.processBlockExperience(level, tool, xpRange.sample(level.getRandom())); + if (experience > 0) { +- this.popExperience(level, pos, experience); + // CraftBukkit start -+ //this.popExperience(level, pos, i); -+ return i; ++ // this.popExperience(level, pos, experience); ++ return experience; } - } + return 0; + } + -+ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { ++ public int getExpDrop(final BlockState state, final ServerLevel level, final BlockPos pos, final ItemStack tool, final boolean dropExperience) { + return 0; + } + // CraftBukkit end - record ShapePairKey(VoxelShape first, VoxelShape second) { + private record ShapePairKey(VoxelShape first, VoxelShape second) { @Override -@@ -601,6 +_,7 @@ +@@ -634,6 +_,7 @@ @Retention(RetentionPolicy.CLASS) @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.TYPE_USE}) diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BrewingStandBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BrewingStandBlock.java.patch index f2dadd1ca3a2..0b1eadfd9045 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BrewingStandBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BrewingStandBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/BrewingStandBlock.java +++ b/net/minecraft/world/level/block/BrewingStandBlock.java -@@ -63,8 +_,7 @@ - - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { +@@ -65,8 +_,7 @@ + protected InteractionResult useWithoutItem( + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { - if (!level.isClientSide() && level.getBlockEntity(pos) instanceof BrewingStandBlockEntity brewingStandBlockEntity) { - player.openMenu(brewingStandBlockEntity); + if (!level.isClientSide() && level.getBlockEntity(pos) instanceof BrewingStandBlockEntity brewingStandBlockEntity && player.openMenu(brewingStandBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BubbleColumnBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BubbleColumnBlock.java.patch index e111ecb76ce9..ff83d1baf3fc 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BubbleColumnBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BubbleColumnBlock.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/world/level/block/BubbleColumnBlock.java +++ b/net/minecraft/world/level/block/BubbleColumnBlock.java -@@ -49,6 +_,7 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -58,6 +_,7 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent - if (pastEdges) { - BlockState blockState = level.getBlockState(pos.above()); - boolean flag = blockState.getCollisionShape(level, pos).isEmpty() && blockState.getFluidState().isEmpty(); + if (isPrecise) { + BlockState stateAbove = level.getBlockState(pos.above()); + boolean nothingAbove = stateAbove.getCollisionShape(level, pos).isEmpty() && stateAbove.getFluidState().isEmpty(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BuddingAmethystBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BuddingAmethystBlock.java.patch index 5e5a9712ddef..5a9e22a2e0be 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BuddingAmethystBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BuddingAmethystBlock.java.patch @@ -1,15 +1,15 @@ --- a/net/minecraft/world/level/block/BuddingAmethystBlock.java +++ b/net/minecraft/world/level/block/BuddingAmethystBlock.java @@ -44,7 +_,13 @@ - BlockState blockState1 = block.defaultBlockState() - .setValue(AmethystClusterBlock.FACING, direction) - .setValue(AmethystClusterBlock.WATERLOGGED, blockState.getFluidState().getType() == Fluids.WATER); -- level.setBlockAndUpdate(blockPos, blockState1); + BlockState targetState = nextStage.defaultBlockState() + .setValue(AmethystClusterBlock.FACING, growDirection) + .setValue(AmethystClusterBlock.WATERLOGGED, relativeState.getFluidState().is(Fluids.WATER)); +- level.setBlockAndUpdate(growPos, targetState); + // Paper start - Have Amethyst throw both spread and grow events -+ if (block == Blocks.SMALL_AMETHYST_BUD) { -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, blockState1, Block.UPDATE_ALL); // CraftBukkit ++ if (nextStage == Blocks.SMALL_AMETHYST_BUD) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, growPos, targetState, Block.UPDATE_ALL); // CraftBukkit + } else { -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, blockPos, blockState1, Block.UPDATE_ALL); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, growPos, targetState, Block.UPDATE_ALL); + } + // Paper end - Have Amethyst throw both spread and grow events } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ButtonBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ButtonBlock.java.patch index 245dc1efd101..380cc51d44f3 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ButtonBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ButtonBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/ButtonBlock.java +++ b/net/minecraft/world/level/block/ButtonBlock.java -@@ -89,6 +_,19 @@ +@@ -92,6 +_,19 @@ if (state.getValue(POWERED)) { return InteractionResult.CONSUME; } else { @@ -20,22 +20,22 @@ this.press(state, level, pos, player); return InteractionResult.SUCCESS; } -@@ -150,6 +_,7 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -162,6 +_,7 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (!level.isClientSide() && this.type.canButtonBeActivatedByArrows() && !state.getValue(POWERED)) { this.checkPressed(state, level, pos); } -@@ -161,7 +_,31 @@ +@@ -173,7 +_,31 @@ : null; - boolean flag = abstractArrow != null; - boolean poweredValue = state.getValue(POWERED); + boolean shouldBePressed = firstArrow != null; + boolean wasPressed = state.getValue(POWERED); + // CraftBukkit start - Call interact event when arrows turn on wooden buttons -+ if (poweredValue != flag && flag) { ++ if (wasPressed != shouldBePressed && shouldBePressed) { + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); -+ org.bukkit.event.entity.EntityInteractEvent event = new org.bukkit.event.entity.EntityInteractEvent(abstractArrow.getBukkitEntity(), block); ++ org.bukkit.event.entity.EntityInteractEvent event = new org.bukkit.event.entity.EntityInteractEvent(firstArrow.getBukkitEntity(), block); + level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { @@ -43,9 +43,9 @@ + } + } + // CraftBukkit end - if (flag != poweredValue) { + if (shouldBePressed != wasPressed) { + // CraftBukkit start -+ boolean powered = poweredValue; ++ boolean powered = wasPressed; + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + int old = (powered) ? 15 : 0; + int current = (!powered) ? 15 : 0; @@ -53,10 +53,10 @@ + org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(block, old, current); + level.getCraftServer().getPluginManager().callEvent(eventRedstone); + -+ if ((flag && eventRedstone.getNewCurrent() <= 0) || (!flag && eventRedstone.getNewCurrent() > 0)) { ++ if ((shouldBePressed && eventRedstone.getNewCurrent() <= 0) || (!shouldBePressed && eventRedstone.getNewCurrent() > 0)) { + return; + } + // CraftBukkit end - level.setBlock(pos, state.setValue(POWERED, flag), Block.UPDATE_ALL); + level.setBlock(pos, state.setValue(POWERED, shouldBePressed), Block.UPDATE_ALL); this.updateNeighbours(state, level, pos); - this.playSound(null, level, pos, flag); + this.playSound(null, level, pos, shouldBePressed); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch index 1aa3214d37bb..7aba4167643a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch @@ -1,38 +1,33 @@ --- a/net/minecraft/world/level/block/CactusBlock.java +++ b/net/minecraft/world/level/block/CactusBlock.java -@@ -58,18 +_,22 @@ - int ageValue = state.getValue(AGE); +@@ -58,18 +_,18 @@ + int age = state.getValue(AGE); - while (level.getBlockState(pos.below(i)).is(this)) { -- if (++i == 3 && ageValue == 15) { -+ if (++i == level.paperConfig().maxGrowthHeight.cactus && ageValue == 15) { // Paper - Configurable cactus/bamboo/reed growth height + while (level.getBlockState(pos.below(height)).is(this)) { +- if (++height == 3 && age == 15) { ++ if (++height == level.paperConfig().maxGrowthHeight.cactus && age == 15) { // Paper - Configurable cactus/bamboo/reed growth height return; } } - if (ageValue == 8 && this.canSurvive(this.defaultBlockState(), level, pos.above())) { -- double d = i >= 3 ? 0.25 : 0.1; -+ double d = i >= level.paperConfig().maxGrowthHeight.cactus ? 0.25 : 0.1; // Paper - Configurable cactus/bamboo/reed growth height - if (random.nextDouble() <= d) { -- level.setBlockAndUpdate(blockPos, Blocks.CACTUS_FLOWER.defaultBlockState()); -- } -- } else if (ageValue == 15 && i < 3) { -- level.setBlockAndUpdate(blockPos, this.defaultBlockState()); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, blockPos, Blocks.CACTUS_FLOWER.defaultBlockState(), Block.UPDATE_ALL); -+ } -+ } else if (ageValue == 15 && i < level.paperConfig().maxGrowthHeight.cactus) { // Paper - Configurable cactus/bamboo/reed growth height -+ // Paper start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, blockPos, this.defaultBlockState(), Block.UPDATE_ALL)) { -+ return; -+ } -+ // Paper end - BlockState blockState = state.setValue(AGE, 0); - level.setBlock(pos, blockState, Block.UPDATE_NONE); - level.neighborChanged(blockState, blockPos, this, null, false); -@@ -124,7 +_,8 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { + if (age == 8 && this.canSurvive(this.defaultBlockState(), level, pos.above())) { +- double chanceToGrowFlower = height >= 3 ? 0.25 : 0.1; ++ double chanceToGrowFlower = height >= level.paperConfig().maxGrowthHeight.cactus ? 0.25 : 0.1; // Paper - Configurable cactus/bamboo/reed growth height + if (random.nextDouble() <= chanceToGrowFlower) { +- level.setBlockAndUpdate(above, Blocks.CACTUS_FLOWER.defaultBlockState()); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, above, Blocks.CACTUS_FLOWER.defaultBlockState(), Block.UPDATE_ALL); // Paper - block grow event + } +- } else if (age == 15 && height < 3) { +- level.setBlockAndUpdate(above, this.defaultBlockState()); ++ } else if (age == 15 && height < level.paperConfig().maxGrowthHeight.cactus) { // Paper - Configurable cactus/bamboo/reed growth height ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, above, this.defaultBlockState(), Block.UPDATE_ALL)) return; // Paper - block grow event + BlockState aboveBlock = state.setValue(AGE, 0); + level.setBlock(pos, aboveBlock, Block.UPDATE_NONE); + level.neighborChanged(aboveBlock, above, this, null, false); +@@ -131,7 +_,8 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { - entity.hurt(level.damageSources().cactus(), 1.0F); + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + entity.hurt(level.damageSources().cactus().eventBlockDamager(level, pos), 1.0F); // CraftBukkit diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch index c0319559ed1e..44157946d3de 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch @@ -1,25 +1,25 @@ --- a/net/minecraft/world/level/block/CakeBlock.java +++ b/net/minecraft/world/level/block/CakeBlock.java -@@ -57,6 +_,12 @@ +@@ -63,6 +_,12 @@ ) { - Item item = stack.getItem(); - if (stack.is(ItemTags.CANDLES) && state.getValue(BITES) == 0 && Block.byItem(item) instanceof CandleBlock candleBlock) { + Item item = itemStack.getItem(); + if (itemStack.is(ItemTags.CANDLES) && state.getValue(BITES) == 0 && Block.byItem(item) instanceof CandleBlock candleBlock) { + // Paper start - call change block event + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, CandleCakeBlock.byCandle(candleBlock))) { + player.containerMenu.forceHeldSlot(hand); // update inv because candle could decrease + return InteractionResult.TRY_WITH_EMPTY_HAND; + } + // Paper end - call change block event - stack.consume(1, player); + itemStack.consume(1, player); level.playSound(null, pos, SoundEvents.CAKE_ADD_CANDLE, SoundSource.BLOCKS, 1.0F, 1.0F); level.setBlockAndUpdate(pos, CandleCakeBlock.byCandle(candleBlock)); -@@ -87,9 +_,28 @@ +@@ -95,9 +_,28 @@ if (!player.canEat(false)) { return InteractionResult.PASS; } else { + // Paper start - call change block event -+ int bitesValue = state.getValue(CakeBlock.BITES); -+ final BlockState newState = bitesValue < MAX_BITES ? state.setValue(CakeBlock.BITES, bitesValue + 1) : level.getFluidState(pos).createLegacyBlock(); ++ int bites = state.getValue(CakeBlock.BITES); ++ final BlockState newState = bites < MAX_BITES ? state.setValue(CakeBlock.BITES, bites + 1) : level.getFluidState(pos).createLegacyBlock(); + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, newState)) { + ((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity().sendHealthUpdate(); + return InteractionResult.PASS; // return a non-consume result to cake blocks don't drop their candles @@ -27,9 +27,10 @@ + // Paper end - call change block event player.awardStat(Stats.EAT_CAKE_SLICE); - player.getFoodData().eat(2, 0.1F); -- int bitesValue = state.getValue(BITES); +- int bites = state.getValue(BITES); + // CraftBukkit start + // player.getFoodData().eat(2, 0.1F); ++ // int bites = state.getValue(BITES); // Paper - move up + int oldFoodLevel = player.getFoodData().foodLevel; + + org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(player, 2 + oldFoodLevel); @@ -40,7 +41,6 @@ + + ((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity().sendHealthUpdate(); + // CraftBukkit end -+ // Paper - move up level.gameEvent(player, GameEvent.EAT, pos); - if (bitesValue < 6) { - level.setBlock(pos, state.setValue(BITES, bitesValue + 1), Block.UPDATE_ALL); + if (bites < 6) { + level.setBlock(pos, state.setValue(BITES, bites + 1), Block.UPDATE_ALL); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CampfireBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CampfireBlock.java.patch index ba3a3eb68533..c21b9f52a73c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CampfireBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CampfireBlock.java.patch @@ -1,25 +1,25 @@ --- a/net/minecraft/world/level/block/CampfireBlock.java +++ b/net/minecraft/world/level/block/CampfireBlock.java -@@ -107,8 +_,9 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -120,8 +_,9 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (state.getValue(LIT) && entity instanceof LivingEntity) { - entity.hurt(level.damageSources().campfire(), this.fireDamage); + entity.hurt(level.damageSources().campfire().eventBlockDamager(level, pos), this.fireDamage); // CraftBukkit } - super.entityInside(state, level, pos, entity, effectApplier, pastEdges); -@@ -219,6 +_,11 @@ - && projectile.mayInteract(serverLevel, blockPos) + super.entityInside(state, level, pos, entity, effectApplier, isPrecise); +@@ -232,6 +_,11 @@ + && projectile.mayInteract(serverLevel, pos) && !state.getValue(LIT) && !state.getValue(WATERLOGGED)) { + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, blockPos, projectile).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, pos, projectile).isCancelled()) { + return; + } + // CraftBukkit end - level.setBlock(blockPos, state.setValue(BlockStateProperties.LIT, true), Block.UPDATE_ALL_IMMEDIATE); + level.setBlock(pos, state.setValue(BlockStateProperties.LIT, true), Block.UPDATE_ALL_IMMEDIATE); } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CartographyTableBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CartographyTableBlock.java.patch index abb169e797f7..6fed31ca1cce 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CartographyTableBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CartographyTableBlock.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/level/block/CartographyTableBlock.java +++ b/net/minecraft/world/level/block/CartographyTableBlock.java -@@ -32,8 +_,9 @@ - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { +@@ -34,8 +_,9 @@ + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { if (!level.isClientSide()) { - player.openMenu(state.getMenuProvider(level, pos)); + if (player.openMenu(state.getMenuProvider(level, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CarvedPumpkinBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CarvedPumpkinBlock.java.patch index 1536368b6288..53f2111574c8 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CarvedPumpkinBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CarvedPumpkinBlock.java.patch @@ -3,18 +3,18 @@ @@ -87,6 +_,7 @@ CopperGolem copperGolem = EntityType.COPPER_GOLEM.create(level, EntitySpawnReason.TRIGGERED); if (copperGolem != null) { - spawnGolemInWorld(level, blockPatternMatch2, copperGolem, blockPatternMatch2.getBlock(0, 0, 0).getPos()); + spawnGolemInWorld(level, copperGolemMatch, copperGolem, copperGolemMatch.getBlock(0, 0, 0).getPos()); + if (!copperGolem.valid) return; // Paper - entityspawnevent - entity was not added to the world so prevent world mutation - this.replaceCopperBlockWithChest(level, blockPatternMatch2); - copperGolem.spawn(this.getWeatherStateFromPattern(blockPatternMatch2)); + this.replaceCopperBlockWithChest(level, copperGolemMatch); + copperGolem.spawn(this.getWeatherStateFromPattern(copperGolemMatch)); } @@ -105,9 +_,22 @@ } - private static void spawnGolemInWorld(Level level, BlockPattern.BlockPatternMatch patternMatch, Entity golem, BlockPos pos) { -- clearPatternBlocks(level, patternMatch); + private static void spawnGolemInWorld(final Level level, final BlockPattern.BlockPatternMatch match, final Entity golem, final BlockPos spawnPos) { +- clearPatternBlocks(level, match); + // clearPatternBlocks(level, patternMatch); // Paper - moved down - golem.snapTo(pos.getX() + 0.5, pos.getY() + 0.05, pos.getZ() + 0.5, 0.0F, 0.0F); + golem.snapTo(spawnPos.getX() + 0.5, spawnPos.getY() + 0.05, spawnPos.getZ() + 0.5, 0.0F, 0.0F); - level.addFreshEntity(golem); + // Paper start + org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; @@ -29,7 +29,7 @@ + return; + } + // Paper end -+ clearPatternBlocks(level, patternMatch); // Paper - from above ++ clearPatternBlocks(level, match); // Paper - from above - for (ServerPlayer serverPlayer : level.getEntitiesOfClass(ServerPlayer.class, golem.getBoundingBox().inflate(5.0))) { - CriteriaTriggers.SUMMONED_ENTITY.trigger(serverPlayer, golem); + for (ServerPlayer player : level.getEntitiesOfClass(ServerPlayer.class, golem.getBoundingBox().inflate(5.0))) { + CriteriaTriggers.SUMMONED_ENTITY.trigger(player, golem); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CauldronBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CauldronBlock.java.patch index dd650a6c3c59..9b0fdf230d69 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CauldronBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CauldronBlock.java.patch @@ -1,46 +1,46 @@ --- a/net/minecraft/world/level/block/CauldronBlock.java +++ b/net/minecraft/world/level/block/CauldronBlock.java -@@ -40,9 +_,19 @@ - public void handlePrecipitation(BlockState state, Level level, BlockPos pos, Biome.Precipitation precipitation) { +@@ -40,10 +_,18 @@ + public void handlePrecipitation(final BlockState state, final Level level, final BlockPos pos, final Biome.Precipitation precipitation) { if (shouldHandlePrecipitation(level, precipitation)) { if (precipitation == Biome.Precipitation.RAIN) { +- level.setBlockAndUpdate(pos, Blocks.WATER_CAULDRON.defaultBlockState()); + // Paper start - Call CauldronLevelChangeEvent -+ if (!LayeredCauldronBlock.changeLevel(level, pos, Blocks.WATER_CAULDRON.defaultBlockState(), null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL, false)) { // avoid duplicate game event ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, Blocks.WATER_CAULDRON.defaultBlockState(), null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { // avoid duplicate game event + return; + } + // Paper end - Call CauldronLevelChangeEvent - level.setBlockAndUpdate(pos, Blocks.WATER_CAULDRON.defaultBlockState()); level.gameEvent(null, GameEvent.BLOCK_CHANGE, pos); } else if (precipitation == Biome.Precipitation.SNOW) { +- level.setBlockAndUpdate(pos, Blocks.POWDER_SNOW_CAULDRON.defaultBlockState()); + // Paper start - Call CauldronLevelChangeEvent -+ if (!LayeredCauldronBlock.changeLevel(level, pos, Blocks.POWDER_SNOW_CAULDRON.defaultBlockState(), null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL, false)) { // avoid duplicate game event ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, Blocks.POWDER_SNOW_CAULDRON.defaultBlockState(), null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { // avoid duplicate game event + return; + } + // Paper end - Call CauldronLevelChangeEvent - level.setBlockAndUpdate(pos, Blocks.POWDER_SNOW_CAULDRON.defaultBlockState()); level.gameEvent(null, GameEvent.BLOCK_CHANGE, pos); } -@@ -58,13 +_,19 @@ - protected void receiveStalactiteDrip(BlockState state, Level level, BlockPos pos, Fluid fluid) { + } +@@ -58,12 +_,20 @@ + protected void receiveStalactiteDrip(final BlockState state, final Level level, final BlockPos pos, final Fluid fluid) { if (fluid == Fluids.WATER) { - BlockState blockState = Blocks.WATER_CAULDRON.defaultBlockState(); -- level.setBlockAndUpdate(pos, blockState); -- level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(blockState)); + BlockState newState = Blocks.WATER_CAULDRON.defaultBlockState(); +- level.setBlockAndUpdate(pos, newState); + // Paper start - Call CauldronLevelChangeEvent; don't send level event or game event if cancelled -+ if (!LayeredCauldronBlock.changeLevel(level, pos, blockState, null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { // CraftBukkit ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, newState, null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { // CraftBukkit + return; + } + // Paper end - Call CauldronLevelChangeEvent + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newState)); level.levelEvent(LevelEvent.SOUND_DRIP_WATER_INTO_CAULDRON, pos, 0); } else if (fluid == Fluids.LAVA) { - BlockState blockState = Blocks.LAVA_CAULDRON.defaultBlockState(); -- level.setBlockAndUpdate(pos, blockState); -- level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(blockState)); + BlockState newState = Blocks.LAVA_CAULDRON.defaultBlockState(); +- level.setBlockAndUpdate(pos, newState); + // Paper start - Call CauldronLevelChangeEvent; don't send level event or game event if cancelled -+ if (!LayeredCauldronBlock.changeLevel(level, pos, blockState, null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { // CraftBukkit ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, newState, null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { // CraftBukkit + return; + } + // Paper end - Call CauldronLevelChangeEvent + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newState)); level.levelEvent(LevelEvent.SOUND_DRIP_LAVA_INTO_CAULDRON, pos, 0); } - } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch index deaefa83917c..a6825d1f7b45 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/block/CaveVines.java +++ b/net/minecraft/world/level/block/CaveVines.java @@ -23,6 +_,12 @@ - static InteractionResult use(Entity entity, BlockState state, Level level, BlockPos pos) { + static InteractionResult use(final Entity sourceEntity, final BlockState state, final Level level, final BlockPos pos) { if (state.getValue(BERRIES)) { if (level instanceof ServerLevel serverLevel) { + // CraftBukkit start - call entity change block event -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, state.setValue(CaveVines.BERRIES, false))) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(sourceEntity, pos, state.setValue(CaveVines.BERRIES, false))) { + return InteractionResult.SUCCESS; + } + // CraftBukkit end - call entity change block event @@ -16,12 +16,12 @@ @@ -30,8 +_,25 @@ level.getBlockEntity(pos), null, - entity, -- (serverLevel1, itemStack) -> Block.popResource(serverLevel1, pos, itemStack) -+ (serverLevel1, itemStack) -> drops.add(itemStack) // Paper - call player harvest block event - store drops from loottable + sourceEntity, +- (serverlvl, itemStack) -> Block.popResource(serverlvl, pos, itemStack) ++ (serverlvl, itemStack) -> drops.add(itemStack) // Paper - call player harvest block event - store drops from loottable ); + // Paper start - call player harvest block event -+ if (entity instanceof net.minecraft.world.entity.player.Player player) { ++ if (sourceEntity instanceof net.minecraft.world.entity.player.Player player) { + org.bukkit.event.player.PlayerHarvestBlockEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerHarvestBlockEvent( + level, pos, player, net.minecraft.world.InteractionHand.MAIN_HAND, drops + ); @@ -37,6 +37,6 @@ + } + } + // Paper end - call player harvest block event - float f = Mth.randomBetween(serverLevel.random, 0.8F, 1.2F); - serverLevel.playSound(null, pos, SoundEvents.CAVE_VINES_PICK_BERRIES, SoundSource.BLOCKS, 1.0F, f); - BlockState blockState = state.setValue(BERRIES, false); + float pitch = Mth.randomBetween(serverLevel.getRandom(), 0.8F, 1.2F); + serverLevel.playSound(null, pos, SoundEvents.CAVE_VINES_PICK_BERRIES, SoundSource.BLOCKS, 1.0F, pitch); + BlockState newState = state.setValue(BERRIES, false); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CaveVinesBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CaveVinesBlock.java.patch index 332ca736380d..ca13ce4d6f2e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CaveVinesBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CaveVinesBlock.java.patch @@ -1,20 +1,21 @@ --- a/net/minecraft/world/level/block/CaveVinesBlock.java +++ b/net/minecraft/world/level/block/CaveVinesBlock.java -@@ -52,8 +_,15 @@ +@@ -52,8 +_,16 @@ @Override - protected BlockState getGrowIntoState(BlockState state, RandomSource random) { -- return super.getGrowIntoState(state, random).setValue(BERRIES, random.nextFloat() < 0.11F); + protected BlockState getGrowIntoState(final BlockState growFromState, final RandomSource random) { +- return super.getGrowIntoState(growFromState, random).setValue(BERRIES, random.nextFloat() < 0.11F); - } -+ // Paper start - Fix Spigot growth modifiers -+ return this.getGrowIntoState(state, random, null); ++ // Paper start - Fix Spigot growth modifiers ++ return this.getGrowIntoState(growFromState, random, null); + } ++ + @Override -+ protected BlockState getGrowIntoState(BlockState state, RandomSource random, @javax.annotation.Nullable Level level) { ++ protected BlockState getGrowIntoState(BlockState growFromState, RandomSource random, @org.jspecify.annotations.Nullable Level level) { + final boolean value = random.nextFloat() < (level != null ? (0.11F * (level.spigotConfig.glowBerryModifier / 100.0F)) : 0.11F); -+ return super.getGrowIntoState(state, random).setValue(CaveVinesBlock.BERRIES, value); ++ return super.getGrowIntoState(growFromState, random).setValue(CaveVinesBlock.BERRIES, value); + } + // Paper end - Fix Spigot growth modifiers @Override - protected ItemStack getCloneItemStack(LevelReader level, BlockPos pos, BlockState state, boolean includeData) { + protected ItemStack getCloneItemStack(final LevelReader level, final BlockPos pos, final BlockState state, final boolean includeData) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CeilingHangingSignBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CeilingHangingSignBlock.java.patch index 5acb1aad3f25..7b55fc8b18f8 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CeilingHangingSignBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CeilingHangingSignBlock.java.patch @@ -1,10 +1,11 @@ --- a/net/minecraft/world/level/block/CeilingHangingSignBlock.java +++ b/net/minecraft/world/level/block/CeilingHangingSignBlock.java -@@ -165,6 +_,6 @@ +@@ -177,7 +_,7 @@ @Override - public @Nullable BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { -- return createTickerHelper(blockEntityType, BlockEntityType.HANGING_SIGN, SignBlockEntity::tick); + public @Nullable BlockEntityTicker getTicker(final Level level, final BlockState blockState, final BlockEntityType type) { +- return createTickerHelper(type, BlockEntityType.HANGING_SIGN, SignBlockEntity::tick); + return null; // CraftBukkit - remove unnecessary sign ticking } - } + + @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ChangeOverTimeBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ChangeOverTimeBlock.java.patch index 8c1b3977c7be..cf23f85f585e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ChangeOverTimeBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ChangeOverTimeBlock.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/block/ChangeOverTimeBlock.java +++ b/net/minecraft/world/level/block/ChangeOverTimeBlock.java @@ -16,7 +_,7 @@ - default void changeOverTime(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { - float f = 0.05688889F; + default void changeOverTime(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { + float eachBlockOncePerDayChance = 0.05688889F; if (random.nextFloat() < 0.05688889F) { -- this.getNextState(state, level, pos, random).ifPresent(blockState -> level.setBlockAndUpdate(pos, blockState)); -+ this.getNextState(state, level, pos, random).ifPresent(blockState -> org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, pos, blockState, Block.UPDATE_ALL)); // CraftBukkit +- this.getNextState(state, level, pos, random).ifPresent(weatheredState -> level.setBlockAndUpdate(pos, weatheredState)); ++ this.getNextState(state, level, pos, random).ifPresent(weatheredState -> org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, pos, weatheredState, Block.UPDATE_ALL)); // CraftBukkit } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ChestBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ChestBlock.java.patch index af84505e929e..c774cd0cec40 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ChestBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ChestBlock.java.patch @@ -1,15 +1,15 @@ --- a/net/minecraft/world/level/block/ChestBlock.java +++ b/net/minecraft/world/level/block/ChestBlock.java -@@ -96,7 +_,7 @@ +@@ -97,7 +_,7 @@ @Override public Optional acceptDouble(final ChestBlockEntity first, final ChestBlockEntity second) { final Container container = new CompoundContainer(first, second); - return Optional.of(new MenuProvider() { -+ return Optional.of(DoubleInventory.wrap(new MenuProvider() { // CraftBukkit - wrap for identification - @Override - public @Nullable AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) { - if (first.canOpen(player) && second.canOpen(player)) { -@@ -117,10 +_,10 @@ ++ return Optional.of(org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest.Provider.wrap(new MenuProvider() { // CraftBukkit - wrap for identification + { + Objects.requireNonNull(container); + } +@@ -122,10 +_,10 @@ if (first.hasCustomName()) { return first.getDisplayName(); } else { @@ -22,43 +22,8 @@ } @Override -@@ -134,6 +_,34 @@ - } - }; - -+ // CraftBukkit start -+ public static class DoubleInventory implements MenuProvider { -+ -+ private final MenuProvider delegate; -+ public final CompoundContainer container; // expose to api -+ -+ private DoubleInventory(MenuProvider delegate, CompoundContainer container) { -+ this.delegate = delegate; -+ this.container = container; -+ } -+ -+ public static DoubleInventory wrap(MenuProvider delegate, CompoundContainer container) { -+ return new DoubleInventory(delegate, container); -+ } -+ -+ @Nullable -+ @Override -+ public AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player player) { -+ return this.delegate.createMenu(syncId, playerInventory, player); -+ } -+ -+ @Override -+ public Component getDisplayName() { -+ return this.delegate.getDisplayName(); -+ } -+ } -+ // CraftBukkit end -+ - @Override - public MapCodec codec() { - return CODEC; -@@ -261,8 +_,7 @@ - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { +@@ -268,8 +_,7 @@ + ) { if (level instanceof ServerLevel serverLevel) { MenuProvider menuProvider = this.getMenuProvider(state, level, pos); - if (menuProvider != null) { @@ -67,31 +32,30 @@ player.awardStat(this.getOpenChestStat()); PiglinAi.angerNearbyPiglins(serverLevel, player, true); } -@@ -299,7 +_,14 @@ +@@ -310,7 +_,13 @@ @Override - public @Nullable MenuProvider getMenuProvider(BlockState state, Level level, BlockPos pos) { + public @Nullable MenuProvider getMenuProvider(final BlockState state, final Level level, final BlockPos pos) { - return this.combine(state, level, pos, false).apply(MENU_PROVIDER_COMBINER).orElse(null); + // CraftBukkit start + return this.getMenuProvider(state, level, pos, false); + } + -+ @Nullable -+ public MenuProvider getMenuProvider(BlockState state, Level level, BlockPos pos, boolean ignoreObstructions) { ++ public @Nullable MenuProvider getMenuProvider(final BlockState state, final Level level, final BlockPos pos, final boolean ignoreObstructions) { + return this.combine(state, level, pos, ignoreObstructions).apply(MENU_PROVIDER_COMBINER).orElse(null); + // CraftBukkit end } - public static DoubleBlockCombiner.Combiner opennessCombiner(final LidBlockEntity lid) { -@@ -341,6 +_,11 @@ + public static DoubleBlockCombiner.Combiner opennessCombiner(final LidBlockEntity entity) { +@@ -352,6 +_,11 @@ } - private static boolean isCatSittingOnChest(LevelAccessor level, BlockPos pos) { + private static boolean isCatSittingOnChest(final LevelAccessor level, final BlockPos pos) { + // Paper start - Option to disable chest cat detection + if (level.getMinecraftWorld().paperConfig().entities.behavior.disableChestCatDetection) { + return false; + } + // Paper end - Option to disable chest cat detection - List entitiesOfClass = level.getEntitiesOfClass( - Cat.class, new AABB(pos.getX(), pos.getY() + 1, pos.getZ(), pos.getX() + 1, pos.getY() + 2, pos.getZ() + 1) - ); + List cats = level.getEntitiesOfClass(Cat.class, new AABB(pos.getX(), pos.getY() + 1, pos.getZ(), pos.getX() + 1, pos.getY() + 2, pos.getZ() + 1)); + if (!cats.isEmpty()) { + for (Cat cat : cats) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch index 18bfe77bae2f..05cb4af28069 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch @@ -1,68 +1,64 @@ --- a/net/minecraft/world/level/block/ChorusFlowerBlock.java +++ b/net/minecraft/world/level/block/ChorusFlowerBlock.java -@@ -96,8 +_,10 @@ +@@ -94,8 +_,10 @@ } - if (flag && allNeighborsEmpty(level, blockPos, null) && level.isEmptyBlock(pos.above(2))) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, this.defaultBlockState().setValue(AGE, ageValue), Block.UPDATE_CLIENTS)) { // CraftBukkit - add event + if (growUpwards && allNeighborsEmpty(level, above, null) && level.isEmptyBlock(pos.above(2))) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, above, this.defaultBlockState().setValue(AGE, currentAge), Block.UPDATE_CLIENTS)) { // CraftBukkit - add event level.setBlock(pos, ChorusPlantBlock.getStateWithConnections(level, pos, this.plant.defaultBlockState()), Block.UPDATE_CLIENTS); - this.placeGrownFlower(level, blockPos, ageValue); + this.placeGrownFlower(level, above, currentAge); + } // CraftBukkit - } else if (ageValue < 4) { - int i = random.nextInt(4); - if (flag1) { -@@ -112,30 +_,40 @@ - if (level.isEmptyBlock(blockPos1) - && level.isEmptyBlock(blockPos1.below()) - && allNeighborsEmpty(level, blockPos1, randomDirection.getOpposite())) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos1, this.defaultBlockState().setValue(AGE, ageValue + 1), Block.UPDATE_CLIENTS)) { // CraftBukkit - add event - this.placeGrownFlower(level, blockPos1, ageValue + 1); - flag2 = true; + } else if (currentAge < 4) { + int numBranchAttempts = random.nextInt(4); + if (pillarOnSupportBlock) { +@@ -108,30 +_,36 @@ + Direction direction = Direction.Plane.HORIZONTAL.getRandomDirection(random); + BlockPos target = pos.relative(direction); + if (level.isEmptyBlock(target) && level.isEmptyBlock(target.below()) && allNeighborsEmpty(level, target, direction.getOpposite())) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, target, this.defaultBlockState().setValue(AGE, currentAge + 1), Block.UPDATE_CLIENTS)) { // CraftBukkit - add event + this.placeGrownFlower(level, target, currentAge + 1); + createdBranch = true; + } // CraftBukkit } } - if (flag2) { + if (createdBranch) { level.setBlock(pos, ChorusPlantBlock.getStateWithConnections(level, pos, this.plant.defaultBlockState()), Block.UPDATE_CLIENTS); } else { -+ // CraftBukkit start - add event -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, this.defaultBlockState().setValue(AGE, 5), Block.UPDATE_CLIENTS)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, this.defaultBlockState().setValue(AGE, 5), Block.UPDATE_CLIENTS)) { // CraftBukkit - add event this.placeDeadFlower(level, pos); -+ } -+ // CraftBukkit end ++ } // CraftBukkit } } else { -+ // CraftBukkit start - add event -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, this.defaultBlockState().setValue(AGE, 5), Block.UPDATE_CLIENTS)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, this.defaultBlockState().setValue(AGE, 5), Block.UPDATE_CLIENTS)) { // CraftBukkit - add event this.placeDeadFlower(level, pos); -+ } -+ // CraftBukkit end ++ } // CraftBukkit } } } } - private void placeGrownFlower(Level level, BlockPos pos, int age) { + private void placeGrownFlower(final Level level, final BlockPos pos, final int age) { - level.setBlock(pos, this.defaultBlockState().setValue(AGE, age), Block.UPDATE_CLIENTS); + // level.setBlock(pos, this.defaultBlockState().setValue(AGE, age), Block.UPDATE_CLIENTS); // Paper - already done above in the event call level.levelEvent(LevelEvent.SOUND_CHORUS_GROW, pos, 0); } - private void placeDeadFlower(Level level, BlockPos pos) { + private void placeDeadFlower(final Level level, final BlockPos pos) { - level.setBlock(pos, this.defaultBlockState().setValue(AGE, 5), Block.UPDATE_CLIENTS); + // level.setBlock(pos, this.defaultBlockState().setValue(AGE, 5), Block.UPDATE_CLIENTS); // Paper - already done above in the event call level.levelEvent(LevelEvent.SOUND_CHORUS_DEATH, pos, 0); } -@@ -261,6 +_,11 @@ - protected void onProjectileHit(Level level, BlockState state, BlockHitResult hit, Projectile projectile) { - BlockPos blockPos = hit.getBlockPos(); - if (level instanceof ServerLevel serverLevel && projectile.mayInteract(serverLevel, blockPos) && projectile.mayBreak(serverLevel)) { +@@ -257,6 +_,11 @@ + protected void onProjectileHit(final Level level, final BlockState state, final BlockHitResult blockHit, final Projectile projectile) { + BlockPos pos = blockHit.getBlockPos(); + if (level instanceof ServerLevel serverLevel && projectile.mayInteract(serverLevel, pos) && projectile.mayBreak(serverLevel)) { + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(projectile, blockPos, state.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(projectile, pos, state.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state + return; + } + // CraftBukkit end - level.destroyBlock(blockPos, true, projectile); + level.destroyBlock(pos, true, projectile); } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ChorusPlantBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ChorusPlantBlock.java.patch index f73493ba87e9..79a13ee7978f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ChorusPlantBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ChorusPlantBlock.java.patch @@ -1,34 +1,34 @@ --- a/net/minecraft/world/level/block/ChorusPlantBlock.java +++ b/net/minecraft/world/level/block/ChorusPlantBlock.java -@@ -38,6 +_,7 @@ +@@ -39,6 +_,7 @@ @Override - public BlockState getStateForPlacement(BlockPlaceContext context) { + public BlockState getStateForPlacement(final BlockPlaceContext context) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableChorusPlantUpdates) return this.defaultBlockState(); // Paper - add option to disable block updates return getStateWithConnections(context.getLevel(), context.getClickedPos(), this.defaultBlockState()); } -@@ -68,6 +_,7 @@ - BlockState neighborState, - RandomSource random +@@ -69,6 +_,7 @@ + final BlockState neighbourState, + final RandomSource random ) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableChorusPlantUpdates) return state; // Paper - add option to disable block updates if (!state.canSurvive(level, pos)) { - scheduledTickAccess.scheduleTick(pos, this, 1); - return super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); -@@ -81,6 +_,7 @@ + ticks.scheduleTick(pos, this, 1); + return super.updateShape(state, level, ticks, pos, directionToNeighbour, neighbourPos, neighbourState, random); +@@ -82,6 +_,7 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableChorusPlantUpdates) return; // Paper - add option to disable block updates if (!state.canSurvive(level, pos)) { level.destroyBlock(pos, true); } -@@ -88,6 +_,7 @@ +@@ -89,6 +_,7 @@ @Override - protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) { + protected boolean canSurvive(final BlockState state, final LevelReader level, final BlockPos pos) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableChorusPlantUpdates) return true; // Paper - add option to disable block updates - BlockState blockState = level.getBlockState(pos.below()); - boolean flag = !level.getBlockState(pos.above()).isAir() && !blockState.isAir(); + BlockState belowState = level.getBlockState(pos.below()); + boolean blockAboveOrBelow = !level.getBlockState(pos.above()).isAir() && !belowState.isAir(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CocoaBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CocoaBlock.java.patch index 687be11e086e..96da0cba2dff 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CocoaBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CocoaBlock.java.patch @@ -3,20 +3,20 @@ @@ -50,10 +_,10 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { -- if (level.random.nextInt(5) == 0) { -+ if (level.random.nextFloat() < (level.spigotConfig.cocoaModifier / (100.0F * 5))) { // Spigot - SPIGOT-7159: Better modifier resolution - int ageValue = state.getValue(AGE); - if (ageValue < 2) { -- level.setBlock(pos, state.setValue(AGE, ageValue + 1), Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state.setValue(AGE, ageValue + 1), Block.UPDATE_CLIENTS); // CraftBukkit + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { +- if (level.getRandom().nextInt(5) == 0) { ++ if (level.getRandom().nextFloat() < (level.spigotConfig.cocoaModifier / (100.0F * 5))) { // Spigot - SPIGOT-7159: Better modifier resolution + int age = state.getValue(AGE); + if (age < 2) { +- level.setBlock(pos, state.setValue(AGE, age + 1), Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state.setValue(AGE, age + 1), Block.UPDATE_CLIENTS); // CraftBukkit } } } @@ -115,7 +_,7 @@ @Override - public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { + public void performBonemeal(final ServerLevel level, final RandomSource random, final BlockPos pos, final BlockState state) { - level.setBlock(pos, state.setValue(AGE, state.getValue(AGE) + 1), Block.UPDATE_CLIENTS); + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state.setValue(AGE, state.getValue(AGE) + 1), Block.UPDATE_CLIENTS); // CraftBukkit } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CommandBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CommandBlock.java.patch index ee56dfd5efd5..191ac3f8f05b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CommandBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CommandBlock.java.patch @@ -1,24 +1,27 @@ --- a/net/minecraft/world/level/block/CommandBlock.java +++ b/net/minecraft/world/level/block/CommandBlock.java -@@ -70,6 +_,15 @@ +@@ -69,8 +_,17 @@ + } + } - private void setPoweredAndUpdate(Level level, BlockPos pos, CommandBlockEntity blockEntity, boolean powered) { - boolean isPowered = blockEntity.isPowered(); +- private void setPoweredAndUpdate(final Level level, final BlockPos pos, final CommandBlockEntity commandBlock, final boolean isPowered) { ++ private void setPoweredAndUpdate(final Level level, final BlockPos pos, final CommandBlockEntity commandBlock, boolean isPowered) { // Paper - remove final from isPowered + boolean wasPowered = commandBlock.isPowered(); + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); -+ int old = isPowered ? 15 : 0; -+ int current = powered ? 15 : 0; ++ int old = wasPowered ? 15 : 0; ++ int current = isPowered ? 15 : 0; + + org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(bukkitBlock, old, current); + level.getCraftServer().getPluginManager().callEvent(eventRedstone); -+ powered = eventRedstone.getNewCurrent() > 0; ++ isPowered = eventRedstone.getNewCurrent() > 0; + // CraftBukkit end - if (powered != isPowered) { - blockEntity.setPowered(powered); - if (powered) { -@@ -126,7 +_,7 @@ - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + if (isPowered != wasPowered) { + commandBlock.setPowered(isPowered); + if (isPowered) { +@@ -129,7 +_,7 @@ + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { BlockEntity blockEntity = level.getBlockEntity(pos); - if (blockEntity instanceof CommandBlockEntity && player.canUseGameMasterBlocks()) { + if (blockEntity instanceof CommandBlockEntity && (player.canUseGameMasterBlocks() || (player.isCreative() && player.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ComparatorBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ComparatorBlock.java.patch index c238837a1b3d..e6c68261dddd 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ComparatorBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ComparatorBlock.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/world/level/block/ComparatorBlock.java +++ b/net/minecraft/world/level/block/ComparatorBlock.java -@@ -167,8 +_,18 @@ - boolean shouldTurnOn = this.shouldTurnOn(level, pos, state); - boolean poweredValue = state.getValue(POWERED); - if (poweredValue && !shouldTurnOn) { +@@ -168,8 +_,18 @@ + boolean sourceOn = this.shouldTurnOn(level, pos, state); + boolean isOn = state.getValue(POWERED); + if (isOn && !sourceOn) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, 15, 0).getNewCurrent() != 0) { + return; + } + // CraftBukkit end level.setBlock(pos, state.setValue(POWERED, false), Block.UPDATE_CLIENTS); - } else if (!poweredValue && shouldTurnOn) { + } else if (!isOn && sourceOn) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, 0, 15).getNewCurrent() != 15) { + return; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch index da102b9c2e84..37f69c8d76c9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch @@ -1,74 +1,79 @@ --- a/net/minecraft/world/level/block/ComposterBlock.java +++ b/net/minecraft/world/level/block/ComposterBlock.java -@@ -252,6 +_,11 @@ - if (levelValue < 8 && COMPOSTABLES.containsKey(stack.getItem())) { - if (levelValue < 7 && !level.isClientSide()) { - BlockState blockState = addItem(player, state, level, pos, stack); +@@ -258,6 +_,11 @@ + if (fillLevel < 8 && COMPOSTABLES.containsKey(itemStack.getItem())) { + if (fillLevel < 7 && !level.isClientSide()) { + BlockState newState = addItem(player, state, level, pos, itemStack); + // Paper start - handle cancelled events -+ if (blockState == null) { ++ if (newState == null) { + return InteractionResult.PASS; + } + // Paper end - level.levelEvent(LevelEvent.COMPOSTER_FILL, pos, state != blockState ? 1 : 0); - player.awardStat(Stats.ITEM_USED.get(stack.getItem())); - stack.consume(1, player); -@@ -277,7 +_,19 @@ - public static BlockState insertItem(Entity entity, BlockState state, ServerLevel level, ItemStack stack, BlockPos pos) { - int levelValue = state.getValue(LEVEL); - if (levelValue < 7 && COMPOSTABLES.containsKey(stack.getItem())) { -- BlockState blockState = addItem(entity, state, level, pos, stack); + level.levelEvent(LevelEvent.COMPOSTER_FILL, pos, state != newState ? 1 : 0); + player.awardStat(Stats.ITEM_USED.get(itemStack.getItem())); + itemStack.consume(1, player); +@@ -287,7 +_,19 @@ + ) { + int fillLevel = state.getValue(LEVEL); + if (fillLevel < 7 && COMPOSTABLES.containsKey(itemStack.getItem())) { +- BlockState newState = addItem(sourceEntity, state, level, pos, itemStack); + // CraftBukkit start + double rand = level.getRandom().nextDouble(); -+ BlockState blockState = null; // Paper -+ if (false && (state == blockState || !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, blockState))) { // Paper - move event call into addItem ++ BlockState newState = null; // Paper ++ if (false && (state == newState || !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(sourceEntity, pos, newState))) { // Paper - move event call into addItem + return state; + } -+ blockState = ComposterBlock.addItem(entity, state, level, pos, stack, rand); ++ newState = ComposterBlock.addItem(sourceEntity, state, level, pos, itemStack, rand); + // Paper start - handle cancelled events -+ if (blockState == null) { ++ if (newState == null) { + return state; + } + // Paper end + // CraftBukkit end - stack.shrink(1); - return blockState; + itemStack.shrink(1); + return newState; } else { -@@ -286,6 +_,14 @@ +@@ -296,6 +_,14 @@ } - public static BlockState extractProduce(Entity entity, BlockState state, Level level, BlockPos pos) { + public static BlockState extractProduce(final Entity sourceEntity, final BlockState state, final Level level, final BlockPos pos) { + // CraftBukkit start -+ if (entity != null && !(entity instanceof Player)) { -+ BlockState emptyState = ComposterBlock.empty(entity, state, org.bukkit.craftbukkit.util.DummyGeneratorAccess.INSTANCE, pos); -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, emptyState)) { ++ if (!(sourceEntity instanceof Player)) { ++ BlockState emptyState = ComposterBlock.empty(sourceEntity, state, org.bukkit.craftbukkit.util.DummyLevelAccessor.INSTANCE, pos); ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(sourceEntity, pos, emptyState)) { + return state; + } + } + // CraftBukkit end if (!level.isClientSide()) { - Vec3 vec3 = Vec3.atLowerCornerWithOffset(pos, 0.5, 1.01, 0.5).offsetRandomXZ(level.random, 0.7F); - ItemEntity itemEntity = new ItemEntity(level, vec3.x(), vec3.y(), vec3.z(), new ItemStack(Items.BONE_MEAL)); -@@ -305,14 +_,39 @@ - return blockState; + Vec3 itemPos = Vec3.atLowerCornerWithOffset(pos, 0.5, 1.01, 0.5).offsetRandomXZ(level.getRandom(), 0.7F); + ItemEntity entity = new ItemEntity(level, itemPos.x(), itemPos.y(), itemPos.z(), new ItemStack(Items.BONE_MEAL)); +@@ -315,16 +_,44 @@ + return newState; } + @Nullable // Paper - static BlockState addItem(@Nullable Entity entity, BlockState state, LevelAccessor level, BlockPos pos, ItemStack stack) { + private static BlockState addItem( + final @Nullable Entity sourceEntity, final BlockState state, final LevelAccessor level, final BlockPos pos, final ItemStack itemStack + ) { + // CraftBukkit start -+ return ComposterBlock.addItem(entity, state, level, pos, stack, level.getRandom().nextDouble()); ++ return ComposterBlock.addItem(sourceEntity, state, level, pos, itemStack, level.getRandom().nextDouble()); + } + @Nullable // Paper - make it nullable -+ static BlockState addItem(@Nullable Entity entity, BlockState state, LevelAccessor level, BlockPos pos, ItemStack stack, double rand) { - int levelValue = state.getValue(LEVEL); - float _float = COMPOSTABLES.getFloat(stack.getItem()); -- if ((levelValue != 0 || !(_float > 0.0F)) && !(level.getRandom().nextDouble() < _float)) { ++ private static BlockState addItem( ++ final @Nullable Entity sourceEntity, final BlockState state, final LevelAccessor level, final BlockPos pos, final ItemStack itemStack, final double rand ++ // CraftBukkit end ++ ) { + int fillLevel = state.getValue(LEVEL); + float chance = COMPOSTABLES.getFloat(itemStack.getItem()); +- if ((fillLevel != 0 || !(chance > 0.0F)) && !(level.getRandom().nextDouble() < chance)) { + // Paper start - Add CompostItemEvent and EntityCompostItemEvent -+ boolean willRaiseLevel = !((levelValue != 0 || _float <= 0.0F) && rand >= (double) _float); ++ boolean willRaiseLevel = !((fillLevel != 0 || chance <= 0.0F) && rand >= (double) chance); + final io.papermc.paper.event.block.CompostItemEvent event; -+ if (entity == null) { -+ event = new io.papermc.paper.event.block.CompostItemEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), stack.getBukkitStack(), willRaiseLevel); ++ if (sourceEntity == null) { ++ event = new io.papermc.paper.event.block.CompostItemEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), itemStack.getBukkitStack(), willRaiseLevel); + } else { -+ event = new io.papermc.paper.event.entity.EntityCompostItemEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), stack.getBukkitStack(), willRaiseLevel); ++ event = new io.papermc.paper.event.entity.EntityCompostItemEvent(sourceEntity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), itemStack.getBukkitStack(), willRaiseLevel); + } + if (!event.callEvent()) { // check for cancellation of entity event (non entity event can't be cancelled cause of hoppers) + return null; @@ -79,22 +84,22 @@ + // Paper end - Add CompostItemEvent and EntityCompostItemEvent return state; } else { - int i = levelValue + 1; - BlockState blockState = state.setValue(LEVEL, i); + int newLevel = fillLevel + 1; + BlockState newState = state.setValue(LEVEL, newLevel); + // Paper start - move the EntityChangeBlockEvent here to avoid conflict later for the compost events -+ if (entity != null && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, blockState)) { ++ if (sourceEntity != null && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(sourceEntity, pos, newState)) { + return null; + } + // Paper end - level.setBlock(pos, blockState, Block.UPDATE_ALL); - level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(entity, blockState)); - if (i == 7) { -@@ -357,13 +_,14 @@ - if (levelValue == 8) { + level.setBlock(pos, newState, Block.UPDATE_ALL); + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(sourceEntity, newState)); + if (newLevel == 7) { +@@ -369,13 +_,14 @@ + if (contentLevel == 8) { return new ComposterBlock.OutputContainer(state, level, pos, new ItemStack(Items.BONE_MEAL)); } else { -- return (WorldlyContainer)(levelValue < 7 ? new ComposterBlock.InputContainer(state, level, pos) : new ComposterBlock.EmptyContainer()); -+ return (WorldlyContainer)(levelValue < 7 ? new ComposterBlock.InputContainer(state, level, pos) : new ComposterBlock.EmptyContainer(level, pos)); // CraftBukkit - empty generatoraccess, blockposition +- return (WorldlyContainer)(contentLevel < 7 ? new ComposterBlock.InputContainer(state, level, pos) : new ComposterBlock.EmptyContainer()); ++ return (WorldlyContainer)(contentLevel < 7 ? new ComposterBlock.InputContainer(state, level, pos) : new ComposterBlock.EmptyContainer(level, pos)); // CraftBukkit - empty levelAccessor, blockPos } } @@ -106,35 +111,35 @@ } @Override -@@ -390,6 +_,7 @@ +@@ -402,6 +_,7 @@ - public InputContainer(BlockState state, LevelAccessor level, BlockPos pos) { + public InputContainer(final BlockState state, final LevelAccessor level, final BlockPos pos) { super(1); + this.bukkitOwner = new org.bukkit.craftbukkit.inventory.CraftBlockInventoryHolder(level, pos, this); // CraftBukkit this.state = state; this.level = level; this.pos = pos; -@@ -421,6 +_,11 @@ - if (!item.isEmpty()) { +@@ -433,6 +_,11 @@ + if (!contents.isEmpty()) { this.changed = true; - BlockState blockState = ComposterBlock.addItem(null, this.state, this.level, this.pos, item); + BlockState newState = ComposterBlock.addItem(null, this.state, this.level, this.pos, contents); + // Paper start - Add CompostItemEvent and EntityCompostItemEvent -+ if (blockState == null) { ++ if (newState == null) { + return; + } + // Paper end - Add CompostItemEvent and EntityCompostItemEvent - this.level.levelEvent(LevelEvent.COMPOSTER_FILL, this.pos, blockState != this.state ? 1 : 0); + this.level.levelEvent(LevelEvent.COMPOSTER_FILL, this.pos, newState != this.state ? 1 : 0); this.removeItemNoUpdate(0); } -@@ -435,6 +_,7 @@ +@@ -447,6 +_,7 @@ - public OutputContainer(BlockState state, LevelAccessor level, BlockPos pos, ItemStack stack) { - super(stack); + public OutputContainer(final BlockState state, final LevelAccessor level, final BlockPos pos, final ItemStack contents) { + super(contents); + this.bukkitOwner = new org.bukkit.craftbukkit.inventory.CraftBlockInventoryHolder(level, pos, this); // Paper this.state = state; this.level = level; this.pos = pos; -@@ -462,8 +_,15 @@ +@@ -474,8 +_,15 @@ @Override public void setChanged() { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ConcretePowderBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ConcretePowderBlock.java.patch index 26a5973132e3..29c4e99ae8d9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ConcretePowderBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ConcretePowderBlock.java.patch @@ -1,67 +1,67 @@ --- a/net/minecraft/world/level/block/ConcretePowderBlock.java +++ b/net/minecraft/world/level/block/ConcretePowderBlock.java -@@ -38,16 +_,33 @@ +@@ -36,16 +_,33 @@ @Override - public void onLand(Level level, BlockPos pos, BlockState state, BlockState replaceableState, FallingBlockEntity fallingBlock) { - if (shouldSolidify(level, pos, replaceableState)) { + public void onLand(final Level level, final BlockPos pos, final BlockState state, final BlockState replacedBlock, final FallingBlockEntity entity) { + if (shouldSolidify(level, pos, replacedBlock)) { - level.setBlock(pos, this.concrete.defaultBlockState(), Block.UPDATE_ALL); + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, pos, this.concrete.defaultBlockState(), Block.UPDATE_ALL); // CraftBukkit } } @Override - public BlockState getStateForPlacement(BlockPlaceContext context) { + public BlockState getStateForPlacement(final BlockPlaceContext context) { - BlockGetter level = context.getLevel(); + Level level = context.getLevel(); // Paper - BlockPos clickedPos = context.getClickedPos(); - BlockState blockState = level.getBlockState(clickedPos); -- return shouldSolidify(level, clickedPos, blockState) ? this.concrete.defaultBlockState() : super.getStateForPlacement(context); + BlockPos pos = context.getClickedPos(); + BlockState replacedBlock = level.getBlockState(pos); +- return shouldSolidify(level, pos, replacedBlock) ? this.concrete.defaultBlockState() : super.getStateForPlacement(context); + // CraftBukkit start -+ if (!ConcretePowderBlock.shouldSolidify(level, clickedPos, blockState)) { ++ if (!ConcretePowderBlock.shouldSolidify(level, pos, replacedBlock)) { + return super.getStateForPlacement(context); + } + + // TODO: An event factory call for methods like this -+ org.bukkit.craftbukkit.block.CraftBlockState craftBlockState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState((net.minecraft.world.level.LevelAccessor) level, clickedPos); -+ craftBlockState.setData(this.concrete.defaultBlockState()); ++ org.bukkit.craftbukkit.block.CraftBlockState snapshot = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, pos); ++ snapshot.setBlock(this.concrete.defaultBlockState()); + -+ org.bukkit.event.block.BlockFormEvent event = new org.bukkit.event.block.BlockFormEvent(craftBlockState.getBlock(), craftBlockState); -+ level.getServer().server.getPluginManager().callEvent(event); ++ org.bukkit.event.block.BlockFormEvent event = new org.bukkit.event.block.BlockFormEvent(snapshot.getBlock(), snapshot); ++ event.callEvent(); + + if (!event.isCancelled()) { -+ return craftBlockState.getHandle(); ++ return snapshot.getHandle(); + } + + return super.getStateForPlacement(context); + // CraftBukkit end } - private static boolean shouldSolidify(BlockGetter level, BlockPos pos, BlockState state) { -@@ -88,9 +_,25 @@ - BlockState neighborState, - RandomSource random + private static boolean shouldSolidify(final BlockGetter level, final BlockPos pos, final BlockState replacedBlock) { +@@ -86,9 +_,25 @@ + final BlockState neighbourState, + final RandomSource random ) { - return touchesLiquid(level, pos) - ? this.concrete.defaultBlockState() -- : super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); +- : super.updateShape(state, level, ticks, pos, directionToNeighbour, neighbourPos, neighbourState, random); + // CraftBukkit start + if (ConcretePowderBlock.touchesLiquid(level, pos)) { + // Suppress during worldgen + if (!(level instanceof Level world1)) { + return this.concrete.defaultBlockState(); + } -+ org.bukkit.craftbukkit.block.CraftBlockState blockState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(world1, pos); -+ blockState.setData(this.concrete.defaultBlockState()); ++ org.bukkit.craftbukkit.block.CraftBlockState snapshot = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(world1, pos); ++ snapshot.setBlock(this.concrete.defaultBlockState()); + -+ org.bukkit.event.block.BlockFormEvent event = new org.bukkit.event.block.BlockFormEvent(blockState.getBlock(), blockState); ++ org.bukkit.event.block.BlockFormEvent event = new org.bukkit.event.block.BlockFormEvent(snapshot.getBlock(), snapshot); + world1.getCraftServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { -+ return blockState.getHandle(); ++ return snapshot.getHandle(); + } + } + -+ return super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); ++ return super.updateShape(state, level, ticks, pos, directionToNeighbour, neighbourPos, neighbourState, random); + // CraftBukkit end } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CopperGolemStatueBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CopperGolemStatueBlock.java.patch index abf91f5579b1..f7127c58613a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CopperGolemStatueBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CopperGolemStatueBlock.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/block/CopperGolemStatueBlock.java +++ b/net/minecraft/world/level/block/CopperGolemStatueBlock.java -@@ -107,15 +_,21 @@ - if (stack.is(ItemTags.AXES)) { +@@ -111,15 +_,21 @@ + if (itemStack.is(ItemTags.AXES)) { return InteractionResult.PASS; } else { - this.updatePose(level, state, pos, player); @@ -10,9 +10,9 @@ } } -- void updatePose(Level level, BlockState state, BlockPos pos, Player player) { +- void updatePose(final Level level, final BlockState state, final BlockPos pos, final Player player) { + // Paper start - call EntityChangeBlockEvent -+ InteractionResult updatePose(Level level, BlockState state, BlockPos pos, Player player) { ++ InteractionResult updatePose(final Level level, final BlockState state, final BlockPos pos, final Player player) { + BlockState newState = state.setValue(POSE, state.getValue(POSE).getNextPose()); + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, newState)) { + return InteractionResult.PASS; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CoralBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CoralBlock.java.patch index 685f6f642edc..f212c1876567 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CoralBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CoralBlock.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/level/block/CoralBlock.java @@ -37,6 +_,11 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (!this.scanForWater(level, pos)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, this.deadBlock.defaultBlockState()).isCancelled()) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CoralFanBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CoralFanBlock.java.patch index 107d539fbbf9..304c7fbcb959 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CoralFanBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CoralFanBlock.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/level/block/CoralFanBlock.java +++ b/net/minecraft/world/level/block/CoralFanBlock.java -@@ -38,6 +_,11 @@ +@@ -37,6 +_,11 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (!scanForWater(state, level, pos)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, this.deadBlock.defaultBlockState().setValue(CoralFanBlock.WATERLOGGED, false)).isCancelled()) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CoralPlantBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CoralPlantBlock.java.patch index d898a4c15b35..8b72b73aedbc 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CoralPlantBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CoralPlantBlock.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/level/block/CoralPlantBlock.java +++ b/net/minecraft/world/level/block/CoralPlantBlock.java -@@ -42,6 +_,11 @@ +@@ -41,6 +_,11 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (!scanForWater(state, level, pos)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, this.deadBlock.defaultBlockState().setValue(CoralPlantBlock.WATERLOGGED, false)).isCancelled()) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CoralWallFanBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CoralWallFanBlock.java.patch index e21227fb17c6..9f4ccfad7d27 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CoralWallFanBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CoralWallFanBlock.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/level/block/CoralWallFanBlock.java +++ b/net/minecraft/world/level/block/CoralWallFanBlock.java -@@ -38,6 +_,11 @@ +@@ -37,6 +_,11 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (!scanForWater(state, level, pos)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, this.deadBlock.defaultBlockState().setValue(CoralWallFanBlock.WATERLOGGED, false).setValue(CoralWallFanBlock.FACING, state.getValue(CoralWallFanBlock.FACING))).isCancelled()) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CrafterBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CrafterBlock.java.patch index 853254707bcf..0384dbfd35f1 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CrafterBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CrafterBlock.java.patch @@ -1,68 +1,68 @@ --- a/net/minecraft/world/level/block/CrafterBlock.java +++ b/net/minecraft/world/level/block/CrafterBlock.java -@@ -151,6 +_,13 @@ +@@ -155,6 +_,13 @@ } else { - RecipeHolder recipeHolder = potentialResults.get(); - ItemStack itemStack = recipeHolder.value().assemble(var11, level.registryAccess()); + RecipeHolder pickedRecipe = recipe.get(); + ItemStack results = pickedRecipe.value().assemble(var11); + // CraftBukkit start -+ org.bukkit.event.block.CrafterCraftEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callCrafterCraftEvent(pos, level, itemStack, recipeHolder); ++ org.bukkit.event.block.CrafterCraftEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callCrafterCraftEvent(pos, level, results, pickedRecipe); + if (event.isCancelled()) { + return; + } -+ itemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getResult()); ++ results = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getResult()); + // CraftBukkit end - if (itemStack.isEmpty()) { + if (results.isEmpty()) { level.levelEvent(LevelEvent.SOUND_CRAFTER_FAIL, pos, 0); } else { -@@ -185,7 +_,25 @@ - Container containerAt = HopperBlockEntity.getContainerAt(level, pos.relative(direction)); - ItemStack itemStack = stack.copy(); - if (containerAt != null && (containerAt instanceof CrafterBlockEntity || stack.getCount() > containerAt.getMaxStackSize(stack))) { +@@ -196,7 +_,25 @@ + Container into = HopperBlockEntity.getContainerAt(level, pos.relative(direction)); + ItemStack remaining = results.copy(); + if (into != null && (into instanceof CrafterBlockEntity || results.getCount() > into.getMaxStackSize(results))) { + // CraftBukkit start - InventoryMoveItemEvent -+ org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); ++ org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(remaining); + + org.bukkit.inventory.Inventory destinationInventory; + // Have to special case large chests as they work oddly -+ if (containerAt instanceof net.minecraft.world.CompoundContainer compoundContainer) { ++ if (into instanceof net.minecraft.world.CompoundContainer compoundContainer) { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); + } else { -+ destinationInventory = containerAt.getOwner().getInventory(); ++ destinationInventory = into.getOwner().getInventory(); + } + -+ org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(crafter.getOwner().getInventory(), oitemstack, destinationInventory, true); ++ org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(blockEntity.getOwner().getInventory(), oitemstack, destinationInventory, true); + level.getCraftServer().getPluginManager().callEvent(event); -+ itemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); - while (!itemStack.isEmpty()) { ++ remaining = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); + while (!remaining.isEmpty()) { + if (event.isCancelled()) { + break; + } + // CraftBukkit end - ItemStack itemStack1 = itemStack.copyWithCount(1); - ItemStack itemStack2 = HopperBlockEntity.addItem(crafter, containerAt, itemStack1, direction.getOpposite()); - if (!itemStack2.isEmpty()) { -@@ -195,7 +_,25 @@ - itemStack.shrink(1); + ItemStack copy = remaining.copyWithCount(1); + ItemStack itemStack = HopperBlockEntity.addItem(blockEntity, into, copy, direction.getOpposite()); + if (!itemStack.isEmpty()) { +@@ -206,7 +_,25 @@ + remaining.shrink(1); } - } else if (containerAt != null) { + } else if (into != null) { + // CraftBukkit start - InventoryMoveItemEvent -+ org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); ++ org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(remaining); + + org.bukkit.inventory.Inventory destinationInventory; + // Have to special case large chests as they work oddly -+ if (containerAt instanceof net.minecraft.world.CompoundContainer compoundContainer) { ++ if (into instanceof net.minecraft.world.CompoundContainer compoundContainer) { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); + } else { -+ destinationInventory = containerAt.getOwner().getInventory(); ++ destinationInventory = into.getOwner().getInventory(); + } + -+ org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(crafter.getOwner().getInventory(), oitemstack, destinationInventory, true); ++ org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(blockEntity.getOwner().getInventory(), oitemstack, destinationInventory, true); + level.getCraftServer().getPluginManager().callEvent(event); -+ itemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); - while (!itemStack.isEmpty()) { ++ remaining = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); + while (!remaining.isEmpty()) { + if (event.isCancelled()) { + break; + } + // CraftBukkit end - int count = itemStack.getCount(); - itemStack = HopperBlockEntity.addItem(crafter, containerAt, itemStack, direction.getOpposite()); - if (count == itemStack.getCount()) { + int oldSize = remaining.getCount(); + remaining = HopperBlockEntity.addItem(blockEntity, into, remaining, direction.getOpposite()); + if (oldSize == remaining.getCount()) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CraftingTableBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CraftingTableBlock.java.patch index 8cd3883d79a2..f865cb22f9f9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CraftingTableBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CraftingTableBlock.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/level/block/CraftingTableBlock.java +++ b/net/minecraft/world/level/block/CraftingTableBlock.java -@@ -31,8 +_,9 @@ - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { +@@ -33,8 +_,9 @@ + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { if (!level.isClientSide()) { - player.openMenu(state.getMenuProvider(level, pos)); + if (player.openMenu(state.getMenuProvider(level, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CropBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CropBlock.java.patch index d30cd59871b4..073db8b0c7bf 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CropBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CropBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/CropBlock.java +++ b/net/minecraft/world/level/block/CropBlock.java -@@ -80,8 +_,25 @@ +@@ -81,8 +_,25 @@ int age = this.getAge(state); if (age < this.getMaxAge()) { float growthSpeed = getGrowthSpeed(this, level, pos); @@ -28,19 +28,19 @@ } } } -@@ -89,7 +_,7 @@ +@@ -90,7 +_,7 @@ - public void growCrops(Level level, BlockPos pos, BlockState state) { - int min = Math.min(this.getMaxAge(), this.getAge(state) + this.getBonemealAgeIncrease(level)); -- level.setBlock(pos, this.getStateForAge(min), Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, this.getStateForAge(min), Block.UPDATE_CLIENTS); // CraftBukkit + public void growCrops(final Level level, final BlockPos pos, final BlockState state) { + int age = Math.min(this.getMaxAge(), this.getAge(state) + this.getBonemealAgeIncrease(level)); +- level.setBlock(pos, this.getStateForAge(age), Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, this.getStateForAge(age), Block.UPDATE_CLIENTS); // CraftBukkit } - protected int getBonemealAgeIncrease(Level level) { -@@ -151,7 +_,8 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { + protected int getBonemealAgeIncrease(final Level level) { +@@ -159,7 +_,8 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { - if (level instanceof ServerLevel serverLevel && entity instanceof Ravager && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (level instanceof ServerLevel serverLevel && entity instanceof Ravager && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !serverLevel.getGameRules().get(GameRules.MOB_GRIEFING))) { // CraftBukkit diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DaylightDetectorBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DaylightDetectorBlock.java.patch index 8e8a4959038e..099ed53b359c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DaylightDetectorBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DaylightDetectorBlock.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/world/level/block/DaylightDetectorBlock.java +++ b/net/minecraft/world/level/block/DaylightDetectorBlock.java -@@ -71,6 +_,7 @@ +@@ -70,6 +_,7 @@ - i = Mth.clamp(i, 0, 15); - if (state.getValue(POWER) != i) { -+ i = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, state.getValue(DaylightDetectorBlock.POWER), i).getNewCurrent(); // CraftBukkit - Call BlockRedstoneEvent - level.setBlock(pos, state.setValue(POWER, i), Block.UPDATE_ALL); + target = Mth.clamp(target, 0, 15); + if (state.getValue(POWER) != target) { ++ target = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, state.getValue(DaylightDetectorBlock.POWER), target).getNewCurrent(); // CraftBukkit - Call BlockRedstoneEvent + level.setBlock(pos, state.setValue(POWER, target), Block.UPDATE_ALL); } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DecoratedPotBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DecoratedPotBlock.java.patch index 260767c050ff..debf5cc6f477 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DecoratedPotBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DecoratedPotBlock.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/level/block/DecoratedPotBlock.java +++ b/net/minecraft/world/level/block/DecoratedPotBlock.java -@@ -209,6 +_,11 @@ - protected void onProjectileHit(Level level, BlockState state, BlockHitResult hit, Projectile projectile) { - BlockPos blockPos = hit.getBlockPos(); - if (level instanceof ServerLevel serverLevel && projectile.mayInteract(serverLevel, blockPos) && projectile.mayBreak(serverLevel)) { +@@ -217,6 +_,11 @@ + protected void onProjectileHit(final Level level, final BlockState state, final BlockHitResult blockHit, final Projectile projectile) { + BlockPos pos = blockHit.getBlockPos(); + if (level instanceof ServerLevel serverLevel && projectile.mayInteract(serverLevel, pos) && projectile.mayBreak(serverLevel)) { + // CraftBukkit start - call EntityChangeBlockEvent -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(projectile, blockPos, this.getFluidState(state).createLegacyBlock())) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(projectile, pos, this.getFluidState(state).createLegacyBlock())) { + return; + } + // CraftBukkit end - level.setBlock(blockPos, state.setValue(CRACKED, true), Block.UPDATE_NONE); - level.destroyBlock(blockPos, true, projectile); + level.setBlock(pos, state.setValue(CRACKED, true), Block.UPDATE_NONE); + level.destroyBlock(pos, true, projectile); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DetectorRailBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DetectorRailBlock.java.patch index 865623c40a09..98c7b010c07f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DetectorRailBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DetectorRailBlock.java.patch @@ -1,35 +1,35 @@ --- a/net/minecraft/world/level/block/DetectorRailBlock.java +++ b/net/minecraft/world/level/block/DetectorRailBlock.java -@@ -49,6 +_,7 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -57,6 +_,7 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (!level.isClientSide()) { if (!state.getValue(POWERED)) { this.checkPressed(level, pos, state); -@@ -79,6 +_,7 @@ +@@ -87,6 +_,7 @@ - private void checkPressed(Level level, BlockPos pos, BlockState state) { + private void checkPressed(final Level level, final BlockPos pos, final BlockState state) { if (this.canSurvive(state, level, pos)) { + if (!state.is(this)) { return; } // Paper - Fix some rails connecting improperly - boolean poweredValue = state.getValue(POWERED); - boolean flag = false; - List interactingMinecartOfType = this.getInteractingMinecartOfType(level, pos, AbstractMinecart.class, entity -> true); -@@ -86,6 +_,16 @@ - flag = true; + boolean wasPressed = state.getValue(POWERED); + boolean shouldBePressed = false; + List entities = this.getInteractingMinecartOfType(level, pos, AbstractMinecart.class, e -> true); +@@ -94,6 +_,16 @@ + shouldBePressed = true; } + // CraftBukkit start -+ if (poweredValue != flag) { ++ if (wasPressed != shouldBePressed) { + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + -+ org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(block, flag ? 15 : 0, flag ? 15 : 0); ++ org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(block, shouldBePressed ? 15 : 0, shouldBePressed ? 15 : 0); + level.getCraftServer().getPluginManager().callEvent(eventRedstone); + -+ flag = eventRedstone.getNewCurrent() > 0; ++ shouldBePressed = eventRedstone.getNewCurrent() > 0; + } + // CraftBukkit end - if (flag && !poweredValue) { - BlockState blockState = state.setValue(POWERED, true); - level.setBlock(pos, blockState, Block.UPDATE_ALL); + if (shouldBePressed && !wasPressed) { + BlockState newState = state.setValue(POWERED, true); + level.setBlock(pos, newState, Block.UPDATE_ALL); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DiodeBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DiodeBlock.java.patch index df692dfea0dc..510666c1dd8d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DiodeBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DiodeBlock.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/world/level/block/DiodeBlock.java +++ b/net/minecraft/world/level/block/DiodeBlock.java -@@ -56,8 +_,18 @@ - boolean poweredValue = state.getValue(POWERED); +@@ -57,8 +_,18 @@ + boolean on = state.getValue(POWERED); boolean shouldTurnOn = this.shouldTurnOn(level, pos, state); - if (poweredValue && !shouldTurnOn) { + if (on && !shouldTurnOn) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, 15, 0).getNewCurrent() != 0) { + return; + } + // CraftBukkit end level.setBlock(pos, state.setValue(POWERED, false), Block.UPDATE_CLIENTS); - } else if (!poweredValue) { + } else if (!on) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, 0, 15).getNewCurrent() != 15) { + return; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DirtPathBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DirtPathBlock.java.patch index bc73d1c956f9..3d58bc2d4783 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DirtPathBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DirtPathBlock.java.patch @@ -3,12 +3,12 @@ @@ -60,6 +_,11 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { + // CraftBukkit start - do not fade if the block is valid here + if (state.canSurvive(level, pos)) { + return; + } + // CraftBukkit end - FarmBlock.turnToDirt(null, state, level, pos); + FarmlandBlock.turnToDirt(null, state, level, pos); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DispenserBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DispenserBlock.java.patch index 190bf71c49a0..7bf41effb2eb 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DispenserBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DispenserBlock.java.patch @@ -1,29 +1,29 @@ --- a/net/minecraft/world/level/block/DispenserBlock.java +++ b/net/minecraft/world/level/block/DispenserBlock.java -@@ -71,8 +_,7 @@ - - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { -- if (!level.isClientSide() && level.getBlockEntity(pos) instanceof DispenserBlockEntity dispenserBlockEntity) { -- player.openMenu(dispenserBlockEntity); -+ if (!level.isClientSide() && level.getBlockEntity(pos) instanceof DispenserBlockEntity dispenserBlockEntity && player.openMenu(dispenserBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation - player.awardStat(dispenserBlockEntity instanceof DropperBlockEntity ? Stats.INSPECT_DROPPER : Stats.INSPECT_DISPENSER); +@@ -75,8 +_,7 @@ + protected InteractionResult useWithoutItem( + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { +- if (!level.isClientSide() && level.getBlockEntity(pos) instanceof DispenserBlockEntity dispenser) { +- player.openMenu(dispenser); ++ if (!level.isClientSide() && level.getBlockEntity(pos) instanceof DispenserBlockEntity dispenser && player.openMenu(dispenser).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation + player.awardStat(dispenser instanceof DropperBlockEntity ? Stats.INSPECT_DROPPER : Stats.INSPECT_DISPENSER); } -@@ -87,17 +_,26 @@ - BlockSource blockSource = new BlockSource(level, pos, state, dispenserBlockEntity); - int randomSlot = dispenserBlockEntity.getRandomSlot(level.random); - if (randomSlot < 0) { +@@ -91,17 +_,26 @@ + BlockSource source = new BlockSource(level, pos, state, blockEntity); + int slot = blockEntity.getRandomSlot(level.getRandom()); + if (slot < 0) { + if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFailedDispenseEvent(level, pos)) { // Paper - Add BlockFailedDispenseEvent level.levelEvent(LevelEvent.SOUND_DISPENSER_FAIL, pos, 0); - level.gameEvent(GameEvent.BLOCK_ACTIVATE, pos, GameEvent.Context.of(dispenserBlockEntity.getBlockState())); + level.gameEvent(GameEvent.BLOCK_ACTIVATE, pos, GameEvent.Context.of(blockEntity.getBlockState())); + } // Paper - Add BlockFailedDispenseEvent } else { - ItemStack item = dispenserBlockEntity.getItem(randomSlot); - DispenseItemBehavior dispenseMethod = this.getDispenseMethod(level, item); - if (dispenseMethod != DispenseItemBehavior.NOOP) { -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockPreDispenseEvent(level, pos, item, randomSlot)) return; // Paper - Add BlockPreDispenseEvent - dispenserBlockEntity.setItem(randomSlot, dispenseMethod.dispense(blockSource, item)); + ItemStack itemStack = blockEntity.getItem(slot); + DispenseItemBehavior behavior = this.getDispenseMethod(level, itemStack); + if (behavior != DispenseItemBehavior.NOOP) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockPreDispenseEvent(level, pos, itemStack, slot)) return; // Paper - Add BlockPreDispenseEvent + blockEntity.setItem(slot, behavior.dispense(source, itemStack)); } } } @@ -35,5 +35,5 @@ + } + // Paper end - Fix NPE with equippable and items without behavior - protected DispenseItemBehavior getDispenseMethod(Level level, ItemStack item) { - if (!item.isItemEnabled(level.enabledFeatures())) { + protected DispenseItemBehavior getDispenseMethod(final Level level, final ItemStack itemStack) { + if (!itemStack.isItemEnabled(level.enabledFeatures())) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DoorBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DoorBlock.java.patch index 77f0acfcdd6f..5b6beae4a05d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DoorBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DoorBlock.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/level/block/DoorBlock.java +++ b/net/minecraft/world/level/block/DoorBlock.java -@@ -221,9 +_,22 @@ - - @Override - protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { -- boolean flag = level.hasNeighborSignal(pos) +@@ -225,9 +_,22 @@ + protected void neighborChanged( + final BlockState state, final Level level, final BlockPos pos, final Block block, final @Nullable Orientation orientation, final boolean movedByPiston + ) { +- boolean signal = level.hasNeighborSignal(pos) - || level.hasNeighborSignal(pos.relative(state.getValue(HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN)); -- if (!this.defaultBlockState().is(neighborBlock) && flag != state.getValue(POWERED)) { +- if (!this.defaultBlockState().is(block) && signal != state.getValue(POWERED)) { + // CraftBukkit start + BlockPos otherHalf = pos.relative(state.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN); + org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); @@ -21,8 +21,8 @@ + org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent(bukkitBlock, oldPower, power); + event.callEvent(); + -+ boolean flag = event.getNewCurrent() > 0; ++ boolean signal = event.getNewCurrent() > 0; + // CraftBukkit end - if (flag != state.getValue(OPEN)) { - this.playSound(null, level, pos, flag); - level.gameEvent(null, flag ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos); + if (signal != state.getValue(OPEN)) { + this.playSound(null, level, pos, signal); + level.gameEvent(null, signal ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DoubleBlockCombiner.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DoubleBlockCombiner.java.patch index 3416e7d85dbc..cfd5138462c3 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DoubleBlockCombiner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DoubleBlockCombiner.java.patch @@ -3,14 +3,14 @@ @@ -34,7 +_,12 @@ return new DoubleBlockCombiner.NeighborCombineResult.Single<>(blockEntity); } else { - BlockPos blockPos = pos.relative(directionGetter.apply(state)); -- BlockState blockState = level.getBlockState(blockPos); + BlockPos neighborPos = pos.relative(connectionResolver.apply(state)); +- BlockState neighbourState = level.getBlockState(neighborPos); + // Paper start - Don't load Chunks from Hoppers and other things -+ BlockState blockState = level.getBlockStateIfLoaded(blockPos); -+ if (blockState == null) { ++ BlockState neighbourState = level.getBlockStateIfLoaded(neighborPos); ++ if (neighbourState == null) { + return new DoubleBlockCombiner.NeighborCombineResult.Single<>(blockEntity); + } + // Paper end - Don't load Chunks from Hoppers and other things - if (blockState.is(state.getBlock())) { - DoubleBlockCombiner.BlockType blockType1 = doubleBlockTypeGetter.apply(blockState); - if (blockType1 != DoubleBlockCombiner.BlockType.SINGLE + if (neighbourState.is(state.getBlock())) { + DoubleBlockCombiner.BlockType neighbourType = typeResolver.apply(neighbourState); + if (neighbourType != DoubleBlockCombiner.BlockType.SINGLE diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DoublePlantBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DoublePlantBlock.java.patch index 0d4725287a6d..5c7d6de9859a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DoublePlantBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DoublePlantBlock.java.patch @@ -1,21 +1,21 @@ --- a/net/minecraft/world/level/block/DoublePlantBlock.java +++ b/net/minecraft/world/level/block/DoublePlantBlock.java -@@ -109,11 +_,16 @@ +@@ -114,11 +_,17 @@ + final BlockState state, + final @Nullable BlockEntity blockEntity, + final ItemStack destroyedWith ++ , boolean includeDrops, boolean dropExp // Paper - fix drops not preventing stats/food exhaustion + ) { +- super.playerDestroy(level, player, pos, Blocks.AIR.defaultBlockState(), blockEntity, destroyedWith); ++ super.playerDestroy(level, player, pos, Blocks.AIR.defaultBlockState(), blockEntity, destroyedWith, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion; } - @Override -- public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack stack) { -- super.playerDestroy(level, player, pos, Blocks.AIR.defaultBlockState(), blockEntity, stack); -+ public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack stack, boolean includeDrops, boolean dropExp) { // Paper - fix drops not preventing stats/food exhaustion -+ super.playerDestroy(level, player, pos, Blocks.AIR.defaultBlockState(), blockEntity, stack, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion; - } - - protected static void preventDropFromBottomPart(Level level, BlockPos pos, BlockState state, Player player) { + protected static void preventDropFromBottomPart(final Level level, final BlockPos pos, final BlockState state, final Player player) { + // CraftBukkit start + if (((net.minecraft.server.level.ServerLevel)level).hasPhysicsEvent && org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPhysicsEvent(level, pos).isCancelled()) { // Paper + return; + } + // CraftBukkit end - DoubleBlockHalf doubleBlockHalf = state.getValue(HALF); - if (doubleBlockHalf == DoubleBlockHalf.UPPER) { - BlockPos blockPos = pos.below(); + DoubleBlockHalf part = state.getValue(HALF); + if (part == DoubleBlockHalf.UPPER) { + BlockPos bottomPos = pos.below(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DragonEggBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DragonEggBlock.java.patch index ea38b8d064ea..63a820102d1c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DragonEggBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DragonEggBlock.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/level/block/DragonEggBlock.java +++ b/net/minecraft/world/level/block/DragonEggBlock.java -@@ -55,6 +_,16 @@ - level.random.nextInt(16) - level.random.nextInt(16) - ); - if (level.getBlockState(blockPos).isAir() && worldBorder.isWithinBounds(blockPos) && !level.isOutsideBuildHeight(blockPos)) { +@@ -61,6 +_,16 @@ + && !level.getBlockState(testPos.below()).isAir() + && worldBorder.isWithinBounds(testPos) + && level.isInsideBuildHeight(testPos)) { + // CraftBukkit start + org.bukkit.block.Block from = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); -+ org.bukkit.block.Block to = org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos); ++ org.bukkit.block.Block to = org.bukkit.craftbukkit.block.CraftBlock.at(level, testPos); + org.bukkit.event.block.BlockFromToEvent event = new org.bukkit.event.block.BlockFromToEvent(from, to); + if (!event.callEvent()) { + return; + } + -+ blockPos = new BlockPos(event.getToBlock().getX(), event.getToBlock().getY(), event.getToBlock().getZ()); ++ testPos = new BlockPos(event.getToBlock().getX(), event.getToBlock().getY(), event.getToBlock().getZ()); + // CraftBukkit end if (level.isClientSide()) { - for (int i1 = 0; i1 < 128; i1++) { - double randomDouble = level.random.nextDouble(); + for (int j = 0; j < 128; j++) { + double d = random.nextDouble(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DriedGhastBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DriedGhastBlock.java.patch index f0247b5487b8..18cb2b81d438 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DriedGhastBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DriedGhastBlock.java.patch @@ -2,32 +2,32 @@ +++ b/net/minecraft/world/level/block/DriedGhastBlock.java @@ -104,10 +_,20 @@ - private void tickWaterlogged(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + private void tickWaterlogged(final BlockState state, final ServerLevel level, final BlockPos position, final RandomSource random) { if (!this.isReadyToSpawn(state)) { + // Paper start - Call BlockGrowEvent -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state.setValue(HYDRATION_LEVEL, this.getHydrationLevel(state) + 1), Block.UPDATE_CLIENTS)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, position, state.setValue(HYDRATION_LEVEL, this.getHydrationLevel(state) + 1), Block.UPDATE_CLIENTS)) { + return; + } + // Paper end - Call BlockGrowEvent - level.playSound(null, pos, SoundEvents.DRIED_GHAST_TRANSITION, SoundSource.BLOCKS, 1.0F, 1.0F); -- level.setBlock(pos, state.setValue(HYDRATION_LEVEL, this.getHydrationLevel(state) + 1), Block.UPDATE_CLIENTS); -+ // level.setBlock(pos, state.setValue(HYDRATION_LEVEL, this.getHydrationLevel(state) + 1), Block.UPDATE_CLIENTS); // Paper - handled above - level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(state)); + level.playSound(null, position, SoundEvents.DRIED_GHAST_TRANSITION, SoundSource.BLOCKS, 1.0F, 1.0F); +- level.setBlock(position, state.setValue(HYDRATION_LEVEL, this.getHydrationLevel(state) + 1), Block.UPDATE_CLIENTS); ++ // level.setBlock(position, state.setValue(HYDRATION_LEVEL, this.getHydrationLevel(state) + 1), Block.UPDATE_CLIENTS); // Paper - handled above + level.gameEvent(GameEvent.BLOCK_CHANGE, position, GameEvent.Context.of(state)); } else { + // Paper start - Call BlockFadeEvent -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, state.getFluidState().createLegacyBlock()).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, position, state.getFluidState().createLegacyBlock()).isCancelled()) { + return; + } + // Paper end - Call BlockFadeEvent - this.spawnGhastling(level, pos, state); + this.spawnGhastling(level, position, state); } } @@ -121,7 +_,7 @@ - float yRot = Direction.getYRot(state.getValue(FACING)); - happyGhast.setYHeadRot(yRot); - happyGhast.snapTo(bottomCenter.x(), bottomCenter.y(), bottomCenter.z(), yRot, 0.0F); -- level.addFreshEntity(happyGhast); -+ level.addFreshEntity(happyGhast, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.REHYDRATION); // Paper - spawn reason - level.playSound(null, happyGhast, SoundEvents.GHASTLING_SPAWN, SoundSource.BLOCKS, 1.0F, 1.0F); + float blockRotation = Direction.getYRot(state.getValue(FACING)); + ghastling.setYHeadRot(blockRotation); + ghastling.snapTo(spawnAt.x(), spawnAt.y(), spawnAt.z(), blockRotation, 0.0F); +- level.addFreshEntity(ghastling); ++ level.addFreshEntity(ghastling, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.REHYDRATION); // Paper - spawn reason + level.playSound(null, ghastling, SoundEvents.GHASTLING_SPAWN, SoundSource.BLOCKS, 1.0F, 1.0F); } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DropExperienceBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DropExperienceBlock.java.patch index 6fbb433e7bfa..28c78bb336d4 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DropExperienceBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DropExperienceBlock.java.patch @@ -1,18 +1,18 @@ --- a/net/minecraft/world/level/block/DropExperienceBlock.java +++ b/net/minecraft/world/level/block/DropExperienceBlock.java -@@ -31,8 +_,16 @@ +@@ -29,8 +_,16 @@ @Override - protected void spawnAfterBreak(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { - super.spawnAfterBreak(state, level, pos, stack, dropExperience); + protected void spawnAfterBreak(final BlockState state, final ServerLevel level, final BlockPos pos, final ItemStack tool, final boolean dropExperience) { + super.spawnAfterBreak(state, level, pos, tool, dropExperience); + // CraftBukkit start - Delegate to getExpDrop + } + + @Override -+ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { ++ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack tool, boolean dropExperience) { if (dropExperience) { -- this.tryDropExperience(level, pos, stack, this.xpRange); +- this.tryDropExperience(level, pos, tool, this.xpRange); - } -+ return this.tryDropExperience(level, pos, stack, this.xpRange); ++ return this.tryDropExperience(level, pos, tool, this.xpRange); + } + + return 0; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DropperBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DropperBlock.java.patch index 97d52b1a403e..6f97429c74c1 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DropperBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DropperBlock.java.patch @@ -1,42 +1,40 @@ --- a/net/minecraft/world/level/block/DropperBlock.java +++ b/net/minecraft/world/level/block/DropperBlock.java @@ -53,6 +_,7 @@ - BlockSource blockSource = new BlockSource(level, pos, state, dispenserBlockEntity); - int randomSlot = dispenserBlockEntity.getRandomSlot(level.random); - if (randomSlot < 0) { + BlockSource source = new BlockSource(level, pos, state, blockEntity); + int slot = blockEntity.getRandomSlot(level.getRandom()); + if (slot < 0) { + if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFailedDispenseEvent(level, pos)) // Paper - Add BlockFailedDispenseEvent level.levelEvent(LevelEvent.SOUND_DISPENSER_FAIL, pos, 0); } else { - ItemStack item = dispenserBlockEntity.getItem(randomSlot); -@@ -61,10 +_,29 @@ - Container containerAt = HopperBlockEntity.getContainerAt(level, pos.relative(direction)); - ItemStack itemStack; - if (containerAt == null) { -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockPreDispenseEvent(level, pos, item, randomSlot)) return; // Paper - Add BlockPreDispenseEvent - itemStack = DISPENSE_BEHAVIOUR.dispense(blockSource, item); + ItemStack itemStack = blockEntity.getItem(slot); +@@ -61,10 +_,27 @@ + Container into = HopperBlockEntity.getContainerAt(level, pos.relative(direction)); + ItemStack remaining; + if (into == null) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockPreDispenseEvent(level, pos, itemStack, slot)) return; // Paper - Add BlockPreDispenseEvent + remaining = DISPENSE_BEHAVIOUR.dispense(source, itemStack); } else { -- itemStack = HopperBlockEntity.addItem(dispenserBlockEntity, containerAt, item.copyWithCount(1), direction.getOpposite()); -- if (itemStack.isEmpty()) { +- remaining = HopperBlockEntity.addItem(blockEntity, into, itemStack.copyWithCount(1), direction.getOpposite()); +- if (remaining.isEmpty()) { + // CraftBukkit start - Fire event when pushing items into other inventories -+ org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); ++ org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack.copyWithCount(1)); + + org.bukkit.inventory.Inventory destinationInventory; + // Have to special case large chests as they work oddly -+ if (containerAt instanceof net.minecraft.world.CompoundContainer compoundContainer) { ++ if (into instanceof net.minecraft.world.CompoundContainer compoundContainer) { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); + } else { -+ destinationInventory = containerAt.getOwner().getInventory(); ++ destinationInventory = into.getOwner().getInventory(); + } + -+ org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(dispenserBlockEntity.getOwner().getInventory(), oitemstack, destinationInventory, true); -+ level.getCraftServer().getPluginManager().callEvent(event); -+ if (event.isCancelled()) { ++ org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(blockEntity.getOwner().getInventory(), oitemstack, destinationInventory, true); ++ if (!event.callEvent()) { + return; + } -+ itemStack = HopperBlockEntity.addItem(dispenserBlockEntity, containerAt, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), direction.getOpposite()); -+ if (event.getItem().equals(oitemstack) && itemStack.isEmpty()) { ++ remaining = HopperBlockEntity.addItem(blockEntity, into, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), direction.getOpposite()); ++ if (event.getItem().equals(oitemstack) && remaining.isEmpty()) { + // CraftBukkit end -+ - itemStack = item.copy(); - itemStack.shrink(1); + remaining = itemStack.copy(); + remaining.shrink(1); } else { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/EndGatewayBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/EndGatewayBlock.java.patch index b0a0bea9ebad..96a145af4719 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/EndGatewayBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/EndGatewayBlock.java.patch @@ -1,30 +1,37 @@ --- a/net/minecraft/world/level/block/EndGatewayBlock.java +++ b/net/minecraft/world/level/block/EndGatewayBlock.java -@@ -89,10 +_,15 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -94,10 +_,15 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (entity.canUsePortal(false) && !level.isClientSide() - && level.getBlockEntity(pos) instanceof TheEndGatewayBlockEntity theEndGatewayBlockEntity - && !theEndGatewayBlockEntity.isCoolingDown()) { + && level.getBlockEntity(pos) instanceof TheEndGatewayBlockEntity endGatewayBlockEntity + && !endGatewayBlockEntity.isCoolingDown()) { + // Paper start - call EntityPortalEnterEvent + org.bukkit.event.entity.EntityPortalEnterEvent event = new org.bukkit.event.entity.EntityPortalEnterEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level), org.bukkit.PortalType.END_GATEWAY); // Paper - add portal type + if (!event.callEvent()) return; + // Paper end - call EntityPortalEnterEvent entity.setAsInsidePortal(this, pos); - TheEndGatewayBlockEntity.triggerCooldown(level, pos, state, theEndGatewayBlockEntity); + TheEndGatewayBlockEntity.triggerCooldown(level, pos, state, endGatewayBlockEntity); } -@@ -106,9 +_,9 @@ +@@ -111,7 +_,7 @@ return null; } else { return entity instanceof ThrownEnderpearl -- ? new TeleportTransition(level, portalPosition, Vec3.ZERO, 0.0F, 0.0F, Set.of(), TeleportTransition.PLACE_PORTAL_TICKET) -+ ? new TeleportTransition(level, portalPosition, Vec3.ZERO, 0.0F, 0.0F, Set.of(), TeleportTransition.PLACE_PORTAL_TICKET, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_GATEWAY) // CraftBukkit +- ? new TeleportTransition(currentLevel, teleportPosition, Vec3.ZERO, 0.0F, 0.0F, Set.of(), TeleportTransition.PLACE_PORTAL_TICKET) ++ ? new TeleportTransition(currentLevel, teleportPosition, Vec3.ZERO, 0.0F, 0.0F, Set.of(), TeleportTransition.PLACE_PORTAL_TICKET, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_GATEWAY) // CraftBukkit : new TeleportTransition( -- level, portalPosition, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.DELTA, Relative.ROTATION), TeleportTransition.PLACE_PORTAL_TICKET -+ level, portalPosition, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.DELTA, Relative.ROTATION), TeleportTransition.PLACE_PORTAL_TICKET, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_GATEWAY // CraftBukkit + currentLevel, + teleportPosition, +@@ -119,7 +_,8 @@ + 0.0F, + 0.0F, + Relative.union(Relative.DELTA, Relative.ROTATION), +- TeleportTransition.PLACE_PORTAL_TICKET ++ TeleportTransition.PLACE_PORTAL_TICKET, ++ org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_GATEWAY // CraftBukkit ); } } else { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/EndPortalBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/EndPortalBlock.java.patch index d9e3c4ce3361..920164481523 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/EndPortalBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/EndPortalBlock.java.patch @@ -1,64 +1,64 @@ --- a/net/minecraft/world/level/block/EndPortalBlock.java +++ b/net/minecraft/world/level/block/EndPortalBlock.java -@@ -59,8 +_,15 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -66,8 +_,14 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (entity.canUsePortal(false)) { + // CraftBukkit start - Entity in portal + org.bukkit.event.entity.EntityPortalEnterEvent event = new org.bukkit.event.entity.EntityPortalEnterEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level), org.bukkit.PortalType.ENDER); // Paper - add portal type -+ level.getCraftServer().getPluginManager().callEvent(event); -+ if (event.isCancelled()) return; // Paper - make cancellable ++ if (!event.callEvent()) return; // Paper - make cancellable + // CraftBukkit end - if (!level.isClientSide() && level.dimension() == Level.END && entity instanceof ServerPlayer serverPlayer && !serverPlayer.seenCredits) { -+ if (level.paperConfig().misc.disableEndCredits) {serverPlayer.seenCredits = true; return;} // Paper - Option to disable end credits - serverPlayer.showEndCredits(); + if (!level.isClientSide() && level.dimension() == Level.END && entity instanceof ServerPlayer player && !player.seenCredits) { ++ if (level.paperConfig().misc.disableEndCredits) {player.seenCredits = true; return;} // Paper - Option to disable end credits + player.showEndCredits(); } else { entity.setAsInsidePortal(this, pos); -@@ -72,7 +_,7 @@ - public @Nullable TeleportTransition getPortalDestination(ServerLevel level, Entity entity, BlockPos pos) { - LevelData.RespawnData respawnData = level.getRespawnData(); - ResourceKey resourceKey = level.dimension(); -- boolean flag = resourceKey == Level.END; -+ boolean flag = level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.END; // CraftBukkit - SPIGOT-6152: send back to main overworld in custom ends - ResourceKey resourceKey1 = flag ? respawnData.dimension() : Level.END; - BlockPos blockPos = flag ? respawnData.pos() : ServerLevel.END_SPAWN_POINT; - ServerLevel level1 = level.getServer().getLevel(resourceKey1); -@@ -84,7 +_,7 @@ - float f1; - Set set; - if (!flag) { -- EndPlatformFeature.createEndPlatform(level1, BlockPos.containing(bottomCenter).below(), true); -+ EndPlatformFeature.createEndPlatform(level1, BlockPos.containing(bottomCenter).below(), true, entity); // CraftBukkit - f = Direction.WEST.toYRot(); - f1 = 0.0F; - set = Relative.union(Relative.DELTA, Set.of(Relative.X_ROT)); -@@ -96,15 +_,24 @@ - f1 = respawnData.pitch(); - set = Relative.union(Relative.DELTA, Relative.ROTATION); +@@ -78,8 +_,7 @@ + @Override + public @Nullable TeleportTransition getPortalDestination(final ServerLevel currentLevel, final Entity entity, final BlockPos portalEntryPos) { + LevelData.RespawnData respawnData = currentLevel.getRespawnData(); +- ResourceKey currentDimension = currentLevel.dimension(); +- boolean fromEnd = currentDimension == Level.END; ++ boolean fromEnd = currentLevel.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.END; // CraftBukkit - SPIGOT-6152: send back to main overworld in custom ends + ResourceKey newDimension = fromEnd ? respawnData.dimension() : Level.END; + BlockPos spawnBlockPos = fromEnd ? respawnData.pos() : ServerLevel.END_SPAWN_POINT; + ServerLevel newLevel = currentLevel.getServer().getLevel(newDimension); +@@ -91,7 +_,7 @@ + float xRot; + Set relatives; + if (!fromEnd) { +- EndPlatformFeature.createEndPlatform(newLevel, BlockPos.containing(spawnPos).below(), true); ++ EndPlatformFeature.createEndPlatform(newLevel, BlockPos.containing(spawnPos).below(), true, entity); // CraftBukkit + yRot = Direction.WEST.toYRot(); + xRot = 0.0F; + relatives = Relative.union(Relative.DELTA, Set.of(Relative.X_ROT)); +@@ -103,15 +_,26 @@ + xRot = respawnData.pitch(); + relatives = Relative.union(Relative.DELTA, Relative.ROTATION); if (entity instanceof ServerPlayer serverPlayer) { - return serverPlayer.findRespawnPositionAndUseSpawnBlock(false, TeleportTransition.DO_NOTHING); + return serverPlayer.findRespawnPositionAndUseSpawnBlock(false, TeleportTransition.DO_NOTHING, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason.END_PORTAL); // CraftBukkit } - bottomCenter = entity.adjustSpawnLocation(level1, blockPos).getBottomCenter(); + spawnPos = entity.adjustSpawnLocation(newLevel, spawnBlockPos).getBottomCenter(); } -- return new TeleportTransition( -- level1, bottomCenter, Vec3.ZERO, f, f1, set, TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET) -- ); + // CraftBukkit start -+ set.removeAll(Relative.ROTATION); // remove relative rotation flags to simplify event mutation -+ float absoluteYaw = !flag ? f : entity.getYRot() + f; -+ float absolutePitch = entity.getXRot() + f1; -+ org.bukkit.craftbukkit.event.PortalEventResult result = org.bukkit.craftbukkit.event.CraftEventFactory.handlePortalEvents(entity, org.bukkit.craftbukkit.util.CraftLocation.toBukkit(bottomCenter, level1, absoluteYaw, absolutePitch), org.bukkit.PortalType.ENDER, 0, 0); ++ relatives.removeAll(Relative.ROTATION); // remove relative rotation flags to simplify event mutation ++ float absoluteYaw = !fromEnd ? yRot : entity.getYRot() + yRot; ++ float absolutePitch = entity.getXRot() + xRot; ++ org.bukkit.craftbukkit.event.PortalEventResult result = org.bukkit.craftbukkit.event.CraftEventFactory.handlePortalEvents(entity, org.bukkit.craftbukkit.util.CraftLocation.toBukkit(spawnPos, newLevel, absoluteYaw, absolutePitch), org.bukkit.PortalType.ENDER, 0, 0); + if (result == null) { + return null; + } + org.bukkit.Location to = result.to(); + -+ return new TeleportTransition(((org.bukkit.craftbukkit.CraftWorld) to.getWorld()).getHandle(), org.bukkit.craftbukkit.util.CraftLocation.toVec3(to), Vec3.ZERO, to.getYaw(), to.getPitch(), set, TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET), org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_PORTAL); + return new TeleportTransition( +- newLevel, spawnPos, Vec3.ZERO, yRot, xRot, relatives, TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET) ++ ((org.bukkit.craftbukkit.CraftWorld) to.getWorld()).getHandle(), org.bukkit.craftbukkit.util.CraftLocation.toVec3(to), Vec3.ZERO, to.getYaw(), to.getPitch(), relatives, TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET), org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_PORTAL + ); + // CraftBukkit end } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch index 30b8c15a5292..80ee1af7526e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch @@ -1,26 +1,22 @@ --- a/net/minecraft/world/level/block/EnderChestBlock.java +++ b/net/minecraft/world/level/block/EnderChestBlock.java -@@ -78,16 +_,17 @@ - PlayerEnderChestContainer enderChestInventory = player.getEnderChestInventory(); - if (enderChestInventory != null && level.getBlockEntity(pos) instanceof EnderChestBlockEntity enderChestBlockEntity) { - BlockPos blockPos = pos.above(); -- if (level.getBlockState(blockPos).isRedstoneConductor(level, blockPos)) { -+ if (level.getBlockState(blockPos).isRedstoneConductor(level, blockPos)) { // Paper - diff on change; make sure that EnderChest#isBlocked uses the same logic +@@ -82,14 +_,15 @@ + PlayerEnderChestContainer container = player.getEnderChestInventory(); + if (container != null && level.getBlockEntity(pos) instanceof EnderChestBlockEntity enderChest) { + BlockPos above = pos.above(); +- if (level.getBlockState(above).isRedstoneConductor(level, above)) { ++ if (level.getBlockState(above).isRedstoneConductor(level, above)) { // Paper - diff on change; make sure that EnderChest#isBlocked uses the same logic return InteractionResult.SUCCESS; } else { - if (level instanceof ServerLevel serverLevel) { -- enderChestInventory.setActiveChest(enderChestBlockEntity); +- container.setActiveChest(enderChest); - player.openMenu( -- new SimpleMenuProvider( -- (containerId, playerInventory, player1) -> ChestMenu.threeRows(containerId, playerInventory, enderChestInventory), CONTAINER_TITLE -- ) +- new SimpleMenuProvider((containerId, inventory, p) -> ChestMenu.threeRows(containerId, inventory, container), CONTAINER_TITLE) - ); + // Paper start - Fix InventoryOpenEvent cancellation - moved up; -+ enderChestInventory.setActiveChest(enderChestBlockEntity); // Needs to happen before ChestMenu.threeRows as it is required for opening animations ++ container.setActiveChest(enderChest); // Needs to happen before ChestMenu.threeRows as it is required for opening animations + if (level instanceof ServerLevel serverLevel && player.openMenu( -+ new SimpleMenuProvider( -+ (containerId, playerInventory, player1) -> ChestMenu.threeRows(containerId, playerInventory, enderChestInventory), CONTAINER_TITLE -+ ) ++ new SimpleMenuProvider((containerId, inventory, p) -> ChestMenu.threeRows(containerId, inventory, container), CONTAINER_TITLE) + ).isPresent()) { + // Paper end - Fix InventoryOpenEvent cancellation - moved up; player.awardStat(Stats.OPEN_ENDERCHEST); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/EyeblossomBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/EyeblossomBlock.java.patch index 729776be28cf..163dc5621cb9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/EyeblossomBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/EyeblossomBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/EyeblossomBlock.java +++ b/net/minecraft/world/level/block/EyeblossomBlock.java -@@ -100,6 +_,7 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -106,6 +_,7 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (!level.isClientSide() && level.getDifficulty() != Difficulty.PEACEFUL diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/FarmBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/FarmlandBlock.java.patch similarity index 51% rename from paper-server/patches/sources/net/minecraft/world/level/block/FarmBlock.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/FarmlandBlock.java.patch index 145e5b929c5c..e9491079cab5 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/FarmBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/FarmlandBlock.java.patch @@ -1,49 +1,49 @@ ---- a/net/minecraft/world/level/block/FarmBlock.java -+++ b/net/minecraft/world/level/block/FarmBlock.java -@@ -95,31 +_,59 @@ +--- a/net/minecraft/world/level/block/FarmlandBlock.java ++++ b/net/minecraft/world/level/block/FarmlandBlock.java +@@ -94,31 +_,59 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { - int moistureValue = state.getValue(MOISTURE); -+ if (moistureValue > 0 && level.paperConfig().tickRates.wetFarmland != 1 && (level.paperConfig().tickRates.wetFarmland < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % level.paperConfig().tickRates.wetFarmland != 0)) { return; } // Paper - Configurable random tick rates for blocks -+ if (moistureValue == 0 && level.paperConfig().tickRates.dryFarmland != 1 && (level.paperConfig().tickRates.dryFarmland < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % level.paperConfig().tickRates.dryFarmland != 0)) { return; } // Paper - Configurable random tick rates for blocks + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { + int moisture = state.getValue(MOISTURE); ++ if (moisture > 0 && level.paperConfig().tickRates.wetFarmland != 1 && (level.paperConfig().tickRates.wetFarmland < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % level.paperConfig().tickRates.wetFarmland != 0)) { return; } // Paper - Configurable random tick rates for blocks ++ if (moisture == 0 && level.paperConfig().tickRates.dryFarmland != 1 && (level.paperConfig().tickRates.dryFarmland < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % level.paperConfig().tickRates.dryFarmland != 0)) { return; } // Paper - Configurable random tick rates for blocks if (!isNearWater(level, pos) && !level.isRainingAt(pos.above())) { - if (moistureValue > 0) { -- level.setBlock(pos, state.setValue(MOISTURE, moistureValue - 1), Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(level, pos, state.setValue(MOISTURE, moistureValue - 1), Block.UPDATE_CLIENTS); // CraftBukkit + if (moisture > 0) { +- level.setBlock(pos, state.setValue(MOISTURE, moisture - 1), Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(level, pos, state.setValue(MOISTURE, moisture - 1), Block.UPDATE_CLIENTS); // CraftBukkit } else if (!shouldMaintainFarmland(level, pos)) { turnToDirt(null, state, level, pos); } - } else if (moistureValue < 7) { + } else if (moisture < 7) { - level.setBlock(pos, state.setValue(MOISTURE, 7), Block.UPDATE_CLIENTS); + org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(level, pos, state.setValue(MOISTURE, 7), Block.UPDATE_CLIENTS); // CraftBukkit } } @Override - public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, double fallDistance) { + public void fallOn(final Level level, final BlockState state, final BlockPos pos, final Entity entity, final double fallDistance) { + super.fallOn(level, state, pos, entity, fallDistance); // CraftBukkit - moved here as game rules / events shouldn't affect fall damage. if (level instanceof ServerLevel serverLevel - && level.random.nextFloat() < fallDistance - 0.5 + && level.getRandom().nextFloat() < fallDistance - 0.5 && entity instanceof LivingEntity && (entity instanceof Player || serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { -+ // CraftBukkit start - Interact soil -+ org.bukkit.event.Cancellable cancellable; -+ if (entity instanceof Player) { -+ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((Player) entity, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null); -+ } else { -+ cancellable = new org.bukkit.event.entity.EntityInteractEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)); -+ level.getCraftServer().getPluginManager().callEvent((org.bukkit.event.entity.EntityInteractEvent) cancellable); -+ } ++ // CraftBukkit start - Interact soil ++ org.bukkit.event.Cancellable cancellable; ++ if (entity instanceof Player) { ++ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((Player) entity, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null); ++ } else { ++ cancellable = new org.bukkit.event.entity.EntityInteractEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)); ++ level.getCraftServer().getPluginManager().callEvent((org.bukkit.event.entity.EntityInteractEvent) cancellable); ++ } + -+ if (cancellable.isCancelled()) { -+ return; -+ } ++ if (cancellable.isCancelled()) { ++ return; ++ } + -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.DIRT.defaultBlockState())) { -+ return; -+ } -+ // CraftBukkit end ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.DIRT.defaultBlockState())) { ++ return; ++ } ++ // CraftBukkit end turnToDirt(entity, state, level, pos); } @@ -51,22 +51,22 @@ + // super.fallOn(level, state, pos, entity, fallDistance); // CraftBukkit - moved up } - public static void turnToDirt(@Nullable Entity entity, BlockState state, Level level, BlockPos pos) { + public static void turnToDirt(final @Nullable Entity sourceEntity, final BlockState state, final Level level, final BlockPos pos) { + // CraftBukkit start -+ if (entity == null) { ++ if (sourceEntity == null) { + if (org.bukkit.craftbukkit.event.CraftEventFactory + .callBlockFadeEvent(level, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) { + return; + } + } + // CraftBukkit end - BlockState blockState = pushEntitiesUp(state, Blocks.DIRT.defaultBlockState(), level, pos); - level.setBlockAndUpdate(pos, blockState); - level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(entity, blockState)); -@@ -130,13 +_,27 @@ + BlockState newState = pushEntitiesUp(state, Blocks.DIRT.defaultBlockState(), level, pos); + level.setBlockAndUpdate(pos, newState); + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(sourceEntity, newState)); +@@ -129,13 +_,27 @@ } - private static boolean isNearWater(LevelReader level, BlockPos pos) { + private static boolean isNearWater(final LevelReader level, final BlockPos pos) { - for (BlockPos blockPos : BlockPos.betweenClosed(pos.offset(-4, 0, -4), pos.offset(4, 1, 4))) { - if (level.getFluidState(blockPos).is(FluidTags.WATER)) { - return true; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/FenceGateBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/FenceGateBlock.java.patch index c9a1747db626..aadfc7e392fe 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/FenceGateBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/FenceGateBlock.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/world/level/block/FenceGateBlock.java +++ b/net/minecraft/world/level/block/FenceGateBlock.java -@@ -199,6 +_,17 @@ - protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { +@@ -188,6 +_,17 @@ + ) { if (!level.isClientSide()) { - boolean hasNeighborSignal = level.hasNeighborSignal(pos); + boolean hasPower = level.hasNeighborSignal(pos); + // CraftBukkit start + boolean oldPowered = state.getValue(FenceGateBlock.POWERED); -+ if (oldPowered != hasNeighborSignal) { -+ int newPower = hasNeighborSignal ? 15 : 0; ++ if (oldPowered != hasPower) { ++ int newPower = hasPower ? 15 : 0; + int oldPower = oldPowered ? 15 : 0; + org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(bukkitBlock, oldPower, newPower); + level.getCraftServer().getPluginManager().callEvent(eventRedstone); -+ hasNeighborSignal = eventRedstone.getNewCurrent() > 0; ++ hasPower = eventRedstone.getNewCurrent() > 0; + } + // CraftBukkit end - if (state.getValue(POWERED) != hasNeighborSignal) { - level.setBlock(pos, state.setValue(POWERED, hasNeighborSignal).setValue(OPEN, hasNeighborSignal), Block.UPDATE_CLIENTS); - if (state.getValue(OPEN) != hasNeighborSignal) { + if (state.getValue(POWERED) != hasPower) { + level.setBlock(pos, state.setValue(POWERED, hasPower).setValue(OPEN, hasPower), Block.UPDATE_CLIENTS); + if (state.getValue(OPEN) != hasPower) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/FireBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/FireBlock.java.patch index eb63a4c8835e..c4b2256bb2fd 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/FireBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/FireBlock.java.patch @@ -1,28 +1,28 @@ --- a/net/minecraft/world/level/block/FireBlock.java +++ b/net/minecraft/world/level/block/FireBlock.java @@ -99,7 +_,25 @@ - BlockState neighborState, - RandomSource random + final BlockState neighbourState, + final RandomSource random ) { - return this.canSurvive(state, level, pos) ? this.getStateWithAge(level, pos, state.getValue(AGE)) : Blocks.AIR.defaultBlockState(); + // CraftBukkit start -+ if (!(level instanceof ServerLevel)) return this.canSurvive(state, level, pos) ? (BlockState) this.getStateWithAge(level, pos, (Integer) state.getValue(FireBlock.AGE)) : Blocks.AIR.defaultBlockState(); // Paper - don't fire events in world generation ++ if (!(level instanceof ServerLevel)) return this.canSurvive(state, level, pos) ? this.getStateWithAge(level, pos, state.getValue(AGE)) : Blocks.AIR.defaultBlockState(); // Paper - don't fire events in world generation + if (!this.canSurvive(state, level, pos)) { + // Suppress during worldgen -+ if (!(level instanceof Level world1)) { ++ if (!(level instanceof Level world)) { + return Blocks.AIR.defaultBlockState(); + } -+ org.bukkit.craftbukkit.block.CraftBlockState blockState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(world1, pos); -+ blockState.setData(Blocks.AIR.defaultBlockState()); ++ org.bukkit.craftbukkit.block.CraftBlockState snapshot = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(world, pos); ++ snapshot.setBlock(Blocks.AIR.defaultBlockState()); + -+ org.bukkit.event.block.BlockFadeEvent event = new org.bukkit.event.block.BlockFadeEvent(blockState.getBlock(), blockState); -+ world1.getCraftServer().getPluginManager().callEvent(event); ++ org.bukkit.event.block.BlockFadeEvent event = new org.bukkit.event.block.BlockFadeEvent(snapshot.getBlock(), snapshot); ++ world.getCraftServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { -+ return blockState.getHandle(); ++ return snapshot.getHandle(); + } + } -+ return this.getStateWithAge(level, pos, (Integer) state.getValue(FireBlock.AGE)); // Paper - don't fire events in world generation; diff on change, see "don't fire events in world generation" ++ return this.getStateWithAge(level, pos, state.getValue(AGE)); // Paper - don't fire events in world generation; diff on change, see "don't fire events in world generation" + // CraftBukkit end } @@ -30,8 +30,8 @@ @@ -139,17 +_,17 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { -- level.scheduleTick(pos, this, getFireTickDelay(level.random)); + protected void tick(BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { +- level.scheduleTick(pos, this, getFireTickDelay(level.getRandom())); + level.scheduleTick(pos, this, getFireTickDelay(level)); // Paper - Add fire-tick-delay option if (level.canSpreadFireAround(pos)) { if (!state.canSurvive(level, pos)) { @@ -39,19 +39,19 @@ + this.fireExtinguished(level, pos); // CraftBukkit - invalid place location } - BlockState blockState = level.getBlockState(pos.below()); - boolean isTag = blockState.is(level.dimensionType().infiniburn()); - int ageValue = state.getValue(AGE); - if (!isTag && level.isRaining() && this.isNearRain(level, pos) && random.nextFloat() < 0.2F + ageValue * 0.03F) { + BlockState belowState = level.getBlockState(pos.below()); + boolean infiniBurn = belowState.is(level.dimensionType().infiniburn()); + int age = state.getValue(AGE); + if (!infiniBurn && level.isRaining() && this.isNearRain(level, pos) && random.nextFloat() < 0.2F + age * 0.03F) { - level.removeBlock(pos, false); + this.fireExtinguished(level, pos); // CraftBukkit - extinguished by rain } else { - int min = Math.min(15, ageValue + random.nextInt(3) / 2); - if (ageValue != min) { + int newAge = Math.min(15, age + random.nextInt(3) / 2); + if (age != newAge) { @@ -161,26 +_,28 @@ if (!this.isValidFireLocation(level, pos)) { - BlockPos blockPos = pos.below(); - if (!level.getBlockState(blockPos).isFaceSturdy(level, blockPos, Direction.UP) || ageValue > 3) { + BlockPos below = pos.below(); + if (!level.getBlockState(below).isFaceSturdy(level, below, Direction.UP) || age > 3) { - level.removeBlock(pos, false); + this.fireExtinguished(level, pos); // CraftBukkit } @@ -59,44 +59,44 @@ return; } - if (ageValue == 15 && random.nextInt(4) == 0 && !this.canBurn(level.getBlockState(pos.below()))) { + if (age == 15 && random.nextInt(4) == 0 && !this.canBurn(level.getBlockState(pos.below()))) { - level.removeBlock(pos, false); + this.fireExtinguished(level, pos); // CraftBukkit return; } } - boolean value = level.environmentAttributes().getValue(EnvironmentAttributes.INCREASED_FIRE_BURNOUT, pos); - int i = value ? -50 : 0; -- this.checkBurnOut(level, pos.east(), 300 + i, random, ageValue); -- this.checkBurnOut(level, pos.west(), 300 + i, random, ageValue); -- this.checkBurnOut(level, pos.below(), 250 + i, random, ageValue); -- this.checkBurnOut(level, pos.above(), 250 + i, random, ageValue); -- this.checkBurnOut(level, pos.north(), 300 + i, random, ageValue); -- this.checkBurnOut(level, pos.south(), 300 + i, random, ageValue); + boolean increasedBurnout = level.environmentAttributes().getValue(EnvironmentAttributes.INCREASED_FIRE_BURNOUT, pos); + int extra = increasedBurnout ? -50 : 0; +- this.checkBurnOut(level, pos.east(), 300 + extra, random, age); +- this.checkBurnOut(level, pos.west(), 300 + extra, random, age); +- this.checkBurnOut(level, pos.below(), 250 + extra, random, age); +- this.checkBurnOut(level, pos.above(), 250 + extra, random, age); +- this.checkBurnOut(level, pos.north(), 300 + extra, random, age); +- this.checkBurnOut(level, pos.south(), 300 + extra, random, age); + // CraftBukkit start - add source blockPos to burn calls -+ this.checkBurnOut(level, pos.east(), 300 + i, random, ageValue, pos); -+ this.checkBurnOut(level, pos.west(), 300 + i, random, ageValue, pos); -+ this.checkBurnOut(level, pos.below(), 250 + i, random, ageValue, pos); -+ this.checkBurnOut(level, pos.above(), 250 + i, random, ageValue, pos); -+ this.checkBurnOut(level, pos.north(), 300 + i, random, ageValue, pos); -+ this.checkBurnOut(level, pos.south(), 300 + i, random, ageValue, pos); ++ this.checkBurnOut(level, pos.east(), 300 + extra, random, age, pos); ++ this.checkBurnOut(level, pos.west(), 300 + extra, random, age, pos); ++ this.checkBurnOut(level, pos.below(), 250 + extra, random, age, pos); ++ this.checkBurnOut(level, pos.above(), 250 + extra, random, age, pos); ++ this.checkBurnOut(level, pos.north(), 300 + extra, random, age, pos); ++ this.checkBurnOut(level, pos.south(), 300 + extra, random, age, pos); + // CraftBukkit end - add source blockPos to burn calls - BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + BlockPos.MutableBlockPos testPos = new BlockPos.MutableBlockPos(); - for (int i1 = -1; i1 <= 1; i1++) { + for (int xx = -1; xx <= 1; xx++) { @@ -202,7 +_,15 @@ - if (i5 > 0 && random.nextInt(i4) <= i5 && (!level.isRaining() || !this.isNearRain(level, mutableBlockPos))) { - int min1 = Math.min(15, ageValue + random.nextInt(5) / 4); -- level.setBlock(mutableBlockPos, this.getStateWithAge(level, mutableBlockPos, min1), Block.UPDATE_ALL); + if (odds > 0 && random.nextInt(rate) <= odds && (!level.isRaining() || !this.isNearRain(level, testPos))) { + int spreadAge = Math.min(15, age + random.nextInt(5) / 4); +- level.setBlock(testPos, this.getStateWithAge(level, testPos, spreadAge), Block.UPDATE_ALL); + // CraftBukkit start - Call to stop spread of fire -+ if (!level.getBlockState(mutableBlockPos).is(Blocks.FIRE)) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, mutableBlockPos, pos).isCancelled()) { ++ if (!level.getBlockState(testPos).is(Blocks.FIRE)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, testPos, pos).isCancelled()) { + continue; + } + -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, mutableBlockPos, this.getStateWithAge(level, mutableBlockPos, min1), UPDATE_ALL); // CraftBukkit ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, testPos, this.getStateWithAge(level, testPos, spreadAge), UPDATE_ALL); // CraftBukkit + } + // CraftBukkit end } @@ -106,11 +106,11 @@ : this.igniteOdds.getInt(state.getBlock()); } -- private void checkBurnOut(Level level, BlockPos pos, int chance, RandomSource random, int age) { -+ private void checkBurnOut(Level level, BlockPos pos, int chance, RandomSource random, int age, BlockPos sourcePos) { // CraftBukkit add sourcePos - int burnOdds = this.getBurnOdds(level.getBlockState(pos)); - if (random.nextInt(chance) < burnOdds) { - BlockState blockState = level.getBlockState(pos); +- private void checkBurnOut(final Level level, final BlockPos pos, final int chance, final RandomSource random, final int age) { ++ private void checkBurnOut(final Level level, final BlockPos pos, final int chance, final RandomSource random, final int age, final BlockPos sourcePos) { // CraftBukkit add sourcePos + int odds = this.getBurnOdds(level.getBlockState(pos)); + if (random.nextInt(chance) < odds) { + BlockState oldState = level.getBlockState(pos); + + // CraftBukkit start + org.bukkit.block.Block burnBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); @@ -121,19 +121,19 @@ + return; + } + -+ if (blockState.getBlock() instanceof TntBlock && !org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.FIRE, null, sourcePos)) { ++ if (oldState.getBlock() instanceof TntBlock && !org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.FIRE, null, sourcePos)) { + return; + } + // CraftBukkit end if (random.nextInt(age + 10) < 5 && !level.isRainingAt(pos)) { - int min = Math.min(age + random.nextInt(5) / 4, 15); - level.setBlock(pos, this.getStateWithAge(level, pos, min), Block.UPDATE_ALL); + int newAge = Math.min(age + random.nextInt(5) / 4, 15); + level.setBlock(pos, this.getStateWithAge(level, pos, newAge), Block.UPDATE_ALL); } else { - level.removeBlock(pos, false); -+ if (!blockState.is(Blocks.TNT)) level.removeBlock(pos, false); // Paper - TNTPrimeEvent; We might be cancelling it below, move the setAir down ++ if (!oldState.is(Blocks.TNT)) level.removeBlock(pos, false); // Paper - TNTPrimeEvent; We might be cancelling it below, move the setAir down } - Block block = blockState.getBlock(); + Block block = oldState.getBlock(); if (block instanceof TntBlock) { + // Paper start - TNTPrimeEvent + org.bukkit.block.Block tntBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); @@ -145,23 +145,24 @@ TntBlock.prime(level, pos); } } -@@ -287,13 +_,14 @@ +@@ -287,13 +_,15 @@ } @Override -- protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { +- protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) { - super.onPlace(state, level, pos, oldState, movedByPiston); -- level.scheduleTick(pos, this, getFireTickDelay(level.random)); -+ protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston, net.minecraft.world.item.context.UseOnContext context) { +- level.scheduleTick(pos, this, getFireTickDelay(level.getRandom())); ++ // CraftBukkit start ++ protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston, final net.minecraft.world.item.context.UseOnContext context) { + super.onPlace(state, level, pos, oldState, movedByPiston, context); + // CraftBukkit end + level.scheduleTick(pos, this, FireBlock.getFireTickDelay(level)); // Paper - Add fire-tick-delay option } -- private static int getFireTickDelay(RandomSource random) { +- private static int getFireTickDelay(final RandomSource random) { - return 30 + random.nextInt(10); -+ private static int getFireTickDelay(Level level) { // Paper - Add fire-tick-delay option -+ return level.paperConfig().environment.fireTickDelay + level.random.nextInt(10); // Paper - Add fire-tick-delay option ++ private static int getFireTickDelay(final Level level) { // Paper - Add fire-tick-delay option ++ return level.paperConfig().environment.fireTickDelay + level.getRandom().nextInt(10); // Paper - Add fire-tick-delay option } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/FlowerPotBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/FlowerPotBlock.java.patch index 6bd8c0239747..34e617023453 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/FlowerPotBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/FlowerPotBlock.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/level/block/FlowerPotBlock.java +++ b/net/minecraft/world/level/block/FlowerPotBlock.java -@@ -67,6 +_,18 @@ +@@ -72,6 +_,18 @@ } else if (!this.isEmpty()) { return InteractionResult.CONSUME; } else { + // Paper start - Add PlayerFlowerPotManipulateEvent + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); -+ org.bukkit.inventory.ItemStack placedStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(stack); ++ org.bukkit.inventory.ItemStack placedStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemStack); + + io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent event = new io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent((org.bukkit.entity.Player) player.getBukkitEntity(), block, placedStack, true); + if (!event.callEvent()) { @@ -16,13 +16,13 @@ + return InteractionResult.CONSUME; + } + // Paper end - Add PlayerFlowerPotManipulateEvent - level.setBlock(pos, blockState, Block.UPDATE_ALL); + level.setBlock(pos, newContents, Block.UPDATE_ALL); level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos); player.awardStat(Stats.POT_FLOWER); -@@ -81,6 +_,18 @@ +@@ -88,6 +_,18 @@ return InteractionResult.CONSUME; } else { - ItemStack itemStack = new ItemStack(this.potted); + ItemStack plant = new ItemStack(this.potted); + // Paper start - Add PlayerFlowerPotManipulateEvent + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + org.bukkit.inventory.ItemStack pottedStack = new org.bukkit.inventory.ItemStack(org.bukkit.craftbukkit.block.CraftBlockType.minecraftToBukkit(this.potted)); @@ -35,6 +35,6 @@ + return InteractionResult.PASS; + } + // Paper end - Add PlayerFlowerPotManipulateEvent - if (!player.addItem(itemStack)) { - player.drop(itemStack, false); + if (!player.addItem(plant)) { + player.drop(plant, false); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/FrogspawnBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/FrogspawnBlock.java.patch index d00e1897c1c7..86a07210ae37 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/FrogspawnBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/FrogspawnBlock.java.patch @@ -1,17 +1,17 @@ --- a/net/minecraft/world/level/block/FrogspawnBlock.java +++ b/net/minecraft/world/level/block/FrogspawnBlock.java -@@ -90,6 +_,7 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -99,6 +_,7 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent - if (entity.getType().equals(EntityType.FALLING_BLOCK)) { + if (entity.is(EntityType.FALLING_BLOCK)) { this.destroyBlock(level, pos); } -@@ -102,6 +_,11 @@ +@@ -111,6 +_,11 @@ } - private void hatchFrogspawn(ServerLevel level, BlockPos pos, RandomSource random) { + private void hatchFrogspawn(final ServerLevel level, final BlockPos pos, final RandomSource random) { + // Paper start - Call BlockFadeEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, Blocks.AIR.defaultBlockState()).isCancelled()) { + return; @@ -20,9 +20,9 @@ this.destroyBlock(level, pos); level.playSound(null, pos, SoundEvents.FROGSPAWN_HATCH, SoundSource.BLOCKS, 1.0F, 1.0F); this.spawnTadpoles(level, pos, random); -@@ -122,7 +_,7 @@ - int randomInt1 = random.nextInt(1, 361); - tadpole.snapTo(d, pos.getY() - 0.5, d1, randomInt1, 0.0F); +@@ -131,7 +_,7 @@ + int yRot = random.nextInt(1, 361); + tadpole.snapTo(xPos, pos.getY() - 0.5, zPos, yRot, 0.0F); tadpole.setPersistenceRequired(); - level.addFreshEntity(tadpole); + level.addFreshEntity(tadpole, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EGG); // Paper - use correct spawn reason diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/FrostedIceBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/FrostedIceBlock.java.patch index 1ad12aa315df..62998358c5a5 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/FrostedIceBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/FrostedIceBlock.java.patch @@ -3,17 +3,17 @@ @@ -43,6 +_,7 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { + if (!level.paperConfig().environment.frostedIce.enabled) return; // Paper - Frosted ice options if (random.nextInt(3) == 0 || this.fewerNeigboursThan(level, pos, 4)) { - int i = level.dimension() == Level.END ? level.getBrightness(LightLayer.BLOCK, pos) : level.getMaxLocalRawBrightness(pos); - if (i > 11 - state.getValue(AGE) - state.getLightBlock() && this.slightlyMelt(state, level, pos)) { + int brightness = level.dimension() == Level.END ? level.getBrightness(LightLayer.BLOCK, pos) : level.getMaxLocalRawBrightness(pos); + if (brightness > 11 - state.getValue(AGE) - state.getLightDampening() && this.slightlyMelt(state, level, pos)) { @@ -52,7 +_,7 @@ - mutableBlockPos.setWithOffset(pos, direction); - BlockState blockState = level.getBlockState(mutableBlockPos); - if (blockState.is(this) && !this.slightlyMelt(blockState, level, mutableBlockPos)) { -- level.scheduleTick(mutableBlockPos, this, Mth.nextInt(random, 20, 40)); -+ level.scheduleTick(mutableBlockPos, this, Mth.nextInt(random, level.paperConfig().environment.frostedIce.delay.min, level.paperConfig().environment.frostedIce.delay.max)); // Paper - Frosted ice options + neighborPos.setWithOffset(pos, direction); + BlockState neighbour = level.getBlockState(neighborPos); + if (neighbour.is(this) && !this.slightlyMelt(neighbour, level, neighborPos)) { +- level.scheduleTick(neighborPos, this, Mth.nextInt(random, 20, 40)); ++ level.scheduleTick(neighborPos, this, Mth.nextInt(random, level.paperConfig().environment.frostedIce.delay.min, level.paperConfig().environment.frostedIce.delay.max)); // Paper - Frosted ice options } } @@ -25,4 +25,4 @@ + level.scheduleTick(pos, this, Mth.nextInt(random, level.paperConfig().environment.frostedIce.delay.min, level.paperConfig().environment.frostedIce.delay.max)); // Paper - Frosted ice options } - private boolean slightlyMelt(BlockState state, Level level, BlockPos pos) { + private boolean slightlyMelt(final BlockState state, final Level level, final BlockPos pos) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/FungusBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/FungusBlock.java.patch deleted file mode 100644 index 1e6860b8a177..000000000000 --- a/paper-server/patches/sources/net/minecraft/world/level/block/FungusBlock.java.patch +++ /dev/null @@ -1,21 +0,0 @@ ---- a/net/minecraft/world/level/block/FungusBlock.java -+++ b/net/minecraft/world/level/block/FungusBlock.java -@@ -72,6 +_,17 @@ - - @Override - public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { -- this.getFeature(level).ifPresent(holder -> holder.value().place(level, level.getChunkSource().getGenerator(), random, pos)); -+ this.getFeature(level) -+ // CraftBukkit start -+ .map((value) -> { -+ if (this == Blocks.WARPED_FUNGUS) { -+ SaplingBlock.treeType = org.bukkit.TreeType.WARPED_FUNGUS; -+ } else if (this == Blocks.CRIMSON_FUNGUS) { -+ SaplingBlock.treeType = org.bukkit.TreeType.CRIMSON_FUNGUS; -+ } -+ return value; -+ }) -+ .ifPresent(holder -> holder.value().place(level, level.getChunkSource().getGenerator(), random, pos)); -+ // CraftBukkit end - } - } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/FurnaceBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/FurnaceBlock.java.patch index 7388ee1d652a..3b49693c82d2 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/FurnaceBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/FurnaceBlock.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/level/block/FurnaceBlock.java @@ -44,8 +_,7 @@ @Override - protected void openContainer(Level level, BlockPos pos, Player player) { + protected void openContainer(final Level level, final BlockPos pos, final Player player) { BlockEntity blockEntity = level.getBlockEntity(pos); - if (blockEntity instanceof FurnaceBlockEntity) { - player.openMenu((MenuProvider)blockEntity); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/GrindstoneBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/GrindstoneBlock.java.patch index 07b756010a5e..b7412f121b0b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/GrindstoneBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/GrindstoneBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/GrindstoneBlock.java +++ b/net/minecraft/world/level/block/GrindstoneBlock.java -@@ -72,8 +_,7 @@ - - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { +@@ -74,8 +_,7 @@ + protected InteractionResult useWithoutItem( + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { - if (!level.isClientSide()) { - player.openMenu(state.getMenuProvider(level, pos)); + if (!level.isClientSide() && player.openMenu(state.getMenuProvider(level, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/GrowingPlantHeadBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/GrowingPlantHeadBlock.java.patch index 0a540390f89f..e2922b98ff49 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/GrowingPlantHeadBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/GrowingPlantHeadBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/GrowingPlantHeadBlock.java +++ b/net/minecraft/world/level/block/GrowingPlantHeadBlock.java -@@ -44,13 +_,31 @@ +@@ -48,13 +_,31 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { - if (state.getValue(AGE) < 25 && random.nextDouble() < this.growPerTickProbability) { + // Spigot start + int modifier = 100; @@ -16,21 +16,21 @@ + } else if (this == Blocks.CAVE_VINES) { + modifier = level.spigotConfig.caveVinesModifier; + } -+ if (state.getValue(AGE) < 25 && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution ++ if (state.getValue(AGE) < 25 && random.nextDouble() < ((modifier / 100.0) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution + // Spigot end - BlockPos blockPos = pos.relative(this.growthDirection); - if (this.canGrowInto(level.getBlockState(blockPos))) { -- level.setBlockAndUpdate(blockPos, this.getGrowIntoState(state, level.random)); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, this.getGrowIntoState(state, level.random, level), Block.UPDATE_ALL); // CraftBukkit // Paper - Fix Spigot growth modifiers + BlockPos growthPos = pos.relative(this.growthDirection); + if (this.canGrowInto(level.getBlockState(growthPos))) { +- level.setBlockAndUpdate(growthPos, this.getGrowIntoState(state, level.getRandom())); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, growthPos, this.getGrowIntoState(state, level.getRandom(), level), Block.UPDATE_ALL); // CraftBukkit // Paper - Fix Spigot growth modifiers } } } + + // Paper start - Fix Spigot growth modifiers -+ protected BlockState getGrowIntoState(BlockState state, RandomSource random, @javax.annotation.Nullable Level level) { ++ protected BlockState getGrowIntoState(BlockState state, RandomSource random, @org.jspecify.annotations.Nullable Level level) { + return this.getGrowIntoState(state, random); + } + // Paper end - Fix Spigot growth modifiers - protected BlockState getGrowIntoState(BlockState state, RandomSource random) { - return state.cycle(AGE); + protected BlockState getGrowIntoState(final BlockState growFromState, final RandomSource random) { + return growFromState.cycle(AGE); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/HoneyBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/HoneyBlock.java.patch index 56d52b467968..ca996ad35af6 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/HoneyBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/HoneyBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/HoneyBlock.java +++ b/net/minecraft/world/level/block/HoneyBlock.java -@@ -62,6 +_,7 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -70,6 +_,7 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (this.isSlidingDown(pos, entity)) { this.maybeDoSlideAchievement(entity, pos); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/HopperBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/HopperBlock.java.patch index 8e160a4c11cb..7b243614f309 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/HopperBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/HopperBlock.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/level/block/HopperBlock.java +++ b/net/minecraft/world/level/block/HopperBlock.java -@@ -102,8 +_,7 @@ - - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { -- if (!level.isClientSide() && level.getBlockEntity(pos) instanceof HopperBlockEntity hopperBlockEntity) { -- player.openMenu(hopperBlockEntity); -+ if (!level.isClientSide() && level.getBlockEntity(pos) instanceof HopperBlockEntity hopperBlockEntity && player.openMenu(hopperBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation +@@ -106,8 +_,7 @@ + protected InteractionResult useWithoutItem( + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { +- if (!level.isClientSide() && level.getBlockEntity(pos) instanceof HopperBlockEntity hopper) { +- player.openMenu(hopper); ++ if (!level.isClientSide() && level.getBlockEntity(pos) instanceof HopperBlockEntity hopper && player.openMenu(hopper).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation player.awardStat(Stats.INSPECT_HOPPER); } -@@ -154,6 +_,7 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -167,6 +_,7 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent BlockEntity blockEntity = level.getBlockEntity(pos); if (blockEntity instanceof HopperBlockEntity) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/HugeMushroomBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/HugeMushroomBlock.java.patch index d1fc0c3f4486..6e29a5cde996 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/HugeMushroomBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/HugeMushroomBlock.java.patch @@ -3,23 +3,23 @@ @@ -45,6 +_,7 @@ @Override - public BlockState getStateForPlacement(BlockPlaceContext context) { + public BlockState getStateForPlacement(final BlockPlaceContext context) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableMushroomBlockUpdates) return this.defaultBlockState(); // Paper - add option to disable block updates BlockGetter level = context.getLevel(); - BlockPos clickedPos = context.getClickedPos(); + BlockPos pos = context.getClickedPos(); return this.defaultBlockState() @@ -67,6 +_,7 @@ - BlockState neighborState, - RandomSource random + final BlockState neighbourState, + final RandomSource random ) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableMushroomBlockUpdates) return state; // Paper - add option to disable block updates - return neighborState.is(this) - ? state.setValue(PROPERTY_BY_DIRECTION.get(direction), false) - : super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); + return neighbourState.is(this) + ? state.setValue(PROPERTY_BY_DIRECTION.get(directionToNeighbour), false) + : super.updateShape(state, level, ticks, pos, directionToNeighbour, neighbourPos, neighbourState, random); @@ -74,6 +_,7 @@ @Override - protected BlockState rotate(BlockState state, Rotation rotation) { + protected BlockState rotate(final BlockState state, final Rotation rotation) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableMushroomBlockUpdates) return state; // Paper - add option to disable block updates return state.setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.NORTH)), state.getValue(NORTH)) .setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.SOUTH)), state.getValue(SOUTH)) @@ -27,7 +27,7 @@ @@ -84,6 +_,7 @@ @Override - protected BlockState mirror(BlockState state, Mirror mirror) { + protected BlockState mirror(final BlockState state, final Mirror mirror) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableMushroomBlockUpdates) return state; // Paper - add option to disable block updates return state.setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.NORTH)), state.getValue(NORTH)) .setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.SOUTH)), state.getValue(SOUTH)) diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/IceBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/IceBlock.java.patch index 8f529c109ad5..0d2f02e520a5 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/IceBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/IceBlock.java.patch @@ -1,25 +1,25 @@ --- a/net/minecraft/world/level/block/IceBlock.java +++ b/net/minecraft/world/level/block/IceBlock.java -@@ -33,8 +_,13 @@ - } - - @Override -- public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack stack) { -- super.playerDestroy(level, player, pos, state, blockEntity, stack); -+ public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack stack, boolean includeDrops, boolean dropExp) { // Paper - fix drops not preventing stats/food exhaustion -+ super.playerDestroy(level, player, pos, state, blockEntity, stack, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion +@@ -40,8 +_,14 @@ + final BlockState state, + final @Nullable BlockEntity blockEntity, + final ItemStack destroyedWith ++ , boolean includeDrops, boolean dropExp // Paper - fix drops not preventing stats/food exhaustion + ) { +- super.playerDestroy(level, player, pos, state, blockEntity, destroyedWith); ++ super.playerDestroy(level, player, pos, state, blockEntity, destroyedWith, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion + // Paper start - Improve Block#breakNaturally API -+ this.afterDestroy(level, pos, stack); ++ this.afterDestroy(level, pos, destroyedWith); + } -+ public void afterDestroy(Level level, BlockPos pos, ItemStack stack) { ++ public void afterDestroy(Level level, BlockPos pos, ItemStack destroyedWith) { + // Paper end - Improve Block#breakNaturally API - if (!EnchantmentHelper.hasTag(stack, EnchantmentTags.PREVENTS_ICE_MELTING)) { + if (!EnchantmentHelper.hasTag(destroyedWith, EnchantmentTags.PREVENTS_ICE_MELTING)) { if (level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos)) { level.removeBlock(pos, false); -@@ -56,7 +_,13 @@ +@@ -63,7 +_,13 @@ } - protected void melt(BlockState state, Level level, BlockPos pos) { + protected void melt(final BlockState state, final Level level, final BlockPos pos) { - if (level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos)) { + // Paper start - call BlockFadeEvent + final boolean waterEvaporates = level.environmentAttributes().getValue(EnvironmentAttributes.WATER_EVAPORATES, pos); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LavaCauldronBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LavaCauldronBlock.java.patch index 2423a53edb45..6cc7719d2c16 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LavaCauldronBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LavaCauldronBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/LavaCauldronBlock.java +++ b/net/minecraft/world/level/block/LavaCauldronBlock.java -@@ -45,9 +_,11 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -52,9 +_,11 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + BlockPos savedPos = pos.immutable(); // Paper - track lava contact effectApplier.apply(InsideBlockEffectType.CLEAR_FREEZE); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LayeredCauldronBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LayeredCauldronBlock.java.patch index c1201af2871d..61ca775594c3 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LayeredCauldronBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LayeredCauldronBlock.java.patch @@ -1,32 +1,32 @@ --- a/net/minecraft/world/level/block/LayeredCauldronBlock.java +++ b/net/minecraft/world/level/block/LayeredCauldronBlock.java -@@ -80,39 +_,71 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -90,38 +_,57 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (level instanceof ServerLevel serverLevel) { BlockPos blockPos = pos.immutable(); - effectApplier.runBefore(InsideBlockEffectType.EXTINGUISH, entity1 -> { -- if (entity1.isOnFire() && entity1.mayInteract(serverLevel, blockPos)) { + effectApplier.runBefore(InsideBlockEffectType.EXTINGUISH, e -> { +- if (e.isOnFire() && e.mayInteract(serverLevel, blockPos)) { - this.handleEntityOnFireInside(state, level, blockPos); -+ if (entity1.isOnFire() && (entity instanceof net.minecraft.world.entity.player.Player || serverLevel.getGameRules().get(net.minecraft.world.level.gamerules.GameRules.MOB_GRIEFING)) && entity1.mayInteract(serverLevel, blockPos)) { // Paper - Fixes MC-248588 -+ // Paper start - cauldron level change event -+ if (this.handleEntityOnFireInside(state, level, blockPos, entity1)) { // Paper - track entity -+ InsideBlockEffectType.EXTINGUISH.effect().affect(entity1, blockPos); // apply extinguishing if event was not cancelled. ++ if (e.isOnFire() && (entity instanceof net.minecraft.world.entity.player.Player || serverLevel.getGameRules().get(net.minecraft.world.level.gamerules.GameRules.MOB_GRIEFING)) && e.mayInteract(serverLevel, blockPos)) { // Paper - Fixes MC-248588 ++ // Paper start - Call CauldronLevelChangeEvent ++ if (this.handleEntityOnFireInside(state, level, blockPos, e)) { // Paper - track entity ++ InsideBlockEffectType.EXTINGUISH.effect().affect(e, blockPos); // apply extinguishing if event was not cancelled. + } -+ // Paper end - cauldron level change event ++ // Paper end - Call CauldronLevelChangeEvent } }); } - effectApplier.apply(InsideBlockEffectType.EXTINGUISH); -+ // effectApplier.apply(InsideBlockEffectType.EXTINGUISH); // Paper - manually applied above - cauldron level change event - delay to not extinguish when event is cancelled ++ // effectApplier.apply(InsideBlockEffectType.EXTINGUISH); // Paper - manually applied above - CauldronLevelChangeEvent - delay to not extinguish when event is cancelled } -- private void handleEntityOnFireInside(BlockState state, Level level, BlockPos pos) { +- private void handleEntityOnFireInside(final BlockState state, final Level level, final BlockPos pos) { + // CraftBukkit start -+ private boolean handleEntityOnFireInside(BlockState state, Level level, BlockPos pos, @javax.annotation.Nullable Entity entity) { ++ private boolean handleEntityOnFireInside(final BlockState state, final Level level, final BlockPos pos, @org.jspecify.annotations.Nullable Entity entity) { if (this.precipitationType == Biome.Precipitation.SNOW) { - lowerFillLevel(Blocks.WATER_CAULDRON.defaultBlockState().setValue(LEVEL, state.getValue(LEVEL)), level, pos); + return lowerFillLevel(Blocks.WATER_CAULDRON.defaultBlockState().setValue(LEVEL, state.getValue(LEVEL)), level, pos, entity, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.EXTINGUISH); // CraftBukkit @@ -36,64 +36,47 @@ } } - public static void lowerFillLevel(BlockState state, Level level, BlockPos pos) { -+ // Paper start + public static void lowerFillLevel(final BlockState state, final Level level, final BlockPos pos) { ++ // Paper start - Call CauldronLevelChangeEvent + lowerFillLevel(state, level, pos, null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.UNKNOWN); + } -+ public static boolean lowerFillLevel(BlockState state, Level level, BlockPos pos, @javax.annotation.Nullable Entity entity, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason reason) { -+ // Paper end - int i = state.getValue(LEVEL) - 1; - BlockState blockState = i == 0 ? Blocks.CAULDRON.defaultBlockState() : state.setValue(LEVEL, i); -- level.setBlockAndUpdate(pos, blockState); -- level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(blockState)); -- } -+ return changeLevel(level, pos, blockState, entity, reason); // Paper -+ } -+ // CraftBukkit start -+ // Paper start - Call CauldronLevelChangeEvent -+ public static boolean changeLevel(Level level, BlockPos pos, BlockState newBlock, @javax.annotation.Nullable Entity entity, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason reason) { // Paper - entity is nullable -+ return changeLevel(level, pos, newBlock, entity, reason, true); -+ } + -+ public static boolean changeLevel(Level level, BlockPos pos, BlockState newBlock, @javax.annotation.Nullable Entity entity, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason reason, boolean sendGameEvent) { // Paper - entity is nullable ++ public static boolean lowerFillLevel(BlockState state, Level level, BlockPos pos, @org.jspecify.annotations.Nullable Entity entity, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason reason) { + // Paper end - Call CauldronLevelChangeEvent -+ org.bukkit.craftbukkit.block.CraftBlockState newState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, pos); -+ newState.setData(newBlock); -+ -+ org.bukkit.event.block.CauldronLevelChangeEvent event = new org.bukkit.event.block.CauldronLevelChangeEvent( -+ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), -+ (entity == null) ? null : entity.getBukkitEntity(), reason, newState -+ ); -+ if (!event.callEvent()) { -+ return false; -+ } -+ newState.place(Block.UPDATE_ALL); -+ if (sendGameEvent) level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newBlock)); // Paper - Call CauldronLevelChangeEvent -+ return true; -+ } -+ // CraftBukkit end + int newLevel = state.getValue(LEVEL) - 1; + BlockState newState = newLevel == 0 ? Blocks.CAULDRON.defaultBlockState() : state.setValue(LEVEL, newLevel); +- level.setBlockAndUpdate(pos, newState); +- level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newState)); ++ // Paper start - Call CauldronLevelChangeEvent ++ boolean success = org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, newState, entity, reason); ++ if (success) level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newState)); ++ return success; ++ // Paper end - Call CauldronLevelChangeEvent + } @Override - public void handlePrecipitation(BlockState state, Level level, BlockPos pos, Biome.Precipitation precipitation) { + public void handlePrecipitation(final BlockState state, final Level level, final BlockPos pos, final Biome.Precipitation precipitation) { if (CauldronBlock.shouldHandlePrecipitation(level, precipitation) && state.getValue(LEVEL) != 3 && precipitation == this.precipitationType) { - BlockState blockState = state.cycle(LEVEL); -- level.setBlockAndUpdate(pos, blockState); -- level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(blockState)); -+ changeLevel(level, pos, blockState, null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL); // CraftBukkit + BlockState newState = state.cycle(LEVEL); +- level.setBlockAndUpdate(pos, newState); ++ // CraftBukkit start ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, newState, null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { ++ return; ++ } ++ // CraftBukkit end + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newState)); } } - -@@ -130,8 +_,11 @@ - protected void receiveStalactiteDrip(BlockState state, Level level, BlockPos pos, Fluid fluid) { +@@ -140,7 +_,11 @@ + protected void receiveStalactiteDrip(final BlockState state, final Level level, final BlockPos pos, final Fluid fluid) { if (!this.isFull(state)) { - BlockState blockState = state.setValue(LEVEL, state.getValue(LEVEL) + 1); -- level.setBlockAndUpdate(pos, blockState); -- level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(blockState)); + BlockState newState = state.setValue(LEVEL, state.getValue(LEVEL) + 1); +- level.setBlockAndUpdate(pos, newState); + // CraftBukkit start -+ if (!changeLevel(level, pos, blockState, null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleCauldronLevelChangeEvent(level, pos, newState, null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { + return; + } + // CraftBukkit end + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newState)); level.levelEvent(LevelEvent.SOUND_DRIP_WATER_INTO_CAULDRON, pos, 0); } - } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LeavesBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LeavesBlock.java.patch index 8cb50108abfb..0fa6ea60b1e9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LeavesBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LeavesBlock.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/level/block/LeavesBlock.java @@ -66,6 +_,14 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (this.decaying(state)) { + // CraftBukkit start + org.bukkit.event.block.LeavesDecayEvent event = new org.bukkit.event.block.LeavesDecayEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LecternBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LecternBlock.java.patch index e8377204ce2a..6f94c9b0f45b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LecternBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LecternBlock.java.patch @@ -1,37 +1,37 @@ --- a/net/minecraft/world/level/block/LecternBlock.java +++ b/net/minecraft/world/level/block/LecternBlock.java -@@ -137,7 +_,24 @@ - - private static void placeBook(@Nullable LivingEntity entity, Level level, BlockPos pos, BlockState state, ItemStack stack) { - if (level.getBlockEntity(pos) instanceof LecternBlockEntity lecternBlockEntity) { -- lecternBlockEntity.setBook(stack.consumeAndReturn(1, entity)); +@@ -142,7 +_,24 @@ + final @Nullable LivingEntity sourceEntity, final Level level, final BlockPos pos, final BlockState state, final ItemStack book + ) { + if (level.getBlockEntity(pos) instanceof LecternBlockEntity lectern) { +- lectern.setBook(book.consumeAndReturn(1, sourceEntity)); + // Paper start - Add PlayerInsertLecternBookEvent + ItemStack eventSourcedBookStack = null; -+ if (entity instanceof final net.minecraft.server.level.ServerPlayer serverPlayer) { ++ if (sourceEntity instanceof final net.minecraft.server.level.ServerPlayer serverPlayer) { + final io.papermc.paper.event.player.PlayerInsertLecternBookEvent event = new io.papermc.paper.event.player.PlayerInsertLecternBookEvent( + serverPlayer.getBukkitEntity(), + org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), -+ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack.copyWithCount(1)) ++ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(book.copyWithCount(1)) + ); + if (!event.callEvent()) return; + eventSourcedBookStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getBook()); + } + if (eventSourcedBookStack == null) { -+ eventSourcedBookStack = stack.consumeAndReturn(1, entity); ++ eventSourcedBookStack = book.consumeAndReturn(1, sourceEntity); + } else { -+ stack.consume(1, entity); ++ book.consume(1, sourceEntity); + } -+ lecternBlockEntity.setBook(eventSourcedBookStack); ++ lectern.setBook(eventSourcedBookStack); + // Paper end - Add PlayerInsertLecternBookEvent - resetBookState(entity, level, pos, state, true); + resetBookState(sourceEntity, level, pos, state, true); level.playSound(null, pos, SoundEvents.BOOK_PUT, SoundSource.BLOCKS, 1.0F, 1.0F); } -@@ -157,6 +_,16 @@ +@@ -162,6 +_,16 @@ } - private static void changePowered(Level level, BlockPos pos, BlockState state, boolean powered) { + private static void changePowered(final Level level, final BlockPos pos, final BlockState state, final boolean isPowered) { + // Paper start - Call BlockRedstoneEvent properly -+ final int currentRedstoneLevel = state.getValue(LecternBlock.POWERED) ? 15 : 0, targetRedstoneLevel = powered ? 15 : 0; ++ final int currentRedstoneLevel = state.getValue(LecternBlock.POWERED) ? 15 : 0, targetRedstoneLevel = isPowered ? 15 : 0; + if (currentRedstoneLevel != targetRedstoneLevel) { + final org.bukkit.event.block.BlockRedstoneEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, currentRedstoneLevel, targetRedstoneLevel); + @@ -40,12 +40,12 @@ + } + } + // Paper end - Call BlockRedstoneEvent properly - level.setBlock(pos, state.setValue(POWERED, powered), Block.UPDATE_ALL); + level.setBlock(pos, state.setValue(POWERED, isPowered), Block.UPDATE_ALL); updateBelow(level, pos, state); } -@@ -243,8 +_,7 @@ +@@ -258,8 +_,7 @@ - private void openScreen(Level level, BlockPos pos, Player player) { + private void openScreen(final Level level, final BlockPos pos, final Player player) { BlockEntity blockEntity = level.getBlockEntity(pos); - if (blockEntity instanceof LecternBlockEntity) { - player.openMenu((LecternBlockEntity)blockEntity); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LeverBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LeverBlock.java.patch index 9777099fd666..faff6df7134e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LeverBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LeverBlock.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/block/LeverBlock.java +++ b/net/minecraft/world/level/block/LeverBlock.java -@@ -67,6 +_,19 @@ - makeParticle(blockState, level, pos, 1.0F); +@@ -70,6 +_,19 @@ + makeParticle(stateAfter, level, pos, 1.0F); } } else { + // CraftBukkit start - Interact Lever -+ boolean powered = state.getValue(LeverBlock.POWERED); // Old powered state ++ boolean powered = stateBefore.getValue(LeverBlock.POWERED); // Old powered state + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + int old = (powered) ? 15 : 0; + int current = (!powered) ? 15 : 0; @@ -17,6 +17,6 @@ + return InteractionResult.SUCCESS; + } + // CraftBukkit end - this.pull(state, level, pos, null); + this.pull(stateBefore, level, pos, null); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LightBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LightBlock.java.patch index 8b3d19d59cd9..27f579545177 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LightBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LightBlock.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/block/LightBlock.java +++ b/net/minecraft/world/level/block/LightBlock.java @@ -49,6 +_,13 @@ - protected void createBlockStateDefinition(StateDefinition.Builder builder) { + protected void createBlockStateDefinition(final StateDefinition.Builder builder) { builder.add(LEVEL, WATERLOGGED); } + // Paper start - prevent unintended light block manipulation @@ -13,4 +13,4 @@ + // Paper end - prevent unintended light block manipulation @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + protected InteractionResult useWithoutItem( diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LightningRodBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LightningRodBlock.java.patch index 9d7c7a1f2797..2f8d6e45cc48 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LightningRodBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LightningRodBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/LightningRodBlock.java +++ b/net/minecraft/world/level/block/LightningRodBlock.java -@@ -82,6 +_,18 @@ +@@ -83,6 +_,18 @@ } - public void onLightningStrike(BlockState state, Level level, BlockPos pos) { + public void onLightningStrike(final BlockState state, final Level level, final BlockPos pos) { + // CraftBukkit start + boolean powered = state.getValue(LightningRodBlock.POWERED); + int old = (powered) ? 15 : 0; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/WaterlilyBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LilyPadBlock.java.patch similarity index 69% rename from paper-server/patches/sources/net/minecraft/world/level/block/WaterlilyBlock.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/LilyPadBlock.java.patch index ae0899c7f52c..9d56a2eedc9f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/WaterlilyBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LilyPadBlock.java.patch @@ -1,11 +1,11 @@ ---- a/net/minecraft/world/level/block/WaterlilyBlock.java -+++ b/net/minecraft/world/level/block/WaterlilyBlock.java -@@ -30,8 +_,14 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +--- a/net/minecraft/world/level/block/LilyPadBlock.java ++++ b/net/minecraft/world/level/block/LilyPadBlock.java +@@ -39,8 +_,14 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent - super.entityInside(state, level, pos, entity, effectApplier, pastEdges); + super.entityInside(state, level, pos, entity, effectApplier, isPrecise); if (level instanceof ServerLevel && entity instanceof AbstractBoat) { + // CraftBukkit start + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, state.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LiquidBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LiquidBlock.java.patch index fc1018d1f279..178213bccbb7 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LiquidBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LiquidBlock.java.patch @@ -1,16 +1,18 @@ --- a/net/minecraft/world/level/block/LiquidBlock.java +++ b/net/minecraft/world/level/block/LiquidBlock.java -@@ -139,9 +_,30 @@ +@@ -150,7 +_,7 @@ @Override - protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) { if (this.shouldSpreadLiquid(level, pos, state)) { - level.scheduleTick(pos, state.getFluidState().getType(), this.fluid.getTickDelay(level)); -- } -- } + level.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(level, pos)); // Paper - Configurable speed for water flowing over lava -+ } -+ } -+ + } + + if (shouldBubbleColumnOccupy(state)) { +@@ -167,6 +_,27 @@ + } + } + + // Paper start - Configurable speed for water flowing over lava + public int getFlowSpeed(Level level, BlockPos pos) { + if (net.minecraft.core.registries.BuiltInRegistries.FLUID.wrapAsHolder(this.fluid).is(FluidTags.WATER)) { @@ -31,33 +33,34 @@ + return fluidState != null && fluidState.is(FluidTags.LAVA); + } + // Paper end - Configurable speed for water flowing over lava - ++ @Override protected BlockState updateShape( -@@ -164,7 +_,7 @@ - @Override - protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { + final BlockState state, +@@ -198,7 +_,7 @@ + final BlockState state, final Level level, final BlockPos pos, final Block block, final @Nullable Orientation orientation, final boolean movedByPiston + ) { if (this.shouldSpreadLiquid(level, pos, state)) { - level.scheduleTick(pos, state.getFluidState().getType(), this.fluid.getTickDelay(level)); + level.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(level, pos)); // Paper - Configurable speed for water flowing over lava } - } -@@ -176,14 +_,20 @@ - BlockPos blockPos = pos.relative(direction.getOpposite()); - if (level.getFluidState(blockPos).is(FluidTags.WATER)) { - Block block = level.getFluidState(pos).isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE; -- level.setBlockAndUpdate(pos, block.defaultBlockState()); + if (shouldBubbleColumnOccupy(state)) { +@@ -221,14 +_,20 @@ + BlockPos neighbourPos = pos.relative(direction.getOpposite()); + if (level.getFluidState(neighbourPos).is(FluidTags.WATER)) { + Block convertToBlock = level.getFluidState(pos).isSource() ? Blocks.OBSIDIAN : Blocks.COBBLESTONE; +- level.setBlockAndUpdate(pos, convertToBlock.defaultBlockState()); - this.fizz(level, pos); + // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, pos, block.defaultBlockState(), Block.UPDATE_ALL)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, pos, convertToBlock.defaultBlockState(), Block.UPDATE_ALL)) { + this.fizz(level, pos); + } + // CraftBukkit end return false; } - if (isSoulSoil && level.getBlockState(blockPos).is(Blocks.BLUE_ICE)) { + if (isOverSoulSoil && level.getBlockState(neighbourPos).is(Blocks.BLUE_ICE)) { - level.setBlockAndUpdate(pos, Blocks.BASALT.defaultBlockState()); - this.fizz(level, pos); + // CraftBukkit start diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LoomBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LoomBlock.java.patch index 99f5f7cec14c..dba6733fe91d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LoomBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LoomBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/LoomBlock.java +++ b/net/minecraft/world/level/block/LoomBlock.java -@@ -32,8 +_,7 @@ - - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { +@@ -34,8 +_,7 @@ + protected InteractionResult useWithoutItem( + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { - if (!level.isClientSide()) { - player.openMenu(state.getMenuProvider(level, pos)); + if (!level.isClientSide() && player.openMenu(state.getMenuProvider(level, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/MagmaBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/MagmaBlock.java.patch index a3fec4f2d3b9..ba551b0baf80 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/MagmaBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/MagmaBlock.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/block/MagmaBlock.java +++ b/net/minecraft/world/level/block/MagmaBlock.java -@@ -29,7 +_,7 @@ +@@ -23,7 +_,7 @@ @Override - public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) { + public void stepOn(final Level level, final BlockPos pos, final BlockState onState, final Entity entity) { if (!entity.isSteppingCarefully() && entity instanceof LivingEntity) { - entity.hurt(level.damageSources().hotFloor(), 1.0F); + entity.hurt(level.damageSources().hotFloor().eventBlockDamager(level, pos), 1.0F); // CraftBukkit } - super.stepOn(level, pos, state, entity); + super.stepOn(level, pos, onState, entity); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/MangrovePropaguleBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/MangrovePropaguleBlock.java.patch index 713e011d6d6c..1cf4d03ee288 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/MangrovePropaguleBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/MangrovePropaguleBlock.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/level/block/MangrovePropaguleBlock.java @@ -102,7 +_,7 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (!isHanging(state)) { - if (random.nextInt(7) == 0) { + if (random.nextFloat() < (level.spigotConfig.saplingModifier / (100.0F * 7))) { // Paper - Fix Spigot growth modifiers diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/MultifaceSpreader.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/MultifaceSpreader.java.patch index 0418f7e49fee..cb37ea773907 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/MultifaceSpreader.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/MultifaceSpreader.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/block/MultifaceSpreader.java +++ b/net/minecraft/world/level/block/MultifaceSpreader.java -@@ -152,14 +_,14 @@ - level.getChunk(pos.pos()).markPosForPostprocessing(pos.pos()); +@@ -176,14 +_,14 @@ + level.getChunk(spreadPos.pos()).markPosForPostprocessing(spreadPos.pos()); } -- return level.setBlock(pos.pos(), stateForPlacement, Block.UPDATE_CLIENTS); -+ return org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos.source(), pos.pos(), stateForPlacement, Block.UPDATE_CLIENTS, true); // CraftBukkit +- return level.setBlock(spreadPos.pos(), spreadState, Block.UPDATE_CLIENTS); ++ return org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, spreadPos.source(), spreadPos.pos(), spreadState, Block.UPDATE_CLIENTS, true); // CraftBukkit } else { return false; } @@ -17,26 +17,26 @@ } @FunctionalInterface -@@ -171,19 +_,19 @@ +@@ -195,19 +_,19 @@ SAME_POSITION { @Override - public MultifaceSpreader.SpreadPos getSpreadPos(BlockPos pos, Direction face, Direction spreadDirection) { -- return new MultifaceSpreader.SpreadPos(pos, face); -+ return new MultifaceSpreader.SpreadPos(pos, face, pos); // CraftBukkit + public MultifaceSpreader.SpreadPos getSpreadPos(final BlockPos pos, final Direction spreadDirection, final Direction fromFace) { +- return new MultifaceSpreader.SpreadPos(pos, spreadDirection); ++ return new MultifaceSpreader.SpreadPos(pos, spreadDirection, pos); // CraftBukkit } }, SAME_PLANE { @Override - public MultifaceSpreader.SpreadPos getSpreadPos(BlockPos pos, Direction face, Direction spreadDirection) { -- return new MultifaceSpreader.SpreadPos(pos.relative(face), spreadDirection); -+ return new MultifaceSpreader.SpreadPos(pos.relative(face), spreadDirection, pos); // CraftBukkit + public MultifaceSpreader.SpreadPos getSpreadPos(final BlockPos pos, final Direction spreadDirection, final Direction fromFace) { +- return new MultifaceSpreader.SpreadPos(pos.relative(spreadDirection), fromFace); ++ return new MultifaceSpreader.SpreadPos(pos.relative(spreadDirection), fromFace, pos); // CraftBukkit } }, WRAP_AROUND { @Override - public MultifaceSpreader.SpreadPos getSpreadPos(BlockPos pos, Direction face, Direction spreadDirection) { -- return new MultifaceSpreader.SpreadPos(pos.relative(face).relative(spreadDirection), face.getOpposite()); -+ return new MultifaceSpreader.SpreadPos(pos.relative(face).relative(spreadDirection), face.getOpposite(), pos); // CraftBukkit + public MultifaceSpreader.SpreadPos getSpreadPos(final BlockPos pos, final Direction spreadDirection, final Direction fromFace) { +- return new MultifaceSpreader.SpreadPos(pos.relative(spreadDirection).relative(fromFace), spreadDirection.getOpposite()); ++ return new MultifaceSpreader.SpreadPos(pos.relative(spreadDirection).relative(fromFace), spreadDirection.getOpposite(), pos); // CraftBukkit } }; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/MushroomBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/MushroomBlock.java.patch index 5cd752321a23..d04287fd33da 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/MushroomBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/MushroomBlock.java.patch @@ -3,26 +3,26 @@ @@ -46,7 +_,7 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, BlockPos pos, final RandomSource random) { - if (random.nextInt(25) == 0) { + if (random.nextFloat() < (level.spigotConfig.mushroomModifier / (100.0F * 25))) { // Spigot - SPIGOT-7159: Better modifier resolution - int i = 5; - int i1 = 4; + int max = 5; + int r = 4; @@ -59,6 +_,7 @@ } - BlockPos blockPos1 = pos.offset(random.nextInt(3) - 1, random.nextInt(2) - random.nextInt(2), random.nextInt(3) - 1); + BlockPos offset = pos.offset(random.nextInt(3) - 1, random.nextInt(2) - random.nextInt(2), random.nextInt(3) - 1); + final BlockPos sourcePos = pos; // Paper - Use correct source for mushroom block spread event - for (int i2 = 0; i2 < 4; i2++) { - if (level.isEmptyBlock(blockPos1) && state.canSurvive(level, blockPos1)) { + for (int i = 0; i < 4; i++) { + if (level.isEmptyBlock(offset) && state.canSurvive(level, offset)) { @@ -69,7 +_,7 @@ } - if (level.isEmptyBlock(blockPos1) && state.canSurvive(level, blockPos1)) { -- level.setBlock(blockPos1, state, Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, sourcePos, blockPos1, state, Block.UPDATE_CLIENTS); // CraftBukkit // Paper - Use correct source for mushroom block spread event + if (level.isEmptyBlock(offset) && state.canSurvive(level, offset)) { +- level.setBlock(offset, state, Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, sourcePos, offset, state, Block.UPDATE_CLIENTS); // CraftBukkit // Paper - Use correct source for mushroom block spread event } } } @@ -31,6 +31,6 @@ } else { level.removeBlock(pos, false); + SaplingBlock.treeType = (this == Blocks.BROWN_MUSHROOM) ? org.bukkit.TreeType.BROWN_MUSHROOM : org.bukkit.TreeType.RED_MUSHROOM; // CraftBukkit - if (optional.get().value().place(level, level.getChunkSource().getGenerator(), random, pos)) { + if (feature.get().value().place(level, level.getChunkSource().getGenerator(), random, pos)) { return true; } else { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/NetherFungusBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/NetherFungusBlock.java.patch new file mode 100644 index 000000000000..2b50d69daf24 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/NetherFungusBlock.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/world/level/block/NetherFungusBlock.java ++++ b/net/minecraft/world/level/block/NetherFungusBlock.java +@@ -80,6 +_,17 @@ + + @Override + public void performBonemeal(final ServerLevel level, final RandomSource random, final BlockPos pos, final BlockState state) { +- this.getFeature(level).ifPresent(feature -> feature.value().place(level, level.getChunkSource().getGenerator(), random, pos)); ++ this.getFeature(level) ++ // CraftBukkit start ++ .map((value) -> { ++ if (this == Blocks.WARPED_FUNGUS) { ++ SaplingBlock.treeType = org.bukkit.TreeType.WARPED_FUNGUS; ++ } else if (this == Blocks.CRIMSON_FUNGUS) { ++ SaplingBlock.treeType = org.bukkit.TreeType.CRIMSON_FUNGUS; ++ } ++ return value; ++ }) ++ .ifPresent(feature -> feature.value().place(level, level.getChunkSource().getGenerator(), random, pos)); ++ // CraftBukkit end + } + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/NetherPortalBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/NetherPortalBlock.java.patch index 30b239c9d2f8..18b9b33953c2 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/NetherPortalBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/NetherPortalBlock.java.patch @@ -3,7 +3,7 @@ @@ -66,7 +_,7 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, BlockPos pos, final RandomSource random) { - if (level.isSpawningMonsters() + if (level.spigotConfig.enableZombiePigmenPortalSpawns && level.isSpawningMonsters() // Spigot && level.environmentAttributes().getValue(EnvironmentAttributes.NETHER_PORTAL_SPAWNS_PIGLINS, pos) @@ -24,10 +24,10 @@ Entity vehicle = entity.getVehicle(); if (vehicle != null) { vehicle.setPortalCooldown(); -@@ -108,7 +_,13 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -115,7 +_,13 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (entity.canUsePortal(false)) { + // CraftBukkit start - Entity in portal @@ -38,76 +38,77 @@ entity.setAsInsidePortal(this, pos); } } -@@ -126,23 +_,47 @@ +@@ -133,16 +_,39 @@ @Override - public @Nullable TeleportTransition getPortalDestination(ServerLevel level, Entity entity, BlockPos pos) { -- ResourceKey resourceKey = level.dimension() == Level.NETHER ? Level.OVERWORLD : Level.NETHER; -+ // CraftBukkit start -+ ResourceKey resourceKey = level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER ? Level.OVERWORLD : Level.NETHER; - ServerLevel level1 = level.getServer().getLevel(resourceKey); + public @Nullable TeleportTransition getPortalDestination(final ServerLevel currentLevel, final Entity entity, final BlockPos portalEntryPos) { +- ResourceKey newDimension = currentLevel.dimension() == Level.NETHER ? Level.OVERWORLD : Level.NETHER; ++ ResourceKey newDimension = currentLevel.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER ? Level.OVERWORLD : Level.NETHER; // CraftBukkit + ServerLevel newLevel = currentLevel.getServer().getLevel(newDimension); + // Paper start - Add EntityPortalReadyEvent -+ io.papermc.paper.event.entity.EntityPortalReadyEvent portalReadyEvent = new io.papermc.paper.event.entity.EntityPortalReadyEvent(entity.getBukkitEntity(), level1 == null ? null : level1.getWorld(), org.bukkit.PortalType.NETHER); ++ io.papermc.paper.event.entity.EntityPortalReadyEvent portalReadyEvent = new io.papermc.paper.event.entity.EntityPortalReadyEvent(entity.getBukkitEntity(), newLevel == null ? null : newLevel.getWorld(), org.bukkit.PortalType.NETHER); + if (!portalReadyEvent.callEvent()) { + entity.portalProcess = null; + return null; + } -+ level1 = portalReadyEvent.getTargetWorld() == null ? null : ((org.bukkit.craftbukkit.CraftWorld) portalReadyEvent.getTargetWorld()).getHandle(); ++ newLevel = portalReadyEvent.getTargetWorld() == null ? null : ((org.bukkit.craftbukkit.CraftWorld) portalReadyEvent.getTargetWorld()).getHandle(); + // Paper end - Add EntityPortalReadyEvent - if (level1 == null) { + if (newLevel == null) { return null; } else { -- boolean flag = level1.dimension() == Level.NETHER; -+ boolean flag = level1.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER; // CraftBukkit - WorldBorder worldBorder = level1.getWorldBorder(); - double teleportationScale = DimensionType.getTeleportationScale(level.dimensionType(), level1.dimensionType()); - BlockPos blockPos = worldBorder.clampToBounds(entity.getX() * teleportationScale, entity.getY(), entity.getZ() * teleportationScale); -- return this.getExitPortal(level1, entity, pos, blockPos, flag, worldBorder); +- boolean toNether = newLevel.dimension() == Level.NETHER; ++ boolean toNether = newLevel.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER; // CraftBukkit + WorldBorder newWorldBorder = newLevel.getWorldBorder(); + double teleportationScale = DimensionType.getTeleportationScale(currentLevel.dimensionType(), newLevel.dimensionType()); + BlockPos approximateExitPos = newWorldBorder.clampToBounds(entity.getX() * teleportationScale, entity.getY(), entity.getZ() * teleportationScale); +- return this.getExitPortal(newLevel, entity, portalEntryPos, approximateExitPos, toNether, newWorldBorder); + // Paper start - Configurable portal search radius -+ int portalSearchRadius = level1.paperConfig().environment.portalSearchRadius; -+ if (entity.level().paperConfig().environment.portalSearchVanillaDimensionScaling && flag) { // flag = is going to nether -+ portalSearchRadius = (int) (portalSearchRadius / level1.dimensionType().coordinateScale()); ++ int portalSearchRadius = newLevel.paperConfig().environment.portalSearchRadius; ++ if (entity.level().paperConfig().environment.portalSearchVanillaDimensionScaling && toNether) { ++ portalSearchRadius = (int) (portalSearchRadius / newLevel.dimensionType().coordinateScale()); + } + // Paper end - Configurable portal search radius + // CraftBukkit start -+ org.bukkit.craftbukkit.event.PortalEventResult result = org.bukkit.craftbukkit.event.CraftEventFactory.handlePortalEvents(entity, org.bukkit.craftbukkit.util.CraftLocation.toBukkit(blockPos, level1), org.bukkit.PortalType.NETHER, portalSearchRadius, level1.paperConfig().environment.portalCreateRadius); // Paper - use custom portal search radius ++ org.bukkit.craftbukkit.event.PortalEventResult result = org.bukkit.craftbukkit.event.CraftEventFactory.handlePortalEvents(entity, org.bukkit.craftbukkit.util.CraftLocation.toBukkit(approximateExitPos, newLevel), org.bukkit.PortalType.NETHER, portalSearchRadius, newLevel.paperConfig().environment.portalCreateRadius); // Paper - use custom portal search radius + if (result == null) { + return null; + } -+ level1 = ((org.bukkit.craftbukkit.CraftWorld) result.to().getWorld()).getHandle(); -+ worldBorder = level1.getWorldBorder(); -+ blockPos = worldBorder.clampToBounds(result.to().getX(), result.to().getY(), result.to().getZ()); -+ return this.getExitPortal(level1, entity, pos, blockPos, worldBorder, result); ++ newLevel = ((org.bukkit.craftbukkit.CraftWorld) result.to().getWorld()).getHandle(); ++ newWorldBorder = newLevel.getWorldBorder(); ++ approximateExitPos = newWorldBorder.clampToBounds(result.to().getX(), result.to().getY(), result.to().getZ()); ++ return this.getExitPortal(newLevel, entity, portalEntryPos, approximateExitPos, toNether, newWorldBorder, result); + // CraftBukkit end } } - private @Nullable TeleportTransition getExitPortal( -- ServerLevel level, Entity entity, BlockPos pos, BlockPos exitPos, boolean isNether, WorldBorder worldBorder -+ ServerLevel level, Entity entity, BlockPos pos, BlockPos exitPos, WorldBorder worldBorder, org.bukkit.craftbukkit.event.PortalEventResult result // CraftBukkit +@@ -153,8 +_,9 @@ + final BlockPos approximateExitPos, + final boolean toNether, + final WorldBorder worldBorder ++ , org.bukkit.craftbukkit.event.PortalEventResult result // CraftBukkit ) { -- Optional optional = level.getPortalForcer().findClosestPortalPosition(exitPos, isNether, worldBorder); -+ Optional optional = level.getPortalForcer().findClosestPortalPosition(exitPos, worldBorder, result.searchRadius()); // CraftBukkit - BlockUtil.FoundRectangle largestRectangleAround; - TeleportTransition.PostTeleportTransition postTeleportTransition; - if (optional.isPresent()) { -@@ -157,17 +_,22 @@ - blockPos1 -> level.getBlockState(blockPos1) == blockState +- Optional exitPortalPos = newLevel.getPortalForcer().findClosestPortalPosition(approximateExitPos, toNether, worldBorder); ++ Optional exitPortalPos = newLevel.getPortalForcer().findClosestPortalPosition(approximateExitPos, worldBorder, result.searchRadius()); // CraftBukkit + BlockUtil.FoundRectangle exitPortal; + TeleportTransition.PostTeleportTransition post; + if (exitPortalPos.isPresent()) { +@@ -169,17 +_,22 @@ + blockPos -> newLevel.getBlockState(blockPos) == portalState ); - postTeleportTransition = TeleportTransition.PLAY_PORTAL_SOUND.then(entity1 -> entity1.placePortalTicket(blockPos)); + post = TeleportTransition.PLAY_PORTAL_SOUND.then(e -> e.placePortalTicket(pos)); - } else { + } else if (result.canCreatePortal()) { // CraftBukkit - Direction.Axis axis = entity.level().getBlockState(pos).getOptionalValue(AXIS).orElse(Direction.Axis.X); -- Optional optional1 = level.getPortalForcer().createPortal(exitPos, axis); -+ Optional optional1 = level.getPortalForcer().createPortal(exitPos, axis, entity, result.createRadius()); // CraftBukkit - if (optional1.isEmpty()) { + Direction.Axis sourcePortalAxis = entity.level().getBlockState(portalEntryPos).getOptionalValue(AXIS).orElse(Direction.Axis.X); +- Optional createdExit = newLevel.getPortalForcer().createPortal(approximateExitPos, sourcePortalAxis); ++ Optional createdExit = newLevel.getPortalForcer().createPortal(approximateExitPos, sourcePortalAxis, entity, result.createRadius()); // CraftBukkit + if (createdExit.isEmpty()) { - LOGGER.error("Unable to create a portal, likely target out of worldborder"); + // LOGGER.error("Unable to create a portal, likely target out of worldborder"); // CraftBukkit return null; } - largestRectangleAround = optional1.get(); - postTeleportTransition = TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET); + exitPortal = createdExit.get(); + post = TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET); } + // CraftBukkit start + else { @@ -115,14 +116,14 @@ + } + // CraftBukkit end - return getDimensionTransitionFromExit(entity, pos, largestRectangleAround, level, postTeleportTransition); + return getDimensionTransitionFromExit(entity, portalEntryPos, exitPortal, newLevel, post); } -@@ -213,7 +_,7 @@ - boolean flag = axis1 == Direction.Axis.X; - Vec3 vec3 = new Vec3(blockPos.getX() + (flag ? d2 : d4), blockPos.getY() + d3, blockPos.getZ() + (flag ? d4 : d2)); - Vec3 vec31 = PortalShape.findCollisionFreePosition(vec3, level, entity, dimensions); -- return new TeleportTransition(level, vec31, Vec3.ZERO, i, 0.0F, Relative.union(Relative.DELTA, Relative.ROTATION), postTeleportTransition); -+ return new TeleportTransition(level, vec31, Vec3.ZERO, i, 0.0F, Relative.union(Relative.DELTA, Relative.ROTATION), postTeleportTransition, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.NETHER_PORTAL); // CraftBukkit +@@ -234,7 +_,7 @@ + ); + Vec3 collisionFreePos = PortalShape.findCollisionFreePosition(targetPos, newLevel, entity, dimensions); + return new TeleportTransition( +- newLevel, collisionFreePos, Vec3.ZERO, outputRotation, 0.0F, Relative.union(Relative.DELTA, Relative.ROTATION), postTeleportTransition ++ newLevel, collisionFreePos, Vec3.ZERO, outputRotation, 0.0F, Relative.union(Relative.DELTA, Relative.ROTATION), postTeleportTransition, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.NETHER_PORTAL // CraftBukkit + ); } - @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/NetherWartBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/NetherWartBlock.java.patch index a36c5dc3bd90..10a6456a1e6d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/NetherWartBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/NetherWartBlock.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/level/block/NetherWartBlock.java +++ b/net/minecraft/world/level/block/NetherWartBlock.java -@@ -50,9 +_,9 @@ +@@ -51,9 +_,9 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { - int ageValue = state.getValue(AGE); -- if (ageValue < 3 && random.nextInt(10) == 0) { -+ if (ageValue < 3 && random.nextFloat() < (level.spigotConfig.wartModifier / (100.0F * 10))) { // Spigot - SPIGOT-7159: Better modifier resolution - state = state.setValue(AGE, ageValue + 1); + protected void randomTick(BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { + int age = state.getValue(AGE); +- if (age < 3 && random.nextInt(10) == 0) { ++ if (age < 3 && random.nextFloat() < (level.spigotConfig.wartModifier / (100.0F * 10))) { // Spigot - SPIGOT-7159: Better modifier resolution + state = state.setValue(AGE, age + 1); - level.setBlock(pos, state, Block.UPDATE_CLIENTS); + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state, Block.UPDATE_CLIENTS); // CraftBukkit } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch index db0d3e70e01e..d2f16d4877f8 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch @@ -3,62 +3,65 @@ @@ -64,6 +_,7 @@ @Override - public BlockState getStateForPlacement(BlockPlaceContext context) { + public BlockState getStateForPlacement(final BlockPlaceContext context) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableNoteblockUpdates) return this.defaultBlockState(); // Paper - place without considering instrument return this.setInstrument(context.getLevel(), context.getClickedPos(), this.defaultBlockState()); } @@ -78,6 +_,7 @@ - BlockState neighborState, - RandomSource random + final BlockState neighbourState, + final RandomSource random ) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableNoteblockUpdates) return state; // Paper - prevent noteblock instrument from updating - boolean flag = direction.getAxis() == Direction.Axis.Y; - return flag + boolean neighborDirectionSetsInstrument = directionToNeighbour.getAxis() == Direction.Axis.Y; + return neighborDirectionSetsInstrument ? this.setInstrument(level, pos, state) -@@ -86,10 +_,12 @@ +@@ -86,12 +_,14 @@ @Override - protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { + protected void neighborChanged( +- final BlockState state, final Level level, final BlockPos pos, final Block block, final @Nullable Orientation orientation, final boolean movedByPiston ++ BlockState state, final Level level, final BlockPos pos, final Block block, final @Nullable Orientation orientation, final boolean movedByPiston // Paper - make state non-final + ) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableNoteblockUpdates) return; // Paper - prevent noteblock powered-state from updating - boolean hasNeighborSignal = level.hasNeighborSignal(pos); - if (hasNeighborSignal != state.getValue(POWERED)) { - if (hasNeighborSignal) { + boolean signal = level.hasNeighborSignal(pos); + if (signal != state.getValue(POWERED)) { + if (signal) { this.playNote(null, state, level, pos); + state = level.getBlockState(pos); // CraftBukkit - SPIGOT-5617: update in case changed in event } - level.setBlock(pos, state.setValue(POWERED, hasNeighborSignal), Block.UPDATE_ALL); -@@ -115,7 +_,7 @@ + level.setBlock(pos, state.setValue(POWERED, signal), Block.UPDATE_ALL); +@@ -123,7 +_,7 @@ @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + protected InteractionResult useWithoutItem(BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult) { if (!level.isClientSide()) { - state = state.cycle(NOTE); + if (!io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableNoteblockUpdates) state = state.cycle(NoteBlock.NOTE); // Paper - prevent noteblock note from updating level.setBlock(pos, state, Block.UPDATE_ALL); this.playNote(player, state, level, pos); player.awardStat(Stats.TUNE_NOTEBLOCK); -@@ -139,9 +_,13 @@ +@@ -147,9 +_,13 @@ @Override - protected boolean triggerEvent(BlockState state, Level level, BlockPos pos, int id, int param) { - NoteBlockInstrument noteBlockInstrument = state.getValue(INSTRUMENT); + protected boolean triggerEvent(final BlockState state, final Level level, final BlockPos pos, final int b0, final int b1) { + NoteBlockInstrument instrument = state.getValue(INSTRUMENT); + // Paper start - move NotePlayEvent call to fix instrument/note changes -+ org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(level, pos, noteBlockInstrument, state.getValue(NOTE)); ++ org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(level, pos, instrument, state.getValue(NOTE)); + if (event.isCancelled()) return false; + // Paper end - move NotePlayEvent call to fix instrument/note changes - float pitchFromNote; - if (noteBlockInstrument.isTunable()) { -- int noteValue = state.getValue(NOTE); -+ int noteValue = event.getNote().getId(); // Paper - move NotePlayEvent call to fix instrument/note changes - pitchFromNote = getPitchFromNote(noteValue); - level.addParticle(ParticleTypes.NOTE, pos.getX() + 0.5, pos.getY() + 1.2, pos.getZ() + 0.5, noteValue / 24.0, 0.0, 0.0); + float pitch; + if (instrument.isTunable()) { +- int note = state.getValue(NOTE); ++ int note = event.getNote().getId(); // Paper - move NotePlayEvent call to fix instrument/note changes + pitch = getPitchFromNote(note); + level.addParticle(ParticleTypes.NOTE, pos.getX() + 0.5, pos.getY() + 1.2, pos.getZ() + 0.5, note / 24.0, 0.0, 0.0); } else { -@@ -157,7 +_,7 @@ +@@ -165,7 +_,7 @@ - holder = Holder.direct(SoundEvent.createVariableRangeEvent(customSoundId)); + soundEvent = Holder.direct(SoundEvent.createVariableRangeEvent(soundId)); } else { -- holder = noteBlockInstrument.getSoundEvent(); -+ holder = org.bukkit.craftbukkit.block.data.CraftBlockData.toNMS(event.getInstrument(), NoteBlockInstrument.class).getSoundEvent(); // Paper - move NotePlayEvent call to fix instrument/note changes +- soundEvent = instrument.getSoundEvent(); ++ soundEvent = org.bukkit.craftbukkit.block.data.CraftBlockData.toVanilla(event.getInstrument(), NoteBlockInstrument.class).getSoundEvent(); // Paper - move NotePlayEvent call to fix instrument/note changes } level.playSeededSound( diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/NyliumBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/NyliumBlock.java.patch index c447936cca0c..bac62e06fc89 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/NyliumBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/NyliumBlock.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/level/block/NyliumBlock.java @@ -39,6 +_,11 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (!canBeNylium(state, level, pos)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, Blocks.NETHERRACK.defaultBlockState()).isCancelled()) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ObserverBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ObserverBlock.java.patch index 88732b7a4a91..daeb79710fc7 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ObserverBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ObserverBlock.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/level/block/ObserverBlock.java +++ b/net/minecraft/world/level/block/ObserverBlock.java -@@ -50,8 +_,18 @@ +@@ -51,8 +_,18 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (state.getValue(POWERED)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, 15, 0).getNewCurrent() != 0) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/PitcherCropBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/PitcherCropBlock.java.patch index f692f02e707a..e1f5016ae192 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/PitcherCropBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/PitcherCropBlock.java.patch @@ -1,28 +1,28 @@ --- a/net/minecraft/world/level/block/PitcherCropBlock.java +++ b/net/minecraft/world/level/block/PitcherCropBlock.java -@@ -117,6 +_,7 @@ - - @Override - public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -125,6 +_,7 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (level instanceof ServerLevel serverLevel && entity instanceof Ravager && serverLevel.getGameRules().get(GameRules.MOB_GRIEFING)) { serverLevel.destroyBlock(pos, true, entity); } -@@ -139,7 +_,7 @@ +@@ -147,7 +_,7 @@ @Override - public void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + public void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { float growthSpeed = CropBlock.getGrowthSpeed(this, level, pos); -- boolean flag = random.nextInt((int)(25.0F / growthSpeed) + 1) == 0; -+ boolean flag = random.nextFloat() < (level.spigotConfig.pitcherPlantModifier / (100.0F * (Math.floor(25.0F / growthSpeed) + 1))); // Paper - Fix Spigot growth modifiers - if (flag) { +- boolean shouldProgressGrowth = random.nextInt((int)(25.0F / growthSpeed) + 1) == 0; ++ boolean shouldProgressGrowth = random.nextFloat() < (level.spigotConfig.pitcherPlantModifier / (100.0F * (Math.floor(25.0F / growthSpeed) + 1))); // Paper - Fix Spigot growth modifiers + if (shouldProgressGrowth) { this.grow(level, state, pos, 1); } -@@ -149,7 +_,7 @@ - int min = Math.min(state.getValue(AGE) + ageIncrement, 4); - if (this.canGrow(level, pos, state, min)) { - BlockState blockState = state.setValue(AGE, min); -- level.setBlock(pos, blockState, Block.UPDATE_CLIENTS); -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, blockState, Block.UPDATE_CLIENTS)) return; // Paper - if (isDouble(min)) { - level.setBlock(pos.above(), blockState.setValue(HALF, DoubleBlockHalf.UPPER), Block.UPDATE_ALL); +@@ -157,7 +_,7 @@ + int updatedAge = Math.min(lowerState.getValue(AGE) + increase, 4); + if (this.canGrow(level, lowerPos, lowerState, updatedAge)) { + BlockState newLowerState = lowerState.setValue(AGE, updatedAge); +- level.setBlock(lowerPos, newLowerState, Block.UPDATE_CLIENTS); ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, lowerPos, newLowerState, Block.UPDATE_CLIENTS)) return; // Paper + if (isDouble(updatedAge)) { + level.setBlock(lowerPos.above(), newLowerState.setValue(HALF, DoubleBlockHalf.UPPER), Block.UPDATE_ALL); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/PointedDripstoneBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/PointedDripstoneBlock.java.patch index e338e196c1fb..84d75864eb4d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/PointedDripstoneBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/PointedDripstoneBlock.java.patch @@ -14,7 +14,7 @@ } @@ -152,7 +_,7 @@ @Override - public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, double fallDistance) { + public void fallOn(final Level level, final BlockState state, final BlockPos pos, final Entity entity, final double fallDistance) { if (state.getValue(TIP_DIRECTION) == Direction.UP && state.getValue(THICKNESS) == DripstoneThickness.TIP) { - entity.causeFallDamage(fallDistance + 2.5, 2.0F, level.damageSources().stalagmite()); + entity.causeFallDamage(fallDistance + 2.5, 2.0F, level.damageSources().stalagmite().eventBlockDamager(level, pos)); // CraftBukkit @@ -22,47 +22,47 @@ super.fallOn(level, state, pos, entity, fallDistance); } @@ -210,10 +_,11 @@ - if (blockPos != null) { - if (fluidAboveStalactite.get().sourceState.is(Blocks.MUD) && fluid == Fluids.WATER) { - BlockState blockState = Blocks.CLAY.defaultBlockState(); -- level.setBlockAndUpdate(fluidAboveStalactite.get().pos, blockState); -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, fluidAboveStalactite.get().pos, blockState, Block.UPDATE_ALL)) { // Paper - Call BlockFormEvent - Block.pushEntitiesUp(fluidAboveStalactite.get().sourceState, blockState, level, fluidAboveStalactite.get().pos); - level.gameEvent(GameEvent.BLOCK_CHANGE, fluidAboveStalactite.get().pos, GameEvent.Context.of(blockState)); - level.levelEvent(LevelEvent.DRIPSTONE_DRIP, blockPos, 0); + if (stalactiteTipPos != null) { + if (fluidInfo.get().sourceState.is(Blocks.MUD) && fluid == Fluids.WATER) { + BlockState newState = Blocks.CLAY.defaultBlockState(); +- level.setBlockAndUpdate(fluidInfo.get().pos, newState); ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, fluidInfo.get().pos, newState, Block.UPDATE_ALL)) { // Paper - Call BlockFormEvent + Block.pushEntitiesUp(fluidInfo.get().sourceState, newState, level, fluidInfo.get().pos); + level.gameEvent(GameEvent.BLOCK_CHANGE, fluidInfo.get().pos, GameEvent.Context.of(newState)); + level.levelEvent(LevelEvent.DRIPSTONE_DRIP, stalactiteTipPos, 0); + } // Paper - Call BlockFormEvent } else { - BlockPos blockPos1 = findFillableCauldronBelowStalactiteTip(level, blockPos, fluid); - if (blockPos1 != null) { -@@ -357,17 +_,17 @@ - if (isUnmergedTipWithDirection(blockState, direction.getOpposite())) { - createMergedTips(blockState, server, blockPos); - } else if (blockState.isAir() || blockState.is(Blocks.WATER)) { -- createDripstone(server, blockPos, direction, DripstoneThickness.TIP); -+ createDripstone(server, blockPos, direction, DripstoneThickness.TIP, pos); // CraftBukkit + BlockPos cauldronPos = findFillableCauldronBelowStalactiteTip(level, stalactiteTipPos, fluid); + if (cauldronPos != null) { +@@ -359,17 +_,17 @@ + if (isUnmergedTipWithDirection(existingStateAtTargetPos, growToDirection.getOpposite())) { + createMergedTips(existingStateAtTargetPos, level, targetPos); + } else if (existingStateAtTargetPos.isAir() || existingStateAtTargetPos.is(Blocks.WATER)) { +- createDripstone(level, targetPos, growToDirection, DripstoneThickness.TIP); ++ createDripstone(level, targetPos, growToDirection, DripstoneThickness.TIP, growFromPos); // CraftBukkit } } -- private static void createDripstone(LevelAccessor level, BlockPos pos, Direction direction, DripstoneThickness thickness) { -+ private static void createDripstone(LevelAccessor level, BlockPos pos, Direction direction, DripstoneThickness thickness, BlockPos source) { // CraftBukkit - BlockState blockState = Blocks.POINTED_DRIPSTONE +- private static void createDripstone(final LevelAccessor level, final BlockPos pos, final Direction direction, final DripstoneThickness thickness) { ++ private static void createDripstone(final LevelAccessor level, final BlockPos pos, final Direction direction, final DripstoneThickness thickness, final BlockPos source) { // CraftBukkit + BlockState state = Blocks.POINTED_DRIPSTONE .defaultBlockState() .setValue(TIP_DIRECTION, direction) .setValue(THICKNESS, thickness) - .setValue(WATERLOGGED, level.getFluidState(pos).getType() == Fluids.WATER); -- level.setBlock(pos, blockState, Block.UPDATE_ALL); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, pos, blockState, Block.UPDATE_ALL); // CraftBukkit + .setValue(WATERLOGGED, level.getFluidState(pos).is(Fluids.WATER)); +- level.setBlock(pos, state, Block.UPDATE_ALL); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, pos, state, Block.UPDATE_ALL); // CraftBukkit } - private static void createMergedTips(BlockState state, LevelAccessor level, BlockPos pos) { -@@ -381,8 +_,8 @@ - blockPos = pos.below(); + private static void createMergedTips(final BlockState tipState, final LevelAccessor level, final BlockPos tipPos) { +@@ -383,8 +_,8 @@ + stalagmitePos = tipPos.below(); } -- createDripstone(level, blockPos1, Direction.DOWN, DripstoneThickness.TIP_MERGE); -- createDripstone(level, blockPos, Direction.UP, DripstoneThickness.TIP_MERGE); -+ createDripstone(level, blockPos1, Direction.DOWN, DripstoneThickness.TIP_MERGE, pos); // CraftBukkit -+ createDripstone(level, blockPos, Direction.UP, DripstoneThickness.TIP_MERGE, pos); // CraftBukkit +- createDripstone(level, stalactitePos, Direction.DOWN, DripstoneThickness.TIP_MERGE); +- createDripstone(level, stalagmitePos, Direction.UP, DripstoneThickness.TIP_MERGE); ++ createDripstone(level, stalactitePos, Direction.DOWN, DripstoneThickness.TIP_MERGE, tipPos); // CraftBukkit ++ createDripstone(level, stalagmitePos, Direction.UP, DripstoneThickness.TIP_MERGE, tipPos); // CraftBukkit } - public static void spawnDripParticle(Level level, BlockPos pos, BlockState state) { + public static void spawnDripParticle(final Level level, final BlockPos stalactiteTipPos, final BlockState stalactiteTipState) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/PowderSnowBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/PowderSnowBlock.java.patch index fb6700ff7d0e..95637c475007 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/PowderSnowBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/PowderSnowBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/PowderSnowBlock.java +++ b/net/minecraft/world/level/block/PowderSnowBlock.java -@@ -60,6 +_,14 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -67,6 +_,14 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + // Paper start - Add EntityInsideBlockEvent + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { + if (entity instanceof net.minecraft.server.level.ServerPlayer player) { @@ -15,18 +15,18 @@ if (!(entity instanceof LivingEntity) || entity.getInBlockState().is(this)) { entity.makeStuckInBlock(state, new Vec3(0.9F, 1.5, 0.9F)); if (level.isClientSide()) { -@@ -85,8 +_,13 @@ - entity1 -> { +@@ -92,8 +_,13 @@ + e -> { if (level instanceof ServerLevel serverLevel - && entity1.isOnFire() -- && (serverLevel.getGameRules().get(GameRules.MOB_GRIEFING) || entity1 instanceof Player) + && e.isOnFire() +- && (serverLevel.getGameRules().get(GameRules.MOB_GRIEFING) || e instanceof Player) + // CraftBukkit - move down - && entity1.mayInteract(serverLevel, blockPos)) { + && e.mayInteract(serverLevel, position)) { + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity1, pos, Blocks.AIR.defaultBlockState(), !(serverLevel.getGameRules().get(GameRules.MOB_GRIEFING) || entity1 instanceof Player))) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(e, pos, Blocks.AIR.defaultBlockState(), !(serverLevel.getGameRules().get(GameRules.MOB_GRIEFING) || e instanceof Player))) { + return; + } + // CraftBukkit end - level.destroyBlock(blockPos, false); + level.destroyBlock(position, false); } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/PoweredRailBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/PoweredRailBlock.java.patch index dcc5b44fbbe8..2dad015f8004 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/PoweredRailBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/PoweredRailBlock.java.patch @@ -3,14 +3,14 @@ @@ -127,6 +_,13 @@ || this.findPoweredRailSignal(level, pos, state, true, 0) || this.findPoweredRailSignal(level, pos, state, false, 0); - if (flag != poweredValue) { + if (shouldPower != isPowered) { + // CraftBukkit start -+ int power = flag ? 15 : 0; ++ int power = shouldPower ? 15 : 0; + int newPower = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, power, 15 - power).getNewCurrent(); + if (newPower == power) { + return; + } + // CraftBukkit end - level.setBlock(pos, state.setValue(POWERED, flag), Block.UPDATE_ALL); + level.setBlock(pos, state.setValue(POWERED, shouldPower), Block.UPDATE_ALL); level.updateNeighborsAt(pos.below(), this); if (state.getValue(SHAPE).isSlope()) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/PressurePlateBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/PressurePlateBlock.java.patch index d813220d2047..8085e80d8835 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/PressurePlateBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/PressurePlateBlock.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/level/block/PressurePlateBlock.java +++ b/net/minecraft/world/level/block/PressurePlateBlock.java -@@ -46,7 +_,29 @@ +@@ -45,7 +_,29 @@ case EVERYTHING -> Entity.class; case MOBS -> LivingEntity.class; }; -- return getEntityCount(level, TOUCH_AABB.move(pos), clazz) > 0 ? 15 : 0; +- return getEntityCount(level, TOUCH_AABB.move(pos), entityClass) > 0 ? 15 : 0; + // CraftBukkit start - Call interact event when turning on a pressure plate -+ for (Entity entity : getEntities(level, PressurePlateBlock.TOUCH_AABB.move(pos), clazz)) { ++ for (Entity entity : getEntities(level, PressurePlateBlock.TOUCH_AABB.move(pos), entityClass)) { + if (this.getSignalForState(level.getBlockState(pos)) == 0) { + org.bukkit.event.Cancellable cancellable; + diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/PumpkinBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/PumpkinBlock.java.patch index 3b1123b07c12..e92d5d86e58a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/PumpkinBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/PumpkinBlock.java.patch @@ -1,44 +1,46 @@ --- a/net/minecraft/world/level/block/PumpkinBlock.java +++ b/net/minecraft/world/level/block/PumpkinBlock.java -@@ -41,6 +_,7 @@ +@@ -48,6 +_,7 @@ } else if (level instanceof ServerLevel serverLevel) { - Direction direction = hitResult.getDirection(); - Direction direction1 = direction.getAxis() == Direction.Axis.Y ? player.getDirection().getOpposite() : direction; + Direction clickedDirection = hitResult.getDirection(); + Direction direction = clickedDirection.getAxis() == Direction.Axis.Y ? player.getDirection().getOpposite() : clickedDirection; + java.util.List drops = new java.util.ArrayList<>(); // Paper dropFromBlockInteractLootTable( serverLevel, BuiltInLootTables.CARVE_PUMPKIN, -@@ -49,15 +_,26 @@ - stack, +@@ -56,16 +_,27 @@ + itemStack, player, - (serverLevel1, itemStack) -> { -- ItemEntity itemEntity = new ItemEntity( -- level, pos.getX() + 0.5 + direction1.getStepX() * 0.65, pos.getY() + 0.1, pos.getZ() + 0.5 + direction1.getStepZ() * 0.65, itemStack + (ignored, pumpkinSeeds) -> { +- ItemEntity entity = new ItemEntity( +- level, pos.getX() + 0.5 + direction.getStepX() * 0.65, pos.getY() + 0.1, pos.getZ() + 0.5 + direction.getStepZ() * 0.65, pumpkinSeeds - ); -- itemEntity.setDeltaMovement( -- 0.05 * direction1.getStepX() + level.random.nextDouble() * 0.02, 0.05, 0.05 * direction1.getStepZ() + level.random.nextDouble() * 0.02 +- RandomSource random = level.getRandom(); +- entity.setDeltaMovement( +- 0.05 * direction.getStepX() + random.nextDouble() * 0.02, 0.05, 0.05 * direction.getStepZ() + random.nextDouble() * 0.02 - ); -- level.addFreshEntity(itemEntity); -+ drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack)); // Paper - move spawn logic after event call +- level.addFreshEntity(entity); ++ drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(pumpkinSeeds)); // Paper - move spawn logic after event call } ); + // Paper start - Add PlayerShearBlockEvent + io.papermc.paper.event.block.PlayerShearBlockEvent event = new io.papermc.paper.event.block.PlayerShearBlockEvent( -+ (org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), drops); ++ (org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), drops); + if (!event.callEvent()) { + return InteractionResult.PASS; + } -+ for (org.bukkit.inventory.ItemStack itemStack : event.getDrops()) { ++ for (org.bukkit.inventory.ItemStack stack : event.getDrops()) { + // moved from above -+ ItemEntity itemEntity = new ItemEntity( -+ level, pos.getX() + 0.5 + direction1.getStepX() * 0.65, pos.getY() + 0.1, pos.getZ() + 0.5 + direction1.getStepZ() * 0.65, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(itemStack) ++ ItemEntity entity = new ItemEntity( ++ level, pos.getX() + 0.5 + direction.getStepX() * 0.65, pos.getY() + 0.1, pos.getZ() + 0.5 + direction.getStepZ() * 0.65, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(stack) + ); -+ itemEntity.setDeltaMovement( -+ 0.05 * direction1.getStepX() + level.random.nextDouble() * 0.02, 0.05, 0.05 * direction1.getStepZ() + level.random.nextDouble() * 0.02 ++ RandomSource random = level.getRandom(); ++ entity.setDeltaMovement( ++ 0.05 * direction.getStepX() + random.nextDouble() * 0.02, 0.05, 0.05 * direction.getStepZ() + random.nextDouble() * 0.02 + ); -+ level.addFreshEntity(itemEntity); ++ level.addFreshEntity(entity); + } + // Paper end - Add PlayerShearBlockEvent level.playSound(null, pos, SoundEvents.PUMPKIN_CARVE, SoundSource.BLOCKS, 1.0F, 1.0F); - level.setBlock(pos, Blocks.CARVED_PUMPKIN.defaultBlockState().setValue(CarvedPumpkinBlock.FACING, direction1), Block.UPDATE_ALL_IMMEDIATE); - stack.hurtAndBreak(1, player, hand.asEquipmentSlot()); + level.setBlock(pos, Blocks.CARVED_PUMPKIN.defaultBlockState().setValue(CarvedPumpkinBlock.FACING, direction), Block.UPDATE_ALL_IMMEDIATE); + itemStack.hurtAndBreak(1, player, hand.asEquipmentSlot()); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/RailState.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/RailState.java.patch index ab0b98fd47f4..c5e80cae49a3 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/RailState.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/RailState.java.patch @@ -10,24 +10,24 @@ + } + // Paper end - Fix some rails connecting improperly + - public RailState(Level level, BlockPos pos, BlockState state) { + public RailState(final Level level, final BlockPos pos, final BlockState state) { this.level = level; this.pos = pos; @@ -140,6 +_,11 @@ } - private void connectTo(RailState state) { + private void connectTo(final RailState rail) { + // Paper start - Fix some rails connecting improperly -+ if (!this.isValid() || !state.isValid()) { ++ if (!this.isValid() || !rail.isValid()) { + return; + } + // Paper end - Fix some rails connecting improperly - this.connections.add(state.pos); - BlockPos blockPos = this.pos.north(); - BlockPos blockPos1 = this.pos.south(); + this.connections.add(rail.pos); + BlockPos north = this.pos.north(); + BlockPos south = this.pos.south(); @@ -330,10 +_,15 @@ - this.state = this.state.setValue(this.block.getShapeProperty(), railShape); - if (alwaysPlace || this.level.getBlockState(this.pos) != this.state) { + this.state = this.state.setValue(this.block.getShapeProperty(), shape); + if (first || this.level.getBlockState(this.pos) != this.state) { this.level.setBlock(this.pos, this.state, Block.UPDATE_ALL); + // Paper start - Fix some rails connecting improperly + if (!this.isValid()) { @@ -36,12 +36,12 @@ + // Paper end - Fix some rails connecting improperly for (int i = 0; i < this.connections.size(); i++) { - RailState rail = this.getRail(this.connections.get(i)); -- if (rail != null) { -+ if (rail != null && rail.isValid()) { // Paper - Fix some rails connecting improperly - rail.removeSoftConnections(); - if (rail.canConnectTo(this)) { - rail.connectTo(this); + RailState neighbor = this.getRail(this.connections.get(i)); +- if (neighbor != null) { ++ if (neighbor != null && neighbor.isValid()) { // Paper - Fix some rails connecting improperly + neighbor.removeSoftConnections(); + if (neighbor.canConnectTo(this)) { + neighbor.connectTo(this); @@ -346,6 +_,6 @@ } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/RedStoneOreBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/RedStoneOreBlock.java.patch index 43b482cd10e6..674f031b2fca 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/RedStoneOreBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/RedStoneOreBlock.java.patch @@ -1,50 +1,49 @@ --- a/net/minecraft/world/level/block/RedStoneOreBlock.java +++ b/net/minecraft/world/level/block/RedStoneOreBlock.java -@@ -37,14 +_,27 @@ +@@ -37,14 +_,26 @@ @Override - protected void attack(BlockState state, Level level, BlockPos pos, Player player) { + protected void attack(final BlockState state, final Level level, final BlockPos pos, final Player player) { - interact(state, level, pos); -+ interact(state, level, pos, player); // CraftBukkit - add entityhuman ++ interact(state, level, pos, player); // CraftBukkit - add player super.attack(state, level, pos, player); } @Override - public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) { + public void stepOn(final Level level, final BlockPos pos, final BlockState onState, final Entity entity) { if (!entity.isSteppingCarefully()) { -- interact(state, level, pos); +- interact(onState, level, pos); + // CraftBukkit start + if (entity instanceof Player player) { + org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(player, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null); + if (!event.isCancelled()) { -+ RedStoneOreBlock.interact(level.getBlockState(pos), level, pos, entity); // add entity ++ interact(level.getBlockState(pos), level, pos, entity); // add entity + } + } else { + org.bukkit.event.entity.EntityInteractEvent event = new org.bukkit.event.entity.EntityInteractEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)); -+ level.getCraftServer().getPluginManager().callEvent(event); -+ if (!event.isCancelled()) { -+ RedStoneOreBlock.interact(level.getBlockState(pos), level, pos, entity); // add entity ++ if (!event.callEvent()) { ++ interact(level.getBlockState(pos), level, pos, entity); // add entity + } + } + // CraftBukkit end } - super.stepOn(level, pos, state, entity); -@@ -57,7 +_,7 @@ + super.stepOn(level, pos, onState, entity); +@@ -63,7 +_,7 @@ if (level.isClientSide()) { spawnParticles(level, pos); } else { - interact(state, level, pos); -+ interact(state, level, pos, player); // CraftBukkit - add entityhuman ++ interact(state, level, pos, player); // CraftBukkit - add player } - return (InteractionResult)(stack.getItem() instanceof BlockItem && new BlockPlaceContext(player, hand, stack, hitResult).canPlace() -@@ -65,9 +_,14 @@ + return (InteractionResult)(itemStack.getItem() instanceof BlockItem && new BlockPlaceContext(player, hand, itemStack, hitResult).canPlace() +@@ -71,9 +_,14 @@ : InteractionResult.SUCCESS); } -- private static void interact(BlockState state, Level level, BlockPos pos) { -+ private static void interact(BlockState state, Level level, BlockPos pos, Entity entity) { // CraftBukkit - add Entity +- private static void interact(final BlockState state, final Level level, final BlockPos pos) { ++ private static void interact(final BlockState state, final Level level, final BlockPos pos, final Entity entity) { // CraftBukkit - add entity spawnParticles(level, pos); if (!state.getValue(LIT)) { + // CraftBukkit start @@ -55,9 +54,9 @@ level.setBlock(pos, state.setValue(LIT, true), Block.UPDATE_ALL); } } -@@ -80,6 +_,11 @@ +@@ -86,6 +_,11 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (state.getValue(LIT)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, state.setValue(RedStoneOreBlock.LIT, false)).isCancelled()) { @@ -67,18 +66,18 @@ level.setBlock(pos, state.setValue(LIT, false), Block.UPDATE_ALL); } } -@@ -87,9 +_,17 @@ +@@ -93,9 +_,17 @@ @Override - protected void spawnAfterBreak(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { - super.spawnAfterBreak(state, level, pos, stack, dropExperience); + protected void spawnAfterBreak(final BlockState state, final ServerLevel level, final BlockPos pos, final ItemStack tool, final boolean dropExperience) { + super.spawnAfterBreak(state, level, pos, tool, dropExperience); + // CraftBukkit start - Delegated to getExpDrop + } + + @Override -+ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { ++ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack tool, boolean dropExperience) { if (dropExperience) { -- this.tryDropExperience(level, pos, stack, UniformInt.of(1, 5)); -+ return this.tryDropExperience(level, pos, stack, UniformInt.of(1, 5)); +- this.tryDropExperience(level, pos, tool, UniformInt.of(1, 5)); ++ return this.tryDropExperience(level, pos, tool, UniformInt.of(1, 5)); } + + return 0; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/RedstoneLampBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/RedstoneLampBlock.java.patch index 652ca91bea10..e23c1484d01a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/RedstoneLampBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/RedstoneLampBlock.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/block/RedstoneLampBlock.java +++ b/net/minecraft/world/level/block/RedstoneLampBlock.java -@@ -40,6 +_,11 @@ - if (litValue) { +@@ -42,6 +_,11 @@ + if (isLit) { level.scheduleTick(pos, this, 4); } else { + // CraftBukkit start @@ -12,9 +12,9 @@ level.setBlock(pos, state.cycle(LIT), Block.UPDATE_CLIENTS); } } -@@ -49,6 +_,11 @@ +@@ -51,6 +_,11 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (state.getValue(LIT) && !level.hasNeighborSignal(pos)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, 15, 0).getNewCurrent() != 0) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/RedstoneTorchBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/RedstoneTorchBlock.java.patch index 86fdd6576331..2e37c507de1b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/RedstoneTorchBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/RedstoneTorchBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/RedstoneTorchBlock.java +++ b/net/minecraft/world/level/block/RedstoneTorchBlock.java -@@ -24,7 +_,7 @@ +@@ -25,7 +_,7 @@ public class RedstoneTorchBlock extends BaseTorchBlock { public static final MapCodec CODEC = simpleCodec(RedstoneTorchBlock::new); public static final BooleanProperty LIT = BlockStateProperties.LIT; @@ -9,38 +9,35 @@ public static final int RECENT_TOGGLE_TIMER = 60; public static final int MAX_RECENT_TOGGLES = 8; public static final int RESTART_DELAY = 160; -@@ -72,14 +_,34 @@ +@@ -73,14 +_,32 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { - boolean hasNeighborSignal = this.hasNeighborSignal(level, pos, state); -- List list = RECENT_TOGGLES.get(level); + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { + boolean neighborSignal = this.hasNeighborSignal(level, pos, state); +- List toggles = RECENT_TOGGLES.get(level); - -- while (list != null && !list.isEmpty() && level.getGameTime() - list.get(0).when > 60L) { -- list.remove(0); +- while (toggles != null && !toggles.isEmpty() && level.getGameTime() - toggles.get(0).when > 60L) { +- toggles.remove(0); + // Paper start - Faster redstone torch rapid clock removal -+ java.util.ArrayDeque redstoneUpdateInfos = level.redstoneUpdateInfos; -+ if (redstoneUpdateInfos != null) { ++ java.util.ArrayDeque toggles = level.redstoneUpdateInfos; ++ if (toggles != null) { + RedstoneTorchBlock.Toggle curr; -+ while ((curr = redstoneUpdateInfos.peek()) != null && level.getGameTime() - curr.when > 60L) { -+ redstoneUpdateInfos.poll(); ++ while ((curr = toggles.peek()) != null && level.getGameTime() - curr.when > 60L) { ++ toggles.poll(); + } } -- + // Paper end - Faster redstone torch rapid clock removal -+ + // CraftBukkit start -+ org.bukkit.plugin.PluginManager manager = level.getCraftServer().getPluginManager(); + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + int oldCurrent = state.getValue(LIT) ? 15 : 0; -+ + + org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent(block, oldCurrent, oldCurrent); + // CraftBukkit end if (state.getValue(LIT)) { - if (hasNeighborSignal) { + if (neighborSignal) { + // CraftBukkit start + if (oldCurrent != 0) { + event.setNewCurrent(0); -+ manager.callEvent(event); ++ event.callEvent(); + if (event.getNewCurrent() != 0) { + return; + } @@ -49,14 +46,14 @@ level.setBlock(pos, state.setValue(LIT, false), Block.UPDATE_ALL); if (isToggledTooFrequently(level, pos, true)) { level.levelEvent(LevelEvent.REDSTONE_TORCH_BURNOUT, pos, 0); -@@ -87,6 +_,15 @@ +@@ -88,6 +_,15 @@ } } - } else if (!hasNeighborSignal && !isToggledTooFrequently(level, pos, false)) { + } else if (!neighborSignal && !isToggledTooFrequently(level, pos, false)) { + // CraftBukkit start + if (oldCurrent != 15) { + event.setNewCurrent(15); -+ manager.callEvent(event); ++ event.callEvent(); + if (event.getNewCurrent() != 15) { + return; + } @@ -65,17 +62,17 @@ level.setBlock(pos, state.setValue(LIT, true), Block.UPDATE_ALL); } } -@@ -124,7 +_,12 @@ +@@ -127,7 +_,12 @@ } - private static boolean isToggledTooFrequently(Level level, BlockPos pos, boolean logToggle) { -- List list = RECENT_TOGGLES.computeIfAbsent(level, blockGetter -> Lists.newArrayList()); + private static boolean isToggledTooFrequently(final Level level, final BlockPos pos, final boolean add) { +- List toggles = RECENT_TOGGLES.computeIfAbsent(level, k -> Lists.newArrayList()); + // Paper start - Faster redstone torch rapid clock removal -+ java.util.ArrayDeque list = level.redstoneUpdateInfos; -+ if (list == null) { -+ list = level.redstoneUpdateInfos = new java.util.ArrayDeque<>(); ++ java.util.ArrayDeque toggles = level.redstoneUpdateInfos; ++ if (toggles == null) { ++ toggles = level.redstoneUpdateInfos = new java.util.ArrayDeque<>(); + } + // Paper end - Faster redstone torch rapid clock removal - if (logToggle) { - list.add(new RedstoneTorchBlock.Toggle(pos.immutable(), level.getGameTime())); + if (add) { + toggles.add(new RedstoneTorchBlock.Toggle(pos.immutable(), level.getGameTime())); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/RespawnAnchorBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/RespawnAnchorBlock.java.patch index 392898ca5c6f..a7de8dc2e270 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/RespawnAnchorBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/RespawnAnchorBlock.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/block/RespawnAnchorBlock.java +++ b/net/minecraft/world/level/block/RespawnAnchorBlock.java -@@ -106,11 +_,16 @@ +@@ -115,11 +_,16 @@ LevelData.RespawnData.of(serverLevel.dimension(), pos, 0.0F, 0.0F), false ); - if (respawnConfig == null || !respawnConfig.isSamePosition(respawnConfig1)) { -- serverPlayer.setRespawnPosition(respawnConfig1, true); -+ if (serverPlayer.setRespawnPosition(respawnConfig1, true, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.RESPAWN_ANCHOR)) { // Paper - Add PlayerSetSpawnEvent + if (respawnConfig == null || !respawnConfig.isSamePosition(newRespawnConfig)) { +- serverPlayer.setRespawnPosition(newRespawnConfig, true); ++ if (serverPlayer.setRespawnPosition(newRespawnConfig, true, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.RESPAWN_ANCHOR)) { // Paper - Add PlayerSetSpawnEvent serverLevel.playSound( null, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, SoundEvents.RESPAWN_ANCHOR_SET_SPAWN, SoundSource.BLOCKS, 1.0F, 1.0F ); @@ -18,20 +18,20 @@ } } -@@ -147,6 +_,7 @@ +@@ -156,6 +_,7 @@ } - private void explode(BlockState state, ServerLevel level, final BlockPos pos2) { -+ org.bukkit.block.BlockState blockState = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos2).getState(); // CraftBukkit - capture BlockState before remove block - level.removeBlock(pos2, false); - boolean flag = Direction.Plane.HORIZONTAL.stream().map(pos2::relative).anyMatch(blockPos -> isWaterThatWouldFlow(blockPos, level)); - final boolean flag1 = flag || level.getFluidState(pos2.above()).is(FluidTags.WATER); -@@ -160,7 +_,7 @@ + private void explode(final BlockState state, final ServerLevel level, final BlockPos pos) { ++ org.bukkit.block.BlockState blockState = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos).getState(); // CraftBukkit - capture BlockState before remove block + level.removeBlock(pos, false); + boolean anyWaterNeighbors = Direction.Plane.HORIZONTAL.stream().map(pos::relative).anyMatch(neighborPos -> isWaterThatWouldFlow(neighborPos, level)); + final boolean inWater = anyWaterNeighbors || level.getFluidState(pos.above()).is(FluidTags.WATER); +@@ -174,7 +_,7 @@ + } }; - Vec3 center = pos2.getCenter(); - level.explode( -- null, level.damageSources().badRespawnPointExplosion(center), explosionDamageCalculator, center, 5.0F, true, Level.ExplosionInteraction.BLOCK -+ null, level.damageSources().badRespawnPointExplosion(center).causingBlockSnapshot(blockState), explosionDamageCalculator, center, 5.0F, true, Level.ExplosionInteraction.BLOCK // CraftBukkit - add state - ); + Vec3 boomPos = pos.getCenter(); +- level.explode(null, level.damageSources().badRespawnPointExplosion(boomPos), damageCalculator, boomPos, 5.0F, true, Level.ExplosionInteraction.BLOCK); ++ level.explode(null, level.damageSources().badRespawnPointExplosion(boomPos).causingBlockSnapshot(blockState), damageCalculator, boomPos, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state } + public static boolean canSetSpawn(final ServerLevel level, final BlockPos pos) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/RootedDirtBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/RootedDirtBlock.java.patch index 508e289bb713..0be20be17351 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/RootedDirtBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/RootedDirtBlock.java.patch @@ -3,7 +3,7 @@ @@ -33,7 +_,7 @@ @Override - public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { + public void performBonemeal(final ServerLevel level, final RandomSource random, final BlockPos pos, final BlockState state) { - level.setBlockAndUpdate(pos.below(), Blocks.HANGING_ROOTS.defaultBlockState()); + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, pos.below(), Blocks.HANGING_ROOTS.defaultBlockState(), Block.UPDATE_ALL); // CraftBukkit } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SaplingBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SaplingBlock.java.patch index d6efefbb6639..0500e2141d60 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SaplingBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SaplingBlock.java.patch @@ -1,23 +1,23 @@ --- a/net/minecraft/world/level/block/SaplingBlock.java +++ b/net/minecraft/world/level/block/SaplingBlock.java -@@ -25,6 +_,7 @@ +@@ -24,6 +_,7 @@ public static final IntegerProperty STAGE = BlockStateProperties.STAGE; private static final VoxelShape SHAPE = Block.column(12.0, 0.0, 12.0); protected final TreeGrower treeGrower; -+ public static @javax.annotation.Nullable org.bukkit.TreeType treeType; // CraftBukkit ++ public static org.bukkit.@org.jspecify.annotations.Nullable TreeType treeType; // CraftBukkit @Override public MapCodec codec() { -@@ -44,7 +_,7 @@ +@@ -43,7 +_,7 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { - if (level.getMaxLocalRawBrightness(pos.above()) >= 9 && random.nextInt(7) == 0) { + if (level.getMaxLocalRawBrightness(pos.above()) >= 9 && random.nextFloat() < (level.spigotConfig.saplingModifier / (100.0F * 7))) { // Spigot - SPIGOT-7159: Better modifier resolution this.advanceTree(level, pos, state, random); } } -@@ -53,7 +_,34 @@ +@@ -52,7 +_,34 @@ if (state.getValue(STAGE) == 0) { level.setBlock(pos, state.cycle(STAGE), Block.UPDATE_NONE); } else { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ScaffoldingBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ScaffoldingBlock.java.patch index ab3df7b7e118..5a7081bee284 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ScaffoldingBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ScaffoldingBlock.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/block/ScaffoldingBlock.java +++ b/net/minecraft/world/level/block/ScaffoldingBlock.java @@ -117,7 +_,7 @@ - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { int distance = getDistance(level, pos); - BlockState blockState = state.setValue(DISTANCE, distance).setValue(BOTTOM, this.isBottom(level, pos, distance)); -- if (blockState.getValue(DISTANCE) == 7) { -+ if (blockState.getValue(DISTANCE) == 7 && !org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, blockState.getFluidState().createLegacyBlock()).isCancelled()) { // CraftBukkit - BlockFadeEvent // Paper - fix wrong block state + BlockState newState = state.setValue(DISTANCE, distance).setValue(BOTTOM, this.isBottom(level, pos, distance)); +- if (newState.getValue(DISTANCE) == 7) { ++ if (newState.getValue(DISTANCE) == 7 && !org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, newState.getFluidState().createLegacyBlock()).isCancelled()) { // CraftBukkit - BlockFadeEvent // Paper - fix wrong block state if (state.getValue(DISTANCE) == 7) { - FallingBlockEntity.fall(level, pos, blockState); + FallingBlockEntity.fall(level, pos, newState); } else { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SculkBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SculkBlock.java.patch index d66b40184d7d..866bae4bf2d9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SculkBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SculkBlock.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/world/level/block/SculkBlock.java +++ b/net/minecraft/world/level/block/SculkBlock.java -@@ -37,8 +_,9 @@ - if (random.nextInt(growthSpawnCost) < charge) { - BlockPos blockPos = pos1.above(); - BlockState randomGrowthState = this.getRandomGrowthState(level, blockPos, random, spreader.isWorldGeneration()); -- level.setBlock(blockPos, randomGrowthState, Block.UPDATE_ALL); -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, randomGrowthState, Block.UPDATE_ALL)) { // CraftBukkit - Call BlockSpreadEvent - level.playSound(null, pos1, randomGrowthState.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1.0F, 1.0F); +@@ -42,8 +_,9 @@ + if (random.nextInt(xpPerGrowthSpawn) < charge) { + BlockPos growthPlacement = chargePos.above(); + BlockState growthState = this.getRandomGrowthState(level, growthPlacement, random, spreader.isWorldGeneration()); +- level.setBlock(growthPlacement, growthState, Block.UPDATE_ALL); ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, originPos, growthPlacement, growthState, Block.UPDATE_ALL)) { // CraftBukkit - Call BlockSpreadEvent + level.playSound(null, chargePos, growthState.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1.0F, 1.0F); + } // CraftBukkit - Call BlockSpreadEvent } - return Math.max(0, charge - growthSpawnCost); + return Math.max(0, charge - xpPerGrowthSpawn); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SculkCatalystBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SculkCatalystBlock.java.patch index b3c120dc3f1d..3cfe30ec4e29 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SculkCatalystBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SculkCatalystBlock.java.patch @@ -2,20 +2,19 @@ +++ b/net/minecraft/world/level/block/SculkCatalystBlock.java @@ -59,8 +_,16 @@ @Override - protected void spawnAfterBreak(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { - super.spawnAfterBreak(state, level, pos, stack, dropExperience); + protected void spawnAfterBreak(final BlockState state, final ServerLevel level, final BlockPos pos, final ItemStack tool, final boolean dropExperience) { + super.spawnAfterBreak(state, level, pos, tool, dropExperience); + // CraftBukkit start - Delegate to getExpDrop + } + + @Override -+ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { ++ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack tool, boolean dropExperience) { if (dropExperience) { -- this.tryDropExperience(level, pos, stack, this.xpRange); -+ return this.tryDropExperience(level, pos, stack, this.xpRange); +- this.tryDropExperience(level, pos, tool, this.xpRange); ++ return this.tryDropExperience(level, pos, tool, this.xpRange); } -- } + + return 0; + // CraftBukkit end -+ } + } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SculkSensorBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SculkSensorBlock.java.patch index 92ddd79a9fd1..95847fa06e2c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SculkSensorBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SculkSensorBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/SculkSensorBlock.java +++ b/net/minecraft/world/level/block/SculkSensorBlock.java -@@ -101,6 +_,18 @@ - && level.getBlockEntity(pos) instanceof SculkSensorBlockEntity sculkSensorBlockEntity +@@ -102,6 +_,18 @@ + && level.getBlockEntity(pos) instanceof SculkSensorBlockEntity sculkSensor && level instanceof ServerLevel serverLevel - && sculkSensorBlockEntity.getVibrationUser().canReceiveVibration(serverLevel, pos, GameEvent.STEP, GameEvent.Context.of(state))) { + && sculkSensor.getVibrationUser().canReceiveVibration(serverLevel, pos, GameEvent.STEP, GameEvent.Context.of(onState))) { + // CraftBukkit start + org.bukkit.event.Cancellable cancellable; + if (entity instanceof net.minecraft.world.entity.player.Player player) { @@ -16,18 +16,18 @@ + return; + } + // CraftBukkit end - sculkSensorBlockEntity.getListener().forceScheduleVibration(serverLevel, GameEvent.STEP, GameEvent.Context.of(entity), entity.position()); + sculkSensor.getListener().forceScheduleVibration(serverLevel, GameEvent.STEP, GameEvent.Context.of(entity), entity.position()); } -@@ -188,10 +_,19 @@ +@@ -189,10 +_,19 @@ } - public static boolean canActivate(BlockState state) { + public static boolean canActivate(final BlockState state) { - return getPhase(state) == SculkSensorPhase.INACTIVE; -+ return state.getBlock() instanceof SculkSensorBlock && getPhase(state) == SculkSensorPhase.INACTIVE; // Paper - Check for a valid type ++ return state.getBlock() instanceof SculkSensorBlock && getPhase(state) == SculkSensorPhase.INACTIVE; // Paper - Check for a valid type } - public static void deactivate(Level level, BlockPos pos, BlockState state) { + public static void deactivate(final Level level, final BlockPos pos, final BlockState state) { + // CraftBukkit start + org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), state.getValue(SculkSensorBlock.POWER), 0); + level.getCraftServer().getPluginManager().callEvent(eventRedstone); @@ -40,34 +40,38 @@ level.setBlock(pos, state.setValue(PHASE, SculkSensorPhase.COOLDOWN).setValue(POWER, 0), Block.UPDATE_ALL); level.scheduleTick(pos, state.getBlock(), 10); updateNeighbours(level, pos, state); -@@ -203,6 +_,15 @@ - } - - public void activate(@Nullable Entity entity, Level level, BlockPos pos, BlockState state, int power, int frequency) { +@@ -208,9 +_,18 @@ + final Level level, + final BlockPos pos, + final BlockState state, +- final int calculatedPower, ++ int calculatedPower, // CraftBukkit + final int vibrationFrequency + ) { + // CraftBukkit start -+ org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), state.getValue(SculkSensorBlock.POWER), power); ++ org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), state.getValue(SculkSensorBlock.POWER), calculatedPower); + level.getCraftServer().getPluginManager().callEvent(eventRedstone); + + if (eventRedstone.getNewCurrent() <= 0) { + return; + } -+ power = eventRedstone.getNewCurrent(); ++ calculatedPower = eventRedstone.getNewCurrent(); + // CraftBukkit end - level.setBlock(pos, state.setValue(PHASE, SculkSensorPhase.ACTIVE).setValue(POWER, power), Block.UPDATE_ALL); + level.setBlock(pos, state.setValue(PHASE, SculkSensorPhase.ACTIVE).setValue(POWER, calculatedPower), Block.UPDATE_ALL); level.scheduleTick(pos, state.getBlock(), this.getActiveTicks()); updateNeighbours(level, pos, state); -@@ -280,8 +_,16 @@ +@@ -288,8 +_,16 @@ @Override - protected void spawnAfterBreak(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { - super.spawnAfterBreak(state, level, pos, stack, dropExperience); + protected void spawnAfterBreak(final BlockState state, final ServerLevel level, final BlockPos pos, final ItemStack tool, final boolean dropExperience) { + super.spawnAfterBreak(state, level, pos, tool, dropExperience); + // CraftBukkit start - Delegate to getExpDrop + } + + @Override -+ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { ++ public int getExpDrop(final BlockState state, final ServerLevel level, final BlockPos pos, final ItemStack tool, final boolean dropExperience) { if (dropExperience) { -- this.tryDropExperience(level, pos, stack, ConstantInt.of(5)); -+ return this.tryDropExperience(level, pos, stack, ConstantInt.of(5)); +- this.tryDropExperience(level, pos, tool, ConstantInt.of(5)); ++ return this.tryDropExperience(level, pos, tool, ConstantInt.of(5)); } + + return 0; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SculkShriekerBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SculkShriekerBlock.java.patch index 3bd01d8b8855..22527605c07a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SculkShriekerBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SculkShriekerBlock.java.patch @@ -2,24 +2,24 @@ +++ b/net/minecraft/world/level/block/SculkShriekerBlock.java @@ -60,6 +_,7 @@ if (level instanceof ServerLevel serverLevel) { - ServerPlayer serverPlayer = SculkShriekerBlockEntity.tryGetPlayer(entity); - if (serverPlayer != null) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(serverPlayer, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null).isCancelled()) return; // CraftBukkit - serverLevel.getBlockEntity(pos, BlockEntityType.SCULK_SHRIEKER).ifPresent(sculkShrieker -> sculkShrieker.tryShriek(serverLevel, serverPlayer)); + ServerPlayer player = SculkShriekerBlockEntity.tryGetPlayer(entity); + if (player != null) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(player, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null).isCancelled()) return; // CraftBukkit + serverLevel.getBlockEntity(pos, BlockEntityType.SCULK_SHRIEKER).ifPresent(shrieker -> shrieker.tryShriek(serverLevel, player)); } } @@ -126,9 +_,16 @@ @Override - protected void spawnAfterBreak(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { - super.spawnAfterBreak(state, level, pos, stack, dropExperience); + protected void spawnAfterBreak(final BlockState state, final ServerLevel level, final BlockPos pos, final ItemStack tool, final boolean dropExperience) { + super.spawnAfterBreak(state, level, pos, tool, dropExperience); + // CraftBukkit start - Delegate to getExpDrop + } + + @Override -+ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { ++ public int getExpDrop(final BlockState state, final ServerLevel level, final BlockPos pos, final ItemStack tool, final boolean dropExperience) { if (dropExperience) { -- this.tryDropExperience(level, pos, stack, ConstantInt.of(5)); -+ return this.tryDropExperience(level, pos, stack, ConstantInt.of(5)); +- this.tryDropExperience(level, pos, tool, ConstantInt.of(5)); ++ return this.tryDropExperience(level, pos, tool, ConstantInt.of(5)); } + return 0; + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SculkSpreader.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SculkSpreader.java.patch index 067dff375749..ac2f03634176 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SculkSpreader.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SculkSpreader.java.patch @@ -7,28 +7,28 @@ + public net.minecraft.world.level.Level level; // CraftBukkit public SculkSpreader( - boolean isWorldGeneration, TagKey replaceableBlocks, int growthSpawnCost, int noGrowthRadius, int chargeDecayRate, int additionalDecayRate -@@ -102,7 +_,7 @@ + final boolean isWorldGeneration, +@@ -107,7 +_,7 @@ - public void load(ValueInput input) { + public void load(final ValueInput input) { this.cursors.clear(); - input.read("cursors", SculkSpreader.ChargeCursor.CODEC.sizeLimitedListOf(32)).orElse(List.of()).forEach(this::addCursor); -+ input.read("cursors", SculkSpreader.ChargeCursor.CODEC.sizeLimitedListOf(32)).orElse(List.of()).forEach((cursor) -> this.addCursor(cursor, false)); // Paper - don't fire event for block entity loading ++ input.read("cursors", SculkSpreader.ChargeCursor.CODEC.sizeLimitedListOf(32)).orElse(List.of()).forEach((cursor) -> this.addCursor(cursor, false)); // Paper - don't fire event for block entity loading } - public void save(ValueOutput output) { -@@ -121,13 +_,24 @@ - public void addCursors(BlockPos pos, int charge) { + public void save(final ValueOutput output) { +@@ -126,13 +_,24 @@ + public void addCursors(final BlockPos startPos, int charge) { while (charge > 0) { - int min = Math.min(charge, 1000); -- this.addCursor(new SculkSpreader.ChargeCursor(pos, min)); -+ this.addCursor(new SculkSpreader.ChargeCursor(pos, min), true); // Paper - allow firing event for other causes - charge -= min; + int currentCharge = Math.min(charge, 1000); +- this.addCursor(new SculkSpreader.ChargeCursor(startPos, currentCharge)); ++ this.addCursor(new SculkSpreader.ChargeCursor(startPos, currentCharge), true); // Paper - allow firing event for other causes + charge -= currentCharge; } } -- private void addCursor(SculkSpreader.ChargeCursor cursor) { -+ private void addCursor(SculkSpreader.ChargeCursor cursor, boolean fireEvent) { // Paper - add boolean to conditionally fire SculkBloomEvent +- private void addCursor(final SculkSpreader.ChargeCursor cursor) { ++ private void addCursor(final SculkSpreader.ChargeCursor cursor, boolean fireEvent) { // Paper - add boolean to conditionally fire SculkBloomEvent if (this.cursors.size() < 32) { + // CraftBukkit start + if (!this.isWorldGeneration() && fireEvent) { // CraftBukkit - SPIGOT-7475: Don't call event during world generation // Paper - add boolean to conditionally fire SculkBloomEvent diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SculkVeinBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SculkVeinBlock.java.patch index 2450b1ae9ef2..47ecdbed28b0 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SculkVeinBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SculkVeinBlock.java.patch @@ -1,32 +1,32 @@ --- a/net/minecraft/world/level/block/SculkVeinBlock.java +++ b/net/minecraft/world/level/block/SculkVeinBlock.java -@@ -90,14 +_,14 @@ - public int attemptUseCharge( - SculkSpreader.ChargeCursor cursor, LevelAccessor level, BlockPos pos, RandomSource random, SculkSpreader spreader, boolean shouldConvertBlocks +@@ -96,14 +_,14 @@ + final SculkSpreader spreader, + final boolean spreadVeins ) { -- if (shouldConvertBlocks && this.attemptPlaceSculk(spreader, level, cursor.getPos(), random)) { -+ if (shouldConvertBlocks && this.attemptPlaceSculk(spreader, level, cursor.getPos(), random, pos)) { // CraftBukkit - add source block +- if (spreadVeins && this.attemptPlaceSculk(spreader, level, cursor.getPos(), random)) { ++ if (spreadVeins && this.attemptPlaceSculk(spreader, level, cursor.getPos(), random, originPos)) { // CraftBukkit - add source block return cursor.getCharge() - 1; } else { return random.nextInt(spreader.chargeDecayRate()) == 0 ? Mth.floor(cursor.getCharge() * 0.5F) : cursor.getCharge(); } } -- private boolean attemptPlaceSculk(SculkSpreader spreader, LevelAccessor level, BlockPos pos, RandomSource random) { -+ private boolean attemptPlaceSculk(SculkSpreader spreader, LevelAccessor level, BlockPos pos, RandomSource random, BlockPos sourceBlock) { // CraftBukkit - BlockState blockState = level.getBlockState(pos); - TagKey tagKey = spreader.replaceableBlocks(); +- private boolean attemptPlaceSculk(final SculkSpreader spreader, final LevelAccessor level, final BlockPos pos, final RandomSource random) { ++ private boolean attemptPlaceSculk(final SculkSpreader spreader, final LevelAccessor level, final BlockPos pos, final RandomSource random, final BlockPos originPos) { // CraftBukkit + BlockState state = level.getBlockState(pos); + TagKey replaceTag = spreader.replaceableBlocks(); -@@ -107,7 +_,11 @@ - BlockState blockState1 = level.getBlockState(blockPos); - if (blockState1.is(tagKey)) { - BlockState blockState2 = Blocks.SCULK.defaultBlockState(); -- level.setBlock(blockPos, blockState2, Block.UPDATE_ALL); +@@ -113,7 +_,11 @@ + BlockState supportState = level.getBlockState(supportPos); + if (supportState.is(replaceTag)) { + BlockState defaultSculk = Blocks.SCULK.defaultBlockState(); +- level.setBlock(supportPos, defaultSculk, Block.UPDATE_ALL); + // CraftBukkit start - Call BlockSpreadEvent -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, sourceBlock, blockPos, blockState2, Block.UPDATE_ALL)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, originPos, supportPos, defaultSculk, Block.UPDATE_ALL)) { + return false; + } + // CraftBukkit end - Block.pushEntitiesUp(blockState1, blockState2, level, blockPos); - level.playSound(null, blockPos, SoundEvents.SCULK_BLOCK_SPREAD, SoundSource.BLOCKS, 1.0F, 1.0F); - this.veinSpreader.spreadAll(blockState2, level, blockPos, spreader.isWorldGeneration()); + Block.pushEntitiesUp(supportState, defaultSculk, level, supportPos); + level.playSound(null, supportPos, SoundEvents.SCULK_BLOCK_SPREAD, SoundSource.BLOCKS, 1.0F, 1.0F); + this.veinSpreader.spreadAll(defaultSculk, level, supportPos, spreader.isWorldGeneration()); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ShelfBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ShelfBlock.java.patch index 67623f8446d0..96c8916f22c0 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ShelfBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ShelfBlock.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/level/block/ShelfBlock.java +++ b/net/minecraft/world/level/block/ShelfBlock.java -@@ -109,6 +_,11 @@ +@@ -111,6 +_,11 @@ if (!level.isClientSide()) { - boolean hasNeighborSignal = level.hasNeighborSignal(pos); - if (state.getValue(POWERED) != hasNeighborSignal) { + boolean signal = level.hasNeighborSignal(pos); + if (state.getValue(POWERED) != signal) { + // Paper start - Call BlockRedstoneEvent -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBinaryRedstoneChange(level, pos, hasNeighborSignal)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBinaryRedstoneChange(level, pos, signal)) { + return; + } + // Paper end - Call BlockRedstoneEvent - BlockState blockState = state.setValue(POWERED, hasNeighborSignal); - if (!hasNeighborSignal) { - blockState = blockState.setValue(SIDE_CHAIN_PART, SideChainPart.UNCONNECTED); + BlockState newState = state.setValue(POWERED, signal); + if (!signal) { + newState = newState.setValue(SIDE_CHAIN_PART, SideChainPart.UNCONNECTED); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ShulkerBoxBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ShulkerBoxBlock.java.patch index 2173429fc874..7e59a7ff1345 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ShulkerBoxBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ShulkerBoxBlock.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/block/ShulkerBoxBlock.java +++ b/net/minecraft/world/level/block/ShulkerBoxBlock.java @@ -76,8 +_,8 @@ - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + ) { if (level instanceof ServerLevel serverLevel && level.getBlockEntity(pos) instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity - && canOpen(state, level, pos, shulkerBoxBlockEntity)) { @@ -12,8 +12,8 @@ PiglinAi.angerNearbyPiglins(serverLevel, player, true); } @@ -115,7 +_,7 @@ - itemEntity.setDefaultPickUpDelay(); - level.addFreshEntity(itemEntity); + entity.setDefaultPickUpDelay(); + level.addFreshEntity(entity); } else { - shulkerBoxBlockEntity.unpackLootTable(player); + shulkerBoxBlockEntity.unpackLootTable(player, true); // Paper - force clear loot table so replenish data isn't persisted in the stack @@ -22,7 +22,7 @@ @@ -125,7 +_,15 @@ @Override - protected List getDrops(BlockState state, LootParams.Builder params) { + protected List getDrops(final BlockState state, LootParams.Builder params) { BlockEntity blockEntity = params.getOptionalParameter(LootContextParams.BLOCK_ENTITY); + Runnable reAdd = null; // Paper if (blockEntity instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SignBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SignBlock.java.patch index 3ea7cf679255..136e9b71aa14 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SignBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SignBlock.java.patch @@ -1,27 +1,27 @@ --- a/net/minecraft/world/level/block/SignBlock.java +++ b/net/minecraft/world/level/block/SignBlock.java -@@ -133,7 +_,7 @@ - } else if (!this.otherPlayerIsEditingSign(player, signBlockEntity) - && player.mayBuild() - && this.hasEditableText(player, signBlockEntity, isFacingFrontText)) { -- this.openTextEdit(player, signBlockEntity, isFacingFrontText); -+ this.openTextEdit(player, signBlockEntity, isFacingFrontText, io.papermc.paper.event.player.PlayerOpenSignEvent.Cause.INTERACT); // Paper - Add PlayerOpenSignEvent +@@ -137,7 +_,7 @@ + } else if (executedClickCommand) { + return InteractionResult.SUCCESS_SERVER; + } else if (!this.otherPlayerIsEditingSign(player, sign) && player.mayBuild() && this.hasEditableText(player, sign, isFrontText)) { +- this.openTextEdit(player, sign, isFrontText); ++ this.openTextEdit(player, sign, isFrontText, io.papermc.paper.event.player.PlayerOpenSignEvent.Cause.INTERACT); // Paper - Add PlayerOpenSignEvent return InteractionResult.SUCCESS_SERVER; } else { return InteractionResult.PASS; -@@ -179,7 +_,34 @@ - return woodType; +@@ -176,7 +_,34 @@ + return block instanceof SignBlock signBlock ? signBlock.type() : WoodType.OAK; } + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Add PlayerOpenSignEvent - public void openTextEdit(Player player, SignBlockEntity signEntity, boolean isFrontText) { + public void openTextEdit(final Player player, final SignBlockEntity sign, final boolean isFrontText) { + // Paper start - Add PlayerOpenSignEvent -+ this.openTextEdit(player, signEntity, isFrontText, io.papermc.paper.event.player.PlayerOpenSignEvent.Cause.UNKNOWN); ++ this.openTextEdit(player, sign, isFrontText, io.papermc.paper.event.player.PlayerOpenSignEvent.Cause.UNKNOWN); + } + -+ public void openTextEdit(Player player, SignBlockEntity signEntity, boolean isFrontText, io.papermc.paper.event.player.PlayerOpenSignEvent.Cause cause) { ++ public void openTextEdit(Player player, SignBlockEntity sign, boolean isFrontText, io.papermc.paper.event.player.PlayerOpenSignEvent.Cause cause) { + org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.getBukkitEntity(); -+ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(signEntity.getLevel(), signEntity.getBlockPos()); ++ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(sign.getLevel(), sign.getBlockPos()); + org.bukkit.craftbukkit.block.CraftSign bukkitSign = (org.bukkit.craftbukkit.block.CraftSign) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(bukkitBlock); + io.papermc.paper.event.player.PlayerOpenSignEvent event = new io.papermc.paper.event.player.PlayerOpenSignEvent( + bukkitPlayer, @@ -36,19 +36,19 @@ + case INTERACT -> org.bukkit.event.player.PlayerSignOpenEvent.Cause.INTERACT; + case UNKNOWN -> org.bukkit.event.player.PlayerSignOpenEvent.Cause.UNKNOWN; + }; -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerSignOpenEvent(player, signEntity, isFrontText, legacyCause)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerSignOpenEvent(player, sign, isFrontText, legacyCause)) { + // Paper end - Add PlayerOpenSignEvent + return; + } + } // Paper - Add PlayerOpenSignEvent - signEntity.setAllowedPlayerEditor(player.getUUID()); - player.openTextEdit(signEntity, isFrontText); + sign.setAllowedPlayerEditor(player.getUUID()); + player.openTextEdit(sign, isFrontText); } -@@ -191,6 +_,6 @@ +@@ -188,6 +_,6 @@ @Override - public @Nullable BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { -- return createTickerHelper(blockEntityType, BlockEntityType.SIGN, SignBlockEntity::tick); + public @Nullable BlockEntityTicker getTicker(final Level level, final BlockState blockState, final BlockEntityType type) { +- return createTickerHelper(type, BlockEntityType.SIGN, SignBlockEntity::tick); + return null; // CraftBukkit - remove unnecessary sign ticking } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SmithingTableBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SmithingTableBlock.java.patch index a7365977a076..82af236afd5b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SmithingTableBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SmithingTableBlock.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/level/block/SmithingTableBlock.java +++ b/net/minecraft/world/level/block/SmithingTableBlock.java -@@ -38,8 +_,9 @@ - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { +@@ -40,8 +_,9 @@ + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { if (!level.isClientSide()) { - player.openMenu(state.getMenuProvider(level, pos)); + if (player.openMenu(state.getMenuProvider(level, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SmokerBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SmokerBlock.java.patch index dd2321c6f79c..2c05e9127682 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SmokerBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SmokerBlock.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/level/block/SmokerBlock.java @@ -43,8 +_,7 @@ @Override - protected void openContainer(Level level, BlockPos pos, Player player) { + protected void openContainer(final Level level, final BlockPos pos, final Player player) { BlockEntity blockEntity = level.getBlockEntity(pos); - if (blockEntity instanceof SmokerBlockEntity) { - player.openMenu((MenuProvider)blockEntity); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SnifferEggBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SnifferEggBlock.java.patch index 67fde9dc3442..97b3651f8394 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SnifferEggBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SnifferEggBlock.java.patch @@ -7,36 +7,36 @@ + // Paper start - Call BlockFadeEvent + private void rescheduleTick(ServerLevel level, BlockPos pos) { + int baseDelay = hatchBoost(level, pos) ? level.paperConfig().entities.sniffer.boostedHatchTime.or(BOOSTED_HATCH_TIME_TICKS) : level.paperConfig().entities.sniffer.hatchTime.or(REGULAR_HATCH_TIME_TICKS); // Paper - Configure sniffer egg hatch time -+ level.scheduleTick(pos, this, (baseDelay / 3) + level.random.nextInt(RANDOM_HATCH_OFFSET_TICKS)); ++ level.scheduleTick(pos, this, (baseDelay / 3) + level.getRandom().nextInt(RANDOM_HATCH_OFFSET_TICKS)); + // reschedule to avoid being stuck here and behave like the other calls (see #onPlace) + } + // Paper end - Call BlockFadeEvent + @Override - public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + public void tick(final BlockState state, final ServerLevel level, final BlockPos position, final RandomSource random) { if (!this.isReadyToHatch(state)) { + // Paper start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state.setValue(HATCH, this.getHatchLevel(state) + 1), Block.UPDATE_CLIENTS)) { -+ this.rescheduleTick(level, pos); ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, position, state.setValue(HATCH, this.getHatchLevel(state) + 1), Block.UPDATE_CLIENTS)) { ++ this.rescheduleTick(level, position); + return; + } + // Paper end - level.playSound(null, pos, SoundEvents.SNIFFER_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.9F + random.nextFloat() * 0.2F); -- level.setBlock(pos, state.setValue(HATCH, this.getHatchLevel(state) + 1), Block.UPDATE_CLIENTS); + level.playSound(null, position, SoundEvents.SNIFFER_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.9F + random.nextFloat() * 0.2F); +- level.setBlock(position, state.setValue(HATCH, this.getHatchLevel(state) + 1), Block.UPDATE_CLIENTS); } else { + // Paper start - Call BlockFadeEvent -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, state.getFluidState().createLegacyBlock()).isCancelled()) { -+ this.rescheduleTick(level, pos); ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, position, state.getFluidState().createLegacyBlock()).isCancelled()) { ++ this.rescheduleTick(level, position); + return; + } + // Paper end - Call BlockFadeEvent - level.playSound(null, pos, SoundEvents.SNIFFER_EGG_HATCH, SoundSource.BLOCKS, 0.7F, 0.9F + random.nextFloat() * 0.2F); - level.destroyBlock(pos, false); + level.playSound(null, position, SoundEvents.SNIFFER_EGG_HATCH, SoundSource.BLOCKS, 0.7F, 0.9F + random.nextFloat() * 0.2F); + level.destroyBlock(position, false); Sniffer sniffer = EntityType.SNIFFER.create(level, EntitySpawnReason.BREEDING); @@ -74,7 +_,7 @@ - Vec3 center = pos.getCenter(); + Vec3 spawnAt = position.getCenter(); sniffer.setBaby(true); - sniffer.snapTo(center.x(), center.y(), center.z(), Mth.wrapDegrees(level.random.nextFloat() * 360.0F), 0.0F); + sniffer.snapTo(spawnAt.x(), spawnAt.y(), spawnAt.z(), Mth.wrapDegrees(level.getRandom().nextFloat() * 360.0F), 0.0F); - level.addFreshEntity(sniffer); + level.addFreshEntity(sniffer, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EGG); // Paper - use correct spawn reason } @@ -46,8 +46,8 @@ level.levelEvent(LevelEvent.PARTICLES_EGG_CRACK, pos, 0); } -- int i = flag ? 12000 : 24000; -+ int i = flag ? level.paperConfig().entities.sniffer.boostedHatchTime.or(BOOSTED_HATCH_TIME_TICKS) : level.paperConfig().entities.sniffer.hatchTime.or(REGULAR_HATCH_TIME_TICKS); // Paper - int i1 = i / 3; +- int hatchTime = boosted ? 12000 : 24000; ++ int hatchTime = boosted ? level.paperConfig().entities.sniffer.boostedHatchTime.or(BOOSTED_HATCH_TIME_TICKS) : level.paperConfig().entities.sniffer.hatchTime.or(REGULAR_HATCH_TIME_TICKS); // Paper + int progressionTickDelay = hatchTime / 3; level.gameEvent(GameEvent.BLOCK_PLACE, pos, GameEvent.Context.of(state)); - level.scheduleTick(pos, this, i1 + level.random.nextInt(300)); + level.scheduleTick(pos, this, progressionTickDelay + level.getRandom().nextInt(300)); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SnowLayerBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SnowLayerBlock.java.patch index c6cb4340da02..55214cbb3832 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SnowLayerBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SnowLayerBlock.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/level/block/SnowLayerBlock.java @@ -103,6 +_,11 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (level.getBrightness(LightLayer.BLOCK, pos) > 11) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, Blocks.AIR.defaultBlockState()).isCancelled()) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SpawnerBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SpawnerBlock.java.patch index 5dfdd1e2d92d..a6d2a1368fad 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SpawnerBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SpawnerBlock.java.patch @@ -1,22 +1,22 @@ --- a/net/minecraft/world/level/block/SpawnerBlock.java +++ b/net/minecraft/world/level/block/SpawnerBlock.java -@@ -40,9 +_,17 @@ +@@ -39,10 +_,18 @@ @Override - protected void spawnAfterBreak(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { - super.spawnAfterBreak(state, level, pos, stack, dropExperience); + protected void spawnAfterBreak(final BlockState state, final ServerLevel level, final BlockPos pos, final ItemStack tool, final boolean dropExperience) { + super.spawnAfterBreak(state, level, pos, tool, dropExperience); + // CraftBukkit start - Delegate to getExpDrop + } + + @Override -+ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { ++ public int getExpDrop(final BlockState state, final ServerLevel level, final BlockPos pos, final ItemStack tool, final boolean dropExperience) { if (dropExperience) { - int i = 15 + level.random.nextInt(15) + level.random.nextInt(15); -- this.popExperience(level, pos, i); -+ // this.popExperience(level, pos, i); -+ return i; + RandomSource random = level.getRandom(); + int magicCount = 15 + random.nextInt(15) + random.nextInt(15); +- this.popExperience(level, pos, magicCount); ++ // this.popExperience(level, pos, magicCount); ++ return magicCount; } -- } + return 0; + // CraftBukkit end -+ } + } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SpongeBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SpongeBlock.java.patch index 1ce60091fb37..5389db97dab7 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SpongeBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SpongeBlock.java.patch @@ -1,63 +1,61 @@ --- a/net/minecraft/world/level/block/SpongeBlock.java +++ b/net/minecraft/world/level/block/SpongeBlock.java -@@ -50,7 +_,8 @@ +@@ -52,7 +_,8 @@ } - private boolean removeWaterBreadthFirstSearch(Level level, BlockPos pos) { -- return BlockPos.breadthFirstTraversal( + private boolean removeWaterBreadthFirstSearch(final Level level, final BlockPos startPos) { +- return BlockPos.breadthFirstTraversal(startPos, 6, 65, (pos, consumer) -> { + org.bukkit.craftbukkit.util.BlockStateListPopulator blockList = new org.bukkit.craftbukkit.util.BlockStateListPopulator(level); // CraftBukkit - Use BlockStateListPopulator -+ BlockPos.breadthFirstTraversal( - pos, - 6, - 65, -@@ -63,16 +_,18 @@ - if (blockPos.equals(pos)) { - return BlockPos.TraversalNodeStatus.ACCEPT; ++ BlockPos.breadthFirstTraversal(startPos, 6, 65, (pos, consumer) -> { + for (Direction direction : ALL_DIRECTIONS) { + consumer.accept(pos.relative(direction)); + } +@@ -60,28 +_,65 @@ + if (pos.equals(startPos)) { + return BlockPos.TraversalNodeStatus.ACCEPT; + } else { +- BlockState state = level.getBlockState(pos); +- FluidState fluidState = level.getFluidState(pos); ++ // CraftBukkit start ++ BlockState blockState = blockList.getBlockState(pos); ++ FluidState fluidState = blockList.getFluidState(pos); ++ // CraftBukkit end + if (!fluidState.is(FluidTags.WATER)) { + return BlockPos.TraversalNodeStatus.SKIP; +- } else if (state.getBlock() instanceof BucketPickup bucketPickup && !bucketPickup.pickupBlock(null, level, pos, state).isEmpty()) { ++ } else if (blockState.getBlock() instanceof BucketPickup bucketPickup ++ && !bucketPickup.pickupBlock(null, blockList, pos, blockState).isEmpty()) { // CraftBukkit + return BlockPos.TraversalNodeStatus.ACCEPT; + } else { +- if (state.getBlock() instanceof LiquidBlock) { +- level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); ++ if (blockState.getBlock() instanceof LiquidBlock) { ++ blockList.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); // CraftBukkit } else { -- BlockState blockState = level.getBlockState(blockPos); -- FluidState fluidState = level.getFluidState(blockPos); -+ // CraftBukkit start -+ BlockState blockState = blockList.getBlockState(blockPos); -+ FluidState fluidState = blockList.getFluidState(blockPos); -+ // CraftBukkit end - if (!fluidState.is(FluidTags.WATER)) { +- if (!state.is(Blocks.KELP) && !state.is(Blocks.KELP_PLANT) && !state.is(Blocks.SEAGRASS) && !state.is(Blocks.TALL_SEAGRASS)) { ++ if (!blockState.is(Blocks.KELP) && !blockState.is(Blocks.KELP_PLANT) && !blockState.is(Blocks.SEAGRASS) && !blockState.is(Blocks.TALL_SEAGRASS)) { return BlockPos.TraversalNodeStatus.SKIP; - } else if (blockState.getBlock() instanceof BucketPickup bucketPickup -- && !bucketPickup.pickupBlock(null, level, blockPos, blockState).isEmpty()) { -+ && !bucketPickup.pickupBlock(null, blockList, blockPos, blockState).isEmpty()) { // CraftBukkit - return BlockPos.TraversalNodeStatus.ACCEPT; - } else { - if (blockState.getBlock() instanceof LiquidBlock) { -- level.setBlock(blockPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); -+ blockList.setBlock(blockPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); // CraftBukkit - } else { - if (!blockState.is(Blocks.KELP) - && !blockState.is(Blocks.KELP_PLANT) -@@ -81,16 +_,49 @@ - return BlockPos.TraversalNodeStatus.SKIP; - } - -- BlockEntity blockEntity = blockState.hasBlockEntity() ? level.getBlockEntity(blockPos) : null; -- dropResources(blockState, level, blockPos, blockEntity); -- level.setBlock(blockPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); -+ // CraftBukkit start -+ // BlockEntity blockEntity = blockState.hasBlockEntity() ? level.getBlockEntity(blockPos) : null; -+ // dropResources(blockState, level, blockPos, blockEntity); -+ blockList.setBlock(blockPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); -+ // CraftBukkit end - } - - return BlockPos.TraversalNodeStatus.ACCEPT; } + +- BlockEntity blockEntity = state.hasBlockEntity() ? level.getBlockEntity(pos) : null; +- dropResources(state, level, pos, blockEntity); +- level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); ++ // CraftBukkit start ++ // BlockEntity blockEntity = blockState.hasBlockEntity() ? level.getBlockEntity(pos) : null; ++ // dropResources(blockState, level, pos, blockEntity); ++ blockList.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); ++ // CraftBukkit end } + + return BlockPos.TraversalNodeStatus.ACCEPT; } -- ) -- > 1; -+ ); + } +- }) > 1; ++ }); + // CraftBukkit start + java.util.List snapshots = blockList.getSnapshotBlocks(); // Is a clone + if (!snapshots.isEmpty()) { -+ final org.bukkit.block.Block sponge = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); ++ final org.bukkit.block.Block sponge = org.bukkit.craftbukkit.block.CraftBlock.at(level, startPos); + + org.bukkit.event.block.SpongeAbsorbEvent event = new org.bukkit.event.block.SpongeAbsorbEvent(sponge, (java.util.List) (java.util.List) snapshots); + if (!event.callEvent()) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SpreadingSnowyBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SpreadingSnowyBlock.java.patch new file mode 100644 index 000000000000..1390b4a5cede --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SpreadingSnowyBlock.java.patch @@ -0,0 +1,28 @@ +--- a/net/minecraft/world/level/block/SpreadingSnowyBlock.java ++++ b/net/minecraft/world/level/block/SpreadingSnowyBlock.java +@@ -46,10 +_,16 @@ + + @Override + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { ++ if (this instanceof GrassBlock && level.paperConfig().tickRates.grassSpread != 1 && (level.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % level.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks + Registry blocks = level.registryAccess().lookupOrThrow(Registries.BLOCK); + Optional baseBlock = blocks.getOptional(this.baseBlock); + if (!baseBlock.isEmpty()) { + if (!canStayAlive(state, level, pos)) { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, baseBlock.get().defaultBlockState()).isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + level.setBlockAndUpdate(pos, baseBlock.get().defaultBlockState()); + } else { + if (level.getMaxLocalRawBrightness(pos.above()) >= 9) { +@@ -58,7 +_,7 @@ + for (int i = 0; i < 4; i++) { + BlockPos testPos = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1); + if (level.getBlockState(testPos).is(baseBlock.get()) && canPropagate(defaultBlockState, level, testPos)) { +- level.setBlockAndUpdate(testPos, defaultBlockState.setValue(SNOWY, isSnowySetting(level.getBlockState(testPos.above())))); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, testPos, defaultBlockState.setValue(SNOWY, isSnowySetting(level.getBlockState(testPos.above()))), Block.UPDATE_ALL); // CraftBukkit + } + } + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java.patch deleted file mode 100644 index 21f0f33a6996..000000000000 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- a/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java -+++ b/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java -@@ -39,7 +_,13 @@ - - @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { -+ if (this instanceof GrassBlock && level.paperConfig().tickRates.grassSpread != 1 && (level.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % level.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks - if (!canBeGrass(state, level, pos)) { -+ // CraftBukkit start -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(level, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) { -+ return; -+ } -+ // CraftBukkit end - level.setBlockAndUpdate(pos, Blocks.DIRT.defaultBlockState()); - } else { - if (level.getMaxLocalRawBrightness(pos.above()) >= 9) { -@@ -48,7 +_,7 @@ - for (int i = 0; i < 4; i++) { - BlockPos blockPos = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1); - if (level.getBlockState(blockPos).is(Blocks.DIRT) && canPropagate(blockState, level, blockPos)) { -- level.setBlockAndUpdate(blockPos, blockState.setValue(SNOWY, isSnowySetting(level.getBlockState(blockPos.above())))); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, blockState.setValue(SNOWY, isSnowySetting(level.getBlockState(blockPos.above()))), Block.UPDATE_ALL); // CraftBukkit - } - } - } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/StemBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/StemBlock.java.patch index 6649f03cd094..f7aa1c9776a8 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/StemBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/StemBlock.java.patch @@ -1,38 +1,38 @@ --- a/net/minecraft/world/level/block/StemBlock.java +++ b/net/minecraft/world/level/block/StemBlock.java -@@ -70,11 +_,11 @@ - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { +@@ -83,11 +_,11 @@ + protected void randomTick(BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (level.getRawBrightness(pos, 0) >= 9) { float growthSpeed = CropBlock.getGrowthSpeed(this, level, pos); - if (random.nextInt((int)(25.0F / growthSpeed) + 1) == 0) { + if (random.nextFloat() < ((this == Blocks.PUMPKIN_STEM ? level.spigotConfig.pumpkinModifier : level.spigotConfig.melonModifier) / (100.0F * (Math.floor((25.0F / growthSpeed) + 1))))) { // Spigot - SPIGOT-7159: Better modifier resolution - int ageValue = state.getValue(AGE); - if (ageValue < 7) { - state = state.setValue(AGE, ageValue + 1); + int age = state.getValue(AGE); + if (age < 7) { + state = state.setValue(AGE, age + 1); - level.setBlock(pos, state, Block.UPDATE_CLIENTS); + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state, Block.UPDATE_CLIENTS); // CraftBukkit } else { - Direction randomDirection = Direction.Plane.HORIZONTAL.getRandomDirection(random); - BlockPos blockPos = pos.relative(randomDirection); -@@ -84,7 +_,11 @@ - Optional optional = registry.getOptional(this.fruit); - Optional optional1 = registry.getOptional(this.attachedStem); - if (optional.isPresent() && optional1.isPresent()) { -- level.setBlockAndUpdate(blockPos, optional.get().defaultBlockState()); + Direction direction = Direction.Plane.HORIZONTAL.getRandomDirection(random); + BlockPos relative = pos.relative(direction); +@@ -97,7 +_,11 @@ + Optional fruit = blocks.getOptional(this.fruit); + Optional stem = blocks.getOptional(this.attachedStem); + if (fruit.isPresent() && stem.isPresent()) { +- level.setBlockAndUpdate(relative, fruit.get().defaultBlockState()); + // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, blockPos, optional.get().defaultBlockState(), Block.UPDATE_ALL)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, relative, fruit.get().defaultBlockState(), Block.UPDATE_ALL)) { + return; + } + // CraftBukkit end - level.setBlockAndUpdate(pos, optional1.get().defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, randomDirection)); + level.setBlockAndUpdate(pos, stem.get().defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, direction)); } } -@@ -112,7 +_,7 @@ - public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { - int min = Math.min(7, state.getValue(AGE) + Mth.nextInt(level.random, 2, 5)); - BlockState blockState = state.setValue(AGE, min); -- level.setBlock(pos, blockState, Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, blockState, Block.UPDATE_CLIENTS); // CraftBukkit - if (min == 7) { - blockState.randomTick(level, pos, level.random); +@@ -125,7 +_,7 @@ + public void performBonemeal(final ServerLevel level, final RandomSource random, final BlockPos pos, final BlockState state) { + int age = Math.min(7, state.getValue(AGE) + Mth.nextInt(random, 2, 5)); + BlockState newState = state.setValue(AGE, age); +- level.setBlock(pos, newState, Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, newState, Block.UPDATE_CLIENTS); // CraftBukkit + if (age == 7) { + newState.randomTick(level, pos, random); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/StonecutterBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/StonecutterBlock.java.patch index f8283db868a2..3d21aeed59fb 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/StonecutterBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/StonecutterBlock.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/level/block/StonecutterBlock.java +++ b/net/minecraft/world/level/block/StonecutterBlock.java -@@ -48,8 +_,9 @@ - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { +@@ -50,8 +_,9 @@ + final BlockState state, final Level level, final BlockPos pos, final Player player, final BlockHitResult hitResult + ) { if (!level.isClientSide()) { - player.openMenu(state.getMenuProvider(level, pos)); + if (player.openMenu(state.getMenuProvider(level, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SugarCaneBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SugarCaneBlock.java.patch index 8fff3fd247e1..128b8087e3d5 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SugarCaneBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SugarCaneBlock.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/world/level/block/SugarCaneBlock.java +++ b/net/minecraft/world/level/block/SugarCaneBlock.java @@ -55,12 +_,13 @@ - i++; + height++; } -- if (i < 3) { -+ if (i < level.paperConfig().maxGrowthHeight.reeds) { // Paper - Configurable cactus/bamboo/reed growth height - int ageValue = state.getValue(AGE); -- if (ageValue == 15) { +- if (height < 3) { ++ if (height < level.paperConfig().maxGrowthHeight.reeds) { // Paper - Configurable cactus/bamboo/reed growth height + int age = state.getValue(AGE); +- if (age == 15) { - level.setBlockAndUpdate(pos.above(), this.defaultBlockState()); + int modifier = level.spigotConfig.caneModifier; // Spigot - SPIGOT-7159: Better modifier resolution -+ if (ageValue >= 15 || (modifier != 100 && random.nextFloat() < (modifier / (100.0F * 16)))) { // Spigot - SPIGOT-7159: Better modifier resolution ++ if (age >= 15 || (modifier != 100 && random.nextFloat() < (modifier / (100.0F * 16)))) { // Spigot - SPIGOT-7159: Better modifier resolution + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos.above(), this.defaultBlockState(), Block.UPDATE_ALL); // CraftBukkit level.setBlock(pos, state.setValue(AGE, 0), Block.UPDATE_NONE); - } else { + } else if (modifier == 100 || random.nextFloat() < (modifier / (100.0F * 16))) { // Spigot - SPIGOT-7159: Better modifier resolution - level.setBlock(pos, state.setValue(AGE, ageValue + 1), Block.UPDATE_NONE); + level.setBlock(pos, state.setValue(AGE, age + 1), Block.UPDATE_NONE); } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch index 80ea0cecb365..ff448a23dfea 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SweetBerryBushBlock.java.patch @@ -1,47 +1,48 @@ --- a/net/minecraft/world/level/block/SweetBerryBushBlock.java +++ b/net/minecraft/world/level/block/SweetBerryBushBlock.java -@@ -71,15 +_,16 @@ +@@ -71,9 +_,9 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { - int ageValue = state.getValue(AGE); -- if (ageValue < 3 && random.nextInt(5) == 0 && level.getRawBrightness(pos.above(), 0) >= 9) { -+ if (ageValue < 3 && random.nextFloat() < (level.spigotConfig.sweetBerryModifier / (100.0F * 5)) && level.getRawBrightness(pos.above(), 0) >= 9) { // Spigot - SPIGOT-7159: Better modifier resolution - BlockState blockState = state.setValue(AGE, ageValue + 1); -- level.setBlock(pos, blockState, Block.UPDATE_CLIENTS); -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, blockState, Block.UPDATE_CLIENTS)) return; // CraftBukkit - level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(blockState)); + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { + int age = state.getValue(AGE); +- if (age < 3 && random.nextInt(5) == 0 && level.getRawBrightness(pos.above(), 0) >= 9) { ++ if (age < 3 && random.nextFloat() < (level.spigotConfig.sweetBerryModifier / (100.0F * 5)) && level.getRawBrightness(pos.above(), 0) >= 9) { // Spigot - SPIGOT-7159: Better modifier resolution + BlockState newState = state.setValue(AGE, age + 1); +- level.setBlock(pos, newState, Block.UPDATE_CLIENTS); ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, newState, Block.UPDATE_CLIENTS)) return; // CraftBukkit + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newState)); } } - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -87,6 +_,7 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent - if (entity instanceof LivingEntity && entity.getType() != EntityType.FOX && entity.getType() != EntityType.BEE) { + if (entity instanceof LivingEntity && !entity.is(EntityType.FOX) && !entity.is(EntityType.BEE)) { entity.makeStuckInBlock(state, new Vec3(0.8F, 0.75, 0.8F)); if (level instanceof ServerLevel serverLevel && state.getValue(AGE) != 0) { -@@ -88,7 +_,7 @@ - double abs = Math.abs(vec3.x()); - double abs1 = Math.abs(vec3.z()); - if (abs >= 0.003F || abs1 >= 0.003F) { +@@ -95,7 +_,7 @@ + double xs = Math.abs(movement.x()); + double zs = Math.abs(movement.z()); + if (xs >= 0.003F || zs >= 0.003F) { - entity.hurtServer(serverLevel, level.damageSources().sweetBerryBush(), 1.0F); + entity.hurtServer(serverLevel, level.damageSources().sweetBerryBush().eventBlockDamager(level, pos), 1.0F); // CraftBukkit } } } -@@ -110,6 +_,7 @@ - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { +@@ -125,6 +_,7 @@ + ) { if (state.getValue(AGE) > 1) { if (level instanceof ServerLevel serverLevel) { + java.util.List drops = new java.util.ArrayList<>(); // Paper Block.dropFromBlockInteractLootTable( serverLevel, BuiltInLootTables.HARVEST_SWEET_BERRY_BUSH, -@@ -117,8 +_,17 @@ +@@ -132,8 +_,17 @@ level.getBlockEntity(pos), null, player, -- (serverLevel1, itemStack) -> Block.popResource(serverLevel1, pos, itemStack) -+ (serverLevel1, itemStack) -> drops.add(itemStack) // Paper +- (serverlvl, itemStack) -> Block.popResource(serverlvl, pos, itemStack) ++ (serverlvl, itemStack) -> drops.add(itemStack) // Paper ); + // CraftBukkit start - useWithoutItem is always MAIN_HAND + org.bukkit.event.player.PlayerHarvestBlockEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerHarvestBlockEvent(level, pos, player, InteractionHand.MAIN_HAND, drops); @@ -53,5 +54,5 @@ + } + // CraftBukkit end serverLevel.playSound( - null, pos, SoundEvents.SWEET_BERRY_BUSH_PICK_BERRIES, SoundSource.BLOCKS, 1.0F, 0.8F + serverLevel.random.nextFloat() * 0.4F + null, pos, SoundEvents.SWEET_BERRY_BUSH_PICK_BERRIES, SoundSource.BLOCKS, 1.0F, 0.8F + serverLevel.getRandom().nextFloat() * 0.4F ); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/TargetBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/TargetBlock.java.patch index 6ea06255f252..bc05106f9b51 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/TargetBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/TargetBlock.java.patch @@ -2,25 +2,25 @@ +++ b/net/minecraft/world/level/block/TargetBlock.java @@ -42,6 +_,10 @@ @Override - protected void onProjectileHit(Level level, BlockState state, BlockHitResult hit, Projectile projectile) { - int i = updateRedstoneOutput(level, state, hit, projectile); + protected void onProjectileHit(final Level level, final BlockState state, final BlockHitResult hitResult, final Projectile projectile) { + int outputStrength = updateRedstoneOutput(level, state, hitResult, projectile); + // Paper start - Add TargetHitEvent + } -+ private static void awardTargetHitCriteria(Projectile projectile, BlockHitResult hit, int i) { ++ private static void awardTargetHitCriteria(Projectile projectile, BlockHitResult hitResult, int outputStrength) { + // Paper end - Add TargetHitEvent - if (projectile.getOwner() instanceof ServerPlayer serverPlayer) { - serverPlayer.awardStat(Stats.TARGET_HIT); - CriteriaTriggers.TARGET_BLOCK_HIT.trigger(serverPlayer, projectile, hit.getLocation(), i); + if (projectile.getOwner() instanceof ServerPlayer playerOwner) { + playerOwner.awardStat(Stats.TARGET_HIT); + CriteriaTriggers.TARGET_BLOCK_HIT.trigger(playerOwner, projectile, hitResult.getLocation(), outputStrength); @@ -51,9 +_,29 @@ - private static int updateRedstoneOutput(LevelAccessor level, BlockState state, BlockHitResult hit, Entity projectile) { - int redstoneStrength = getRedstoneStrength(hit, hit.getLocation()); - int i = projectile instanceof AbstractArrow ? 20 : 8; + private static int updateRedstoneOutput(final LevelAccessor level, final BlockState state, final BlockHitResult hitResult, final Entity entity) { + int redstoneStrength = getRedstoneStrength(hitResult, hitResult.getLocation()); + int duration = entity instanceof AbstractArrow ? 20 : 8; + // Paper start - Add TargetHitEvent + boolean shouldAward = false; -+ if (projectile instanceof Projectile) { -+ final org.bukkit.craftbukkit.block.CraftBlock craftBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, hit.getBlockPos()); -+ final org.bukkit.block.BlockFace blockFace = org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(hit.getDirection()); -+ final io.papermc.paper.event.block.TargetHitEvent targetHitEvent = new io.papermc.paper.event.block.TargetHitEvent((org.bukkit.entity.Projectile) projectile.getBukkitEntity(), craftBlock, blockFace, redstoneStrength); ++ if (entity instanceof Projectile) { ++ final org.bukkit.craftbukkit.block.CraftBlock craftBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, hitResult.getBlockPos()); ++ final org.bukkit.block.BlockFace blockFace = org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(hitResult.getDirection()); ++ final io.papermc.paper.event.block.TargetHitEvent targetHitEvent = new io.papermc.paper.event.block.TargetHitEvent((org.bukkit.entity.Projectile) entity.getBukkitEntity(), craftBlock, blockFace, redstoneStrength); + if (targetHitEvent.callEvent()) { + redstoneStrength = targetHitEvent.getSignalStrength(); + shouldAward = true; @@ -29,13 +29,13 @@ + } + } + // Paper end - Add TargetHitEvent - if (!level.getBlockTicks().hasScheduledTick(hit.getBlockPos(), state.getBlock())) { - setOutputPower(level, state, redstoneStrength, hit.getBlockPos(), i); + if (!level.getBlockTicks().hasScheduledTick(hitResult.getBlockPos(), state.getBlock())) { + setOutputPower(level, state, redstoneStrength, hitResult.getBlockPos(), duration); } + + // Paper start - Award Hit Criteria after Block Update + if (shouldAward) { -+ awardTargetHitCriteria((Projectile) projectile, hit, redstoneStrength); ++ awardTargetHitCriteria((Projectile) entity, hitResult, redstoneStrength); + } + // Paper end - Award Hit Criteria after Block Update diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/TntBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/TntBlock.java.patch index 478b2ae1b110..a2dbd1b38a0a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/TntBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/TntBlock.java.patch @@ -2,66 +2,66 @@ +++ b/net/minecraft/world/level/block/TntBlock.java @@ -47,7 +_,7 @@ @Override - protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) { if (!oldState.is(state.getBlock())) { - if (level.hasNeighborSignal(pos) && prime(level, pos)) { + if (level.hasNeighborSignal(pos) && prime(level, pos, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.REDSTONE, null, null))) { // CraftBukkit - TNTPrimeEvent level.removeBlock(pos, false); } } -@@ -55,7 +_,7 @@ - - @Override - protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { +@@ -57,7 +_,7 @@ + protected void neighborChanged( + final BlockState state, final Level level, final BlockPos pos, final Block block, final @Nullable Orientation orientation, final boolean movedByPiston + ) { - if (level.hasNeighborSignal(pos) && prime(level, pos)) { + if (level.hasNeighborSignal(pos) && prime(level, pos, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.REDSTONE, null, null))) { // CraftBukkit - TNTPrimeEvent level.removeBlock(pos, false); } } -@@ -63,7 +_,7 @@ +@@ -65,7 +_,7 @@ @Override - public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) { + public BlockState playerWillDestroy(final Level level, final BlockPos pos, final BlockState state, final Player player) { if (!level.isClientSide() && !player.getAbilities().instabuild && state.getValue(UNSTABLE)) { - prime(level, pos); + prime(level, pos, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.BLOCK_BREAK, player, null)); // CraftBukkit - TNTPrimeEvent } return super.playerWillDestroy(level, pos, state, player); -@@ -80,11 +_,16 @@ +@@ -82,11 +_,16 @@ } - public static boolean prime(Level level, BlockPos pos) { + public static boolean prime(final Level level, final BlockPos pos) { - return prime(level, pos, null); + // Paper start + return prime(level, pos, null, () -> true); + } -+ public static boolean prime(Level level, BlockPos pos, java.util.function.BooleanSupplier event) { ++ public static boolean prime(final Level level, final BlockPos pos, final java.util.function.BooleanSupplier event) { + return prime(level, pos, null, event); + // Paper end } -- private static boolean prime(Level level, BlockPos pos, @Nullable LivingEntity entity) { +- private static boolean prime(final Level level, final BlockPos pos, final @Nullable LivingEntity source) { - if (level instanceof ServerLevel serverLevel && serverLevel.getGameRules().get(GameRules.TNT_EXPLODES)) { -+ private static boolean prime(Level level, BlockPos pos, @Nullable LivingEntity entity, java.util.function.BooleanSupplier event) { // Paper ++ private static boolean prime(final Level level, final BlockPos pos, final @Nullable LivingEntity source, final java.util.function.BooleanSupplier event) { // Paper + if (level instanceof ServerLevel serverLevel && serverLevel.getGameRules().get(GameRules.TNT_EXPLODES) && event.getAsBoolean()) { // Paper - PrimedTnt primedTnt = new PrimedTnt(level, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, entity); - level.addFreshEntity(primedTnt); - level.playSound(null, primedTnt.getX(), primedTnt.getY(), primedTnt.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F); -@@ -102,7 +_,7 @@ - if (!stack.is(Items.FLINT_AND_STEEL) && !stack.is(Items.FIRE_CHARGE)) { - return super.useItemOn(stack, state, level, pos, player, hand, hitResult); + PrimedTnt tnt = new PrimedTnt(level, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, source); + level.addFreshEntity(tnt); + level.playSound(null, tnt.getX(), tnt.getY(), tnt.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F); +@@ -110,7 +_,7 @@ + if (!itemStack.is(Items.FLINT_AND_STEEL) && !itemStack.is(Items.FIRE_CHARGE)) { + return super.useItemOn(itemStack, state, level, pos, player, hand, hitResult); } else { - if (prime(level, pos, player)) { + if (prime(level, pos, player, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.PLAYER, player, null))) { // Paper level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL_IMMEDIATE); - Item item = stack.getItem(); - if (stack.is(Items.FLINT_AND_STEEL)) { -@@ -128,7 +_,7 @@ + Item item = itemStack.getItem(); + if (itemStack.is(Items.FLINT_AND_STEEL)) { +@@ -136,7 +_,7 @@ Entity owner = projectile.getOwner(); if (projectile.isOnFire() - && projectile.mayInteract(serverLevel, blockPos) -- && prime(level, blockPos, owner instanceof LivingEntity ? (LivingEntity)owner : null)) { -+ && prime(level, blockPos, owner instanceof LivingEntity ? (LivingEntity)owner : null, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(projectile, blockPos, state.getFluidState().createLegacyBlock()) && org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, blockPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.PROJECTILE, projectile, null))) { // Paper - level.removeBlock(blockPos, false); + && projectile.mayInteract(serverLevel, pos) +- && prime(level, pos, owner instanceof LivingEntity ? (LivingEntity)owner : null)) { ++ && prime(level, pos, owner instanceof LivingEntity ? (LivingEntity)owner : null, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(projectile, pos, state.getFluidState().createLegacyBlock()) && org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.PROJECTILE, projectile, null))) { // Paper + level.removeBlock(pos, false); } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/TrapDoorBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/TrapDoorBlock.java.patch index 81c5d46478c4..7d266526bbb6 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/TrapDoorBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/TrapDoorBlock.java.patch @@ -1,26 +1,26 @@ --- a/net/minecraft/world/level/block/TrapDoorBlock.java +++ b/net/minecraft/world/level/block/TrapDoorBlock.java -@@ -128,7 +_,37 @@ +@@ -133,7 +_,37 @@ if (!level.isClientSide()) { - boolean hasNeighborSignal = level.hasNeighborSignal(pos); - if (hasNeighborSignal != state.getValue(POWERED)) { -- if (state.getValue(OPEN) != hasNeighborSignal) { + boolean signal = level.hasNeighborSignal(pos); + if (signal != state.getValue(POWERED)) { +- if (state.getValue(OPEN) != signal) { + // CraftBukkit start + org.bukkit.block.Block bblock = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + int power = bblock.getBlockPower(); + int oldPower = state.getValue(TrapDoorBlock.OPEN) ? 15 : 0; + -+ if (oldPower == 0 ^ power == 0 || neighborBlock.defaultBlockState().isSignalSource()) { ++ if (oldPower == 0 ^ power == 0 || block.defaultBlockState().isSignalSource()) { + org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(bblock, oldPower, power); + level.getCraftServer().getPluginManager().callEvent(eventRedstone); -+ hasNeighborSignal = eventRedstone.getNewCurrent() > 0; ++ signal = eventRedstone.getNewCurrent() > 0; + } + // CraftBukkit end + // Paper start - break redstone on trapdoors early -+ boolean open = state.getValue(TrapDoorBlock.OPEN) != hasNeighborSignal; ++ boolean open = state.getValue(TrapDoorBlock.OPEN) != signal; + // note: this must run before any state for this block/its neighbours are written to the world + // we allow the redstone event to fire so that plugins can block -+ if (hasNeighborSignal && open) { // if we are now powered and it caused the trap door to open ++ if (signal && open) { // if we are now powered and it caused the trap door to open + // in this case, first check for the redstone on top first + BlockPos abovePos = pos.above(); + BlockState above = level.getBlockState(abovePos); @@ -36,6 +36,6 @@ + } + if (open) { + // Paper end - break redstone on trapdoors early - state = state.setValue(OPEN, hasNeighborSignal); - this.playSound(null, level, pos, hasNeighborSignal); + state = state.setValue(OPEN, signal); + this.playSound(null, level, pos, signal); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/TripWireBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/TripWireBlock.java.patch index 3b60250e8717..56e3ed1b8398 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/TripWireBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/TripWireBlock.java.patch @@ -1,77 +1,77 @@ --- a/net/minecraft/world/level/block/TripWireBlock.java +++ b/net/minecraft/world/level/block/TripWireBlock.java -@@ -73,6 +_,7 @@ +@@ -72,6 +_,7 @@ @Override - public BlockState getStateForPlacement(BlockPlaceContext context) { + public BlockState getStateForPlacement(final BlockPlaceContext context) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates) return this.defaultBlockState(); // Paper - place tripwire without updating BlockGetter level = context.getLevel(); - BlockPos clickedPos = context.getClickedPos(); + BlockPos pos = context.getClickedPos(); return this.defaultBlockState() -@@ -93,6 +_,7 @@ - BlockState neighborState, - RandomSource random +@@ -92,6 +_,7 @@ + final BlockState neighbourState, + final RandomSource random ) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates) return state; // Paper - prevent tripwire from updating - return direction.getAxis().isHorizontal() - ? state.setValue(PROPERTY_BY_DIRECTION.get(direction), this.shouldConnectTo(neighborState, direction)) - : super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); -@@ -100,6 +_,7 @@ + return directionToNeighbour.getAxis().isHorizontal() + ? state.setValue(PROPERTY_BY_DIRECTION.get(directionToNeighbour), this.shouldConnectTo(neighbourState, directionToNeighbour)) + : super.updateShape(state, level, ticks, pos, directionToNeighbour, neighbourPos, neighbourState, random); +@@ -99,6 +_,7 @@ @Override - protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates) return; // Paper - prevent adjacent tripwires from updating if (!oldState.is(state.getBlock())) { this.updateSource(level, pos, state); } -@@ -107,6 +_,7 @@ +@@ -106,6 +_,7 @@ @Override - protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + protected void affectNeighborsAfterRemoval(final BlockState state, final ServerLevel level, final BlockPos pos, final boolean movedByPiston) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates) return; // Paper - prevent adjacent tripwires from updating if (!movedByPiston) { this.updateSource(level, pos, state.setValue(POWERED, true)); } -@@ -114,6 +_,7 @@ +@@ -113,6 +_,7 @@ @Override - public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) { + public BlockState playerWillDestroy(final Level level, final BlockPos pos, final BlockState state, final Player player) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates) return state; // Paper - prevent disarming tripwires if (!level.isClientSide() && !player.getMainHandItem().isEmpty() && player.getMainHandItem().is(Items.SHEARS)) { level.setBlock(pos, state.setValue(DISARMED, true), Block.UPDATE_NONE); level.gameEvent(player, GameEvent.SHEAR, pos); -@@ -123,6 +_,7 @@ +@@ -122,6 +_,7 @@ } - private void updateSource(Level level, BlockPos pos, BlockState state) { + private void updateSource(final Level level, final BlockPos pos, final BlockState state) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates) return; // Paper - prevent adjacent tripwires from updating for (Direction direction : new Direction[]{Direction.SOUTH, Direction.WEST}) { for (int i = 1; i < 42; i++) { - BlockPos blockPos = pos.relative(direction, i); -@@ -148,6 +_,8 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { + BlockPos testPos = pos.relative(direction, i); +@@ -154,6 +_,8 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates) return; // Paper - prevent tripwires from detecting collision + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (!level.isClientSide()) { if (!state.getValue(POWERED)) { this.checkPressed(level, pos, List.of(entity)); -@@ -157,6 +_,7 @@ +@@ -163,6 +_,7 @@ @Override - protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void tick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates) return; // Paper - prevent tripwire pressed check if (level.getBlockState(pos).getValue(POWERED)) { this.checkPressed(level, pos); } -@@ -180,6 +_,39 @@ +@@ -186,6 +_,39 @@ } } } + + // CraftBukkit start - Call interact even when triggering connected tripwire -+ if (poweredValue != flag && flag && blockState.getValue(TripWireBlock.ATTACHED)) { ++ if (wasPressed != shouldBePressed && shouldBePressed && state.getValue(TripWireBlock.ATTACHED)) { + org.bukkit.plugin.PluginManager manager = level.getCraftServer().getPluginManager(); + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + boolean allowed = false; @@ -103,5 +103,5 @@ + } + // CraftBukkit end - if (flag != poweredValue) { - blockState = blockState.setValue(POWERED, flag); + if (shouldBePressed != wasPressed) { + state = state.setValue(POWERED, shouldBePressed); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/TripWireHookBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/TripWireHookBlock.java.patch index 426cec4007c3..c2c1b0663d4b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/TripWireHookBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/TripWireHookBlock.java.patch @@ -1,31 +1,30 @@ --- a/net/minecraft/world/level/block/TripWireHookBlock.java +++ b/net/minecraft/world/level/block/TripWireHookBlock.java -@@ -112,10 +_,10 @@ - if (optionalValue.isPresent()) { - Direction direction = optionalValue.get(); - boolean flag = hookState.getOptionalValue(ATTACHED).orElse(false); -- boolean flag1 = hookState.getOptionalValue(POWERED).orElse(false); -+ boolean flag1 = hookState.getOptionalValue(POWERED).orElse(false); // Paper - diff on change, for event below - Block block = hookState.getBlock(); - boolean flag2 = !attaching; -- boolean flag3 = false; -+ boolean flag3 = false; // Paper - diff on change, for event below - int i = 0; - BlockState[] blockStates = new BlockState[42]; +@@ -119,10 +_,10 @@ + if (facingOptional.isPresent()) { + Direction direction = facingOptional.get(); + boolean wasAttached = state.getOptionalValue(ATTACHED).orElse(false); +- boolean wasPowered = state.getOptionalValue(POWERED).orElse(false); ++ boolean wasPowered = state.getOptionalValue(POWERED).orElse(false); // Paper - diff on change, for event below + Block block = state.getBlock(); + boolean attached = !isBeingDestroyed; +- boolean powered = false; ++ boolean powered = false; // Paper - diff on change, for event below + int receiverPos = 0; + BlockState[] wireStates = new BlockState[42]; -@@ -151,21 +_,48 @@ - flag2 &= i > 1; - flag3 &= flag2; - BlockState blockState1 = block.defaultBlockState().trySetValue(ATTACHED, flag2).trySetValue(POWERED, flag3); +@@ -158,8 +_,20 @@ + attached &= receiverPos > 1; + powered &= attached; + BlockState newState = block.defaultBlockState().trySetValue(ATTACHED, attached).trySetValue(POWERED, powered); + boolean cancelledEmitterHook = false, cancelledReceiverHook = false; // Paper - Call BlockRedstoneEvent -+ boolean wasPowered = flag1, willBePowered = flag3; // Paper - OBFHELPER - if (i > 0) { - BlockPos blockPosx = pos.relative(direction, i); + if (receiverPos > 0) { + BlockPos testPosx = pos.relative(direction, receiverPos); + // Paper start - Call BlockRedstoneEvent -+ if (wasPowered != willBePowered) { -+ int newCurrent = willBePowered ? 15 : 0; ++ if (wasPowered != powered) { ++ int newCurrent = powered ? 15 : 0; + org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent( -+ org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPosx), wasPowered ? 15 : 0, newCurrent ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, testPosx), wasPowered ? 15 : 0, newCurrent + ); + event.callEvent(); + cancelledReceiverHook = event.getNewCurrent() != newCurrent; @@ -33,15 +32,18 @@ + if (!cancelledReceiverHook) { // always trigger two events even when the first hook current change is cancelled + // Paper end - Call BlockRedstoneEvent Direction opposite = direction.getOpposite(); - level.setBlock(blockPosx, blockState1.setValue(FACING, opposite), Block.UPDATE_ALL); - notifyNeighbors(block, level, blockPosx, opposite); - emitState(level, blockPosx, flag2, flag3, flag, flag1); + level.setBlock(testPosx, newState.setValue(FACING, opposite), Block.UPDATE_ALL); + notifyNeighbors(block, level, testPosx, opposite); +@@ -169,15 +_,29 @@ + } + + emitState(level, testPosx, attached, powered, wasAttached, wasPowered); - } + } // Paper - Call BlockRedstoneEvent + } + // Paper start - Call BlockRedstoneEvent -+ if (wasPowered != willBePowered) { -+ int newCurrent = willBePowered ? 15 : 0; ++ if (wasPowered != powered) { ++ int newCurrent = powered ? 15 : 0; + org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent( + org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), wasPowered ? 15 : 0, newCurrent + ); @@ -51,24 +53,24 @@ + // Paper end - Call BlockRedstoneEvent + if (!cancelledEmitterHook) { // Paper - Call BlockRedstoneEvent - emitState(level, pos, flag2, flag3, flag, flag1); - if (!attaching) { + emitState(level, pos, attached, powered, wasAttached, wasPowered); + if (!isBeingDestroyed) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.skipTripwireHookPlacementValidation || level.getBlockState(pos).is(Blocks.TRIPWIRE_HOOK)) // Paper - Validate tripwire hook placement before update - level.setBlock(pos, blockState1.setValue(FACING, direction), Block.UPDATE_ALL); - if (shouldNotifyNeighbours) { + level.setBlock(pos, newState.setValue(FACING, direction), Block.UPDATE_ALL); + if (canUpdate) { notifyNeighbors(block, level, pos, direction); } } + } // Paper - Call BlockRedstoneEvent - if (flag != flag2) { - for (int i2 = 1; i2 < i; i2++) { -@@ -174,7 +_,7 @@ - if (blockState2 != null) { - BlockState blockState3 = level.getBlockState(blockPos1); - if (blockState3.is(Blocks.TRIPWIRE) || blockState3.is(Blocks.TRIPWIRE_HOOK)) { -- level.setBlock(blockPos1, blockState2.trySetValue(ATTACHED, flag2), Block.UPDATE_ALL); -+ if (!io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates || !blockState3.is(Blocks.TRIPWIRE)) level.setBlock(blockPos1, blockState2.trySetValue(ATTACHED, flag2), Block.UPDATE_ALL); // Paper - prevent tripwire from updating + if (wasAttached != attached) { + for (int i = 1; i < receiverPos; i++) { +@@ -186,7 +_,7 @@ + if (wireData != null) { + BlockState testPosState = level.getBlockState(testPosx); + if (testPosState.is(Blocks.TRIPWIRE) || testPosState.is(Blocks.TRIPWIRE_HOOK)) { +- level.setBlock(testPosx, wireData.trySetValue(ATTACHED, attached), Block.UPDATE_ALL); ++ if (!io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates || !testPosState.is(Blocks.TRIPWIRE)) level.setBlock(testPosx, wireData.trySetValue(ATTACHED, attached), Block.UPDATE_ALL); // Paper - prevent tripwire from updating } } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/TurtleEggBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/TurtleEggBlock.java.patch index 88d48aac5312..49ba7862869d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/TurtleEggBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/TurtleEggBlock.java.patch @@ -3,7 +3,7 @@ @@ -75,6 +_,19 @@ && level instanceof ServerLevel serverLevel && this.canDestroyEgg(serverLevel, entity) - && level.random.nextInt(chance) == 0) { + && level.getRandom().nextInt(randomness) == 0) { + // CraftBukkit start - Step on eggs + org.bukkit.event.Cancellable cancellable; + if (entity instanceof Player) { @@ -22,15 +22,15 @@ } @@ -96,10 +_,19 @@ if (this.shouldUpdateHatchLevel(level, pos) && onSand(level, pos)) { - int hatchValue = state.getValue(HATCH); - if (hatchValue < 2) { + int hatch = state.getValue(HATCH); + if (hatch < 2) { + // CraftBukkit start - Call BlockGrowEvent -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state.setValue(HATCH, hatchValue + 1), Block.UPDATE_CLIENTS)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state.setValue(HATCH, hatch + 1), Block.UPDATE_CLIENTS)) { + return; + } + // CraftBukkit end level.playSound(null, pos, SoundEvents.TURTLE_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.9F + random.nextFloat() * 0.2F); -- level.setBlock(pos, state.setValue(HATCH, hatchValue + 1), Block.UPDATE_CLIENTS); +- level.setBlock(pos, state.setValue(HATCH, hatch + 1), Block.UPDATE_CLIENTS); level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(state)); } else { + // CraftBukkit start - Call BlockFadeEvent @@ -50,14 +50,14 @@ } } } -@@ -139,8 +_,8 @@ - } - - @Override -- public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack stack) { -- super.playerDestroy(level, player, pos, state, blockEntity, stack); -+ public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack stack, boolean includeDrops, boolean dropExp) { // Paper - fix drops not preventing stats/food exhaustion -+ super.playerDestroy(level, player, pos, state, blockEntity, stack, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion +@@ -146,8 +_,9 @@ + final BlockState state, + final @Nullable BlockEntity blockEntity, + final ItemStack destroyedWith ++ , boolean includeDrops, boolean dropExp // Paper - fix drops not preventing stats/food exhaustion + ) { +- super.playerDestroy(level, player, pos, state, blockEntity, destroyedWith); ++ super.playerDestroy(level, player, pos, state, blockEntity, destroyedWith, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion this.decreaseEggs(level, pos, state); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/VegetationBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/VegetationBlock.java.patch index cc5f85a5a349..c15b56a485d2 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/VegetationBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/VegetationBlock.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/level/block/VegetationBlock.java +++ b/net/minecraft/world/level/block/VegetationBlock.java @@ -35,9 +_,15 @@ - BlockState neighborState, - RandomSource random + final BlockState neighbourState, + final RandomSource random ) { - return !state.canSurvive(level, pos) - ? Blocks.AIR.defaultBlockState() -- : super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); +- : super.updateShape(state, level, ticks, pos, directionToNeighbour, neighbourPos, neighbourState, random); + // CraftBukkit start + if (!state.canSurvive(level, pos)) { + // Suppress during worldgen @@ -14,7 +14,7 @@ + return Blocks.AIR.defaultBlockState(); + } + } -+ return super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); ++ return super.updateShape(state, level, ticks, pos, directionToNeighbour, neighbourPos, neighbourState, random); + // CraftBukkit end } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/VineBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/VineBlock.java.patch index 653121c288a3..104fc06cc92d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/VineBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/VineBlock.java.patch @@ -2,67 +2,69 @@ +++ b/net/minecraft/world/level/block/VineBlock.java @@ -166,7 +_,7 @@ @Override - protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { if (level.getGameRules().get(GameRules.SPREAD_VINES)) { - if (random.nextInt(4) == 0) { + if (random.nextFloat() < (level.spigotConfig.vineModifier / (100.0F * 4))) { // Spigot - SPIGOT-7159: Better modifier resolution - Direction random1 = Direction.getRandom(random); - BlockPos blockPos = pos.above(); - if (random1.getAxis().isHorizontal() && !state.getValue(getPropertyForFace(random1))) { -@@ -180,28 +_,31 @@ - boolean value1 = state.getValue(getPropertyForFace(counterClockWise)); - BlockPos blockPos2 = blockPos1.relative(clockWise); - BlockPos blockPos3 = blockPos1.relative(counterClockWise); + Direction testDirection = Direction.getRandom(random); + BlockPos abovePos = pos.above(); + if (testDirection.getAxis().isHorizontal() && !state.getValue(getPropertyForFace(testDirection))) { +@@ -180,30 +_,33 @@ + boolean ccwHasConnectingFace = state.getValue(getPropertyForFace(ccwDirection)); + BlockPos cwTestPos = testPos.relative(cwDirection); + BlockPos ccwTestPos = testPos.relative(ccwDirection); + // CraftBukkit start - Call BlockSpreadEvent + BlockPos source = pos; - if (value && isAcceptableNeighbour(level, blockPos2, clockWise)) { -- level.setBlock(blockPos1, this.defaultBlockState().setValue(getPropertyForFace(clockWise), true), Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, blockPos1, this.defaultBlockState().setValue(getPropertyForFace(clockWise), true), Block.UPDATE_CLIENTS); - } else if (value1 && isAcceptableNeighbour(level, blockPos3, counterClockWise)) { -- level.setBlock(blockPos1, this.defaultBlockState().setValue(getPropertyForFace(counterClockWise), true), Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, blockPos1, this.defaultBlockState().setValue(getPropertyForFace(counterClockWise), true), Block.UPDATE_CLIENTS); + if (cwHasConnectingFace && isAcceptableNeighbour(level, cwTestPos, cwDirection)) { +- level.setBlock(testPos, this.defaultBlockState().setValue(getPropertyForFace(cwDirection), true), Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, testPos, this.defaultBlockState().setValue(getPropertyForFace(cwDirection), true), Block.UPDATE_CLIENTS); + } else if (ccwHasConnectingFace && isAcceptableNeighbour(level, ccwTestPos, ccwDirection)) { +- level.setBlock(testPos, this.defaultBlockState().setValue(getPropertyForFace(ccwDirection), true), Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, testPos, this.defaultBlockState().setValue(getPropertyForFace(ccwDirection), true), Block.UPDATE_CLIENTS); } else { - Direction opposite = random1.getOpposite(); - if (value && level.isEmptyBlock(blockPos2) && isAcceptableNeighbour(level, pos.relative(clockWise), opposite)) { -- level.setBlock(blockPos2, this.defaultBlockState().setValue(getPropertyForFace(opposite), true), Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, blockPos2, this.defaultBlockState().setValue(getPropertyForFace(opposite), true), Block.UPDATE_CLIENTS); - } else if (value1 && level.isEmptyBlock(blockPos3) && isAcceptableNeighbour(level, pos.relative(counterClockWise), opposite)) { -- level.setBlock(blockPos3, this.defaultBlockState().setValue(getPropertyForFace(opposite), true), Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, blockPos3, this.defaultBlockState().setValue(getPropertyForFace(opposite), true), Block.UPDATE_CLIENTS); - } else if (random.nextFloat() < 0.05 && isAcceptableNeighbour(level, blockPos1.above(), Direction.UP)) { -- level.setBlock(blockPos1, this.defaultBlockState().setValue(UP, true), Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, blockPos1, this.defaultBlockState().setValue(UP, true), Block.UPDATE_CLIENTS); + Direction opposite = testDirection.getOpposite(); + if (cwHasConnectingFace && level.isEmptyBlock(cwTestPos) && isAcceptableNeighbour(level, pos.relative(cwDirection), opposite)) { +- level.setBlock(cwTestPos, this.defaultBlockState().setValue(getPropertyForFace(opposite), true), Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, cwTestPos, this.defaultBlockState().setValue(getPropertyForFace(opposite), true), Block.UPDATE_CLIENTS); + } else if (ccwHasConnectingFace + && level.isEmptyBlock(ccwTestPos) + && isAcceptableNeighbour(level, pos.relative(ccwDirection), opposite)) { +- level.setBlock(ccwTestPos, this.defaultBlockState().setValue(getPropertyForFace(opposite), true), Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, ccwTestPos, this.defaultBlockState().setValue(getPropertyForFace(opposite), true), Block.UPDATE_CLIENTS); + } else if (random.nextFloat() < 0.05 && isAcceptableNeighbour(level, testPos.above(), Direction.UP)) { +- level.setBlock(testPos, this.defaultBlockState().setValue(UP, true), Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, source, testPos, this.defaultBlockState().setValue(UP, true), Block.UPDATE_CLIENTS); } + // CraftBukkit end } - } else if (isAcceptableNeighbour(level, blockPos1, random1)) { -- level.setBlock(pos, state.setValue(getPropertyForFace(random1), true), Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, (BlockState) state.setValue(VineBlock.getPropertyForFace(random1), true), Block.UPDATE_CLIENTS); // CraftBukkit + } else if (isAcceptableNeighbour(level, testPos, testDirection)) { +- level.setBlock(pos, state.setValue(getPropertyForFace(testDirection), true), Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state.setValue(VineBlock.getPropertyForFace(testDirection), true), Block.UPDATE_CLIENTS); // CraftBukkit } } } else { - if (random1 == Direction.UP && pos.getY() < level.getMaxY()) { - if (this.canSupportAtFace(level, pos, random1)) { + if (testDirection == Direction.UP && pos.getY() < level.getMaxY()) { + if (this.canSupportAtFace(level, pos, testDirection)) { - level.setBlock(pos, state.setValue(UP, true), Block.UPDATE_CLIENTS); + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(level, pos, state.setValue(UP, true), Block.UPDATE_CLIENTS); // CraftBukkit return; } -@@ -219,7 +_,7 @@ +@@ -221,7 +_,7 @@ } - if (this.hasHorizontalConnection(blockState1)) { -- level.setBlock(blockPos, blockState1, Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos, blockState1, Block.UPDATE_CLIENTS); // CraftBukkit + if (this.hasHorizontalConnection(aboveState)) { +- level.setBlock(abovePos, aboveState, Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, abovePos, aboveState, Block.UPDATE_CLIENTS); // CraftBukkit } return; -@@ -233,7 +_,7 @@ - BlockState blockState2 = blockState.isAir() ? this.defaultBlockState() : blockState; - BlockState blockState3 = this.copyRandomFaces(state, blockState2, random); - if (blockState2 != blockState3 && this.hasHorizontalConnection(blockState3)) { -- level.setBlock(blockPos1, blockState3, Block.UPDATE_CLIENTS); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, blockPos1, blockState3, Block.UPDATE_CLIENTS); // CraftBukkit +@@ -235,7 +_,7 @@ + BlockState before = belowState.isAir() ? this.defaultBlockState() : belowState; + BlockState after = this.copyRandomFaces(state, before, random); + if (before != after && this.hasHorizontalConnection(after)) { +- level.setBlock(belowPos, after, Block.UPDATE_CLIENTS); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, belowPos, after, Block.UPDATE_CLIENTS); // CraftBukkit } } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/WallHangingSignBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/WallHangingSignBlock.java.patch index b3a0e5ec97b3..b44dd55e2012 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/WallHangingSignBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/WallHangingSignBlock.java.patch @@ -1,10 +1,11 @@ --- a/net/minecraft/world/level/block/WallHangingSignBlock.java +++ b/net/minecraft/world/level/block/WallHangingSignBlock.java -@@ -174,6 +_,6 @@ +@@ -181,7 +_,7 @@ @Override - public @Nullable BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { -- return createTickerHelper(blockEntityType, BlockEntityType.HANGING_SIGN, SignBlockEntity::tick); + public @Nullable BlockEntityTicker getTicker(final Level level, final BlockState blockState, final BlockEntityType type) { +- return createTickerHelper(type, BlockEntityType.HANGING_SIGN, SignBlockEntity::tick); + return null; // CraftBukkit - remove unnecessary sign ticking } - } + + @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/WeatheringCopperGolemStatueBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/WeatheringCopperGolemStatueBlock.java.patch index ede28b248b61..a6ae841b3849 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/WeatheringCopperGolemStatueBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/WeatheringCopperGolemStatueBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/WeatheringCopperGolemStatueBlock.java +++ b/net/minecraft/world/level/block/WeatheringCopperGolemStatueBlock.java -@@ -58,16 +_,21 @@ +@@ -64,16 +_,21 @@ return InteractionResult.PASS; } @@ -11,7 +11,7 @@ if (this.getAge().equals(WeatheringCopper.WeatherState.UNAFFECTED)) { CopperGolem copperGolem = copperGolemStatueBlockEntity.removeStatue(state); -- stack.hurtAndBreak(1, player, hand.asEquipmentSlot()); +- itemStack.hurtAndBreak(1, player, hand.asEquipmentSlot()); if (copperGolem != null) { - level.addFreshEntity(copperGolem); - level.removeBlock(pos, false); @@ -20,7 +20,7 @@ + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, newState)) { + return InteractionResult.PASS; + } -+ stack.hurtAndBreak(1, player, hand.asEquipmentSlot()); // Paper - moved after event ++ itemStack.hurtAndBreak(1, player, hand.asEquipmentSlot()); // Paper - moved after event + level.addFreshEntity(copperGolem, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.REANIMATE); // Paper - add SpawnReason + level.setBlock(pos, newState, Block.UPDATE_ALL); + // Paper end - call EntityChangeBlockEvent and spawnReason diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/WebBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/WebBlock.java.patch index 269b2202f046..0cceaf579007 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/WebBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/WebBlock.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/world/level/block/WebBlock.java +++ b/net/minecraft/world/level/block/WebBlock.java -@@ -25,6 +_,7 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -32,6 +_,7 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent - Vec3 vec3 = new Vec3(0.25, 0.05F, 0.25); + Vec3 speedMultiplier = new Vec3(0.25, 0.05F, 0.25); if (entity instanceof LivingEntity livingEntity && livingEntity.hasEffect(MobEffects.WEAVING)) { - vec3 = new Vec3(0.5, 0.25, 0.5); + speedMultiplier = new Vec3(0.5, 0.25, 0.5); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/WeightedPressurePlateBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/WeightedPressurePlateBlock.java.patch index 07cf136cbf0a..323d70dfe997 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/WeightedPressurePlateBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/WeightedPressurePlateBlock.java.patch @@ -3,11 +3,11 @@ @@ -39,7 +_,25 @@ @Override - protected int getSignalStrength(Level level, BlockPos pos) { -- int min = Math.min(getEntityCount(level, TOUCH_AABB.move(pos), Entity.class), this.maxWeight); + protected int getSignalStrength(final Level level, final BlockPos pos) { +- int count = Math.min(getEntityCount(level, TOUCH_AABB.move(pos), Entity.class), this.maxWeight); + // CraftBukkit start + // int min = Math.min(getEntityCount(level, TOUCH_AABB.move(pos), Entity.class), this.maxWeight); -+ int min = 0; ++ int count = 0; + for (Entity entity : getEntities(level, TOUCH_AABB.move(pos), Entity.class)) { + org.bukkit.event.Cancellable cancellable; + @@ -20,10 +20,10 @@ + + // We only want to block turning the plate on if all events are cancelled + if (!cancellable.isCancelled()) { -+ min++; ++ count++; + } + } + // CraftBukkit end - if (min > 0) { - float f = (float)Math.min(this.maxWeight, min) / this.maxWeight; - return Mth.ceil(f * 15.0F); + if (count > 0) { + float percent = (float)Math.min(this.maxWeight, count) / this.maxWeight; + return Mth.ceil(percent * 15.0F); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/WitherRoseBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/WitherRoseBlock.java.patch index 06ebeb800603..681137eaa139 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/WitherRoseBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/WitherRoseBlock.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/WitherRoseBlock.java +++ b/net/minecraft/world/level/block/WitherRoseBlock.java -@@ -64,11 +_,12 @@ - - @Override - protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean pastEdges) { +@@ -72,11 +_,12 @@ + final InsideBlockEffectApplier effectApplier, + final boolean isPrecise + ) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent if (level instanceof ServerLevel serverLevel && level.getDifficulty() != Difficulty.PEACEFUL diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/WitherSkullBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/WitherSkullBlock.java.patch index a1496c38cf0b..5aa9ac611b52 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/WitherSkullBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/WitherSkullBlock.java.patch @@ -3,37 +3,37 @@ @@ -49,6 +_,7 @@ } - public static void checkSpawn(Level level, BlockPos pos, SkullBlockEntity blockEntity) { + public static void checkSpawn(final Level level, final BlockPos pos, final SkullBlockEntity placedSkull) { + if (level.captureBlockStates) return; // CraftBukkit if (!level.isClientSide()) { - BlockState blockState = blockEntity.getBlockState(); - boolean flag = blockState.is(Blocks.WITHER_SKELETON_SKULL) || blockState.is(Blocks.WITHER_SKELETON_WALL_SKULL); + BlockState blockState = placedSkull.getBlockState(); + boolean correctBlock = blockState.is(Blocks.WITHER_SKELETON_SKULL) || blockState.is(Blocks.WITHER_SKELETON_WALL_SKULL); @@ -57,7 +_,7 @@ - if (blockPatternMatch != null) { + if (match != null) { WitherBoss witherBoss = EntityType.WITHER.create(level, EntitySpawnReason.TRIGGERED); if (witherBoss != null) { -- CarvedPumpkinBlock.clearPatternBlocks(level, blockPatternMatch); -+ // CarvedPumpkinBlock.clearPatternBlocks(level, blockPatternMatch); // CraftBukkit - move down - BlockPos pos1 = blockPatternMatch.getBlock(1, 2, 0).getPos(); +- CarvedPumpkinBlock.clearPatternBlocks(level, match); ++ // CarvedPumpkinBlock.clearPatternBlocks(level, match); // CraftBukkit - move down + BlockPos spawnPos = match.getBlock(1, 2, 0).getPos(); witherBoss.snapTo( - pos1.getX() + 0.5, + spawnPos.getX() + 0.5, @@ -68,12 +_,18 @@ ); - witherBoss.yBodyRot = blockPatternMatch.getForwards().getAxis() == Direction.Axis.X ? 0.0F : 90.0F; + witherBoss.yBodyRot = match.getForwards().getAxis() == Direction.Axis.X ? 0.0F : 90.0F; witherBoss.makeInvulnerable(); + // CraftBukkit start + if (!level.addFreshEntity(witherBoss, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BUILD_WITHER)) { + return; + } -+ CarvedPumpkinBlock.clearPatternBlocks(level, blockPatternMatch); // CraftBukkit - from above ++ CarvedPumpkinBlock.clearPatternBlocks(level, match); // CraftBukkit - from above + // CraftBukkit end - for (ServerPlayer serverPlayer : level.getEntitiesOfClass(ServerPlayer.class, witherBoss.getBoundingBox().inflate(50.0))) { - CriteriaTriggers.SUMMONED_ENTITY.trigger(serverPlayer, witherBoss); + for (ServerPlayer player : level.getEntitiesOfClass(ServerPlayer.class, witherBoss.getBoundingBox().inflate(50.0))) { + CriteriaTriggers.SUMMONED_ENTITY.trigger(player, witherBoss); } - level.addFreshEntity(witherBoss); + // level.addFreshEntity(witherBoss); // CraftBukkit - moved up - CarvedPumpkinBlock.updatePatternBlocks(level, blockPatternMatch); + CarvedPumpkinBlock.updatePatternBlocks(level, match); } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch index f068f0f98742..6157f3afddab 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch @@ -1,14 +1,16 @@ --- a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +++ b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java -@@ -105,11 +_,49 @@ +@@ -109,13 +_,51 @@ }; public final Reference2IntOpenHashMap>> recipesUsed = new Reference2IntOpenHashMap<>(); private final RecipeManager.CachedCheck quickCheck; + public final RecipeType recipeType; // Paper - cook speed multiplier API + public double cookSpeedMultiplier = 1.0; // Paper - cook speed multiplier API - protected AbstractFurnaceBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState, RecipeType recipeType) { - super(type, pos, blockState); + protected AbstractFurnaceBlockEntity( + final BlockEntityType type, final BlockPos worldPosition, final BlockState blockState, final RecipeType recipeType + ) { + super(type, worldPosition, blockState); this.quickCheck = RecipeManager.createCheck(recipeType); - } + this.recipeType = recipeType; // Paper - cook speed multiplier API @@ -49,9 +51,9 @@ + } + // CraftBukkit end - private boolean isLit() { - return this.litTimeRemaining > 0; -@@ -126,6 +_,7 @@ + @Override + protected void loadAdditional(final ValueInput input) { +@@ -128,6 +_,7 @@ this.litTotalTime = input.getShortOr("lit_total_time", (short)0); this.recipesUsed.clear(); this.recipesUsed.putAll(input.read("RecipesUsed", RECIPES_USED_CODEC).orElse(Map.of())); @@ -59,7 +61,7 @@ } @Override -@@ -135,6 +_,7 @@ +@@ -137,6 +_,7 @@ output.putShort("cooking_total_time", (short)this.cookingTotalTime); output.putShort("lit_time_remaining", (short)this.litTimeRemaining); output.putShort("lit_total_time", (short)this.litTotalTime); @@ -67,216 +69,208 @@ ContainerHelper.saveAllItems(output, this.items); output.store("RecipesUsed", RECIPES_USED_CODEC, this.recipesUsed); } -@@ -161,11 +_,22 @@ - - int maxStackSize = furnace.getMaxStackSize(); - if (!furnace.isLit() && canBurn(level.registryAccess(), recipeHolder, singleRecipeInput, furnace.items, maxStackSize)) { -- furnace.litTimeRemaining = furnace.getBurnDuration(level.fuelValues(), itemStack); -+ // CraftBukkit start -+ org.bukkit.craftbukkit.inventory.CraftItemStack fuel = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); -+ -+ org.bukkit.event.inventory.FurnaceBurnEvent furnaceBurnEvent = new org.bukkit.event.inventory.FurnaceBurnEvent( -+ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), -+ fuel, -+ furnace.getBurnDuration(level.fuelValues(), itemStack) -+ ); -+ if (!furnaceBurnEvent.callEvent()) return; -+ // CraftBukkit end -+ -+ furnace.litTimeRemaining = furnaceBurnEvent.getBurnTime(); // CraftBukkit - respect event output - furnace.litTotalTime = furnace.litTimeRemaining; -- if (furnace.isLit()) { -+ if (furnace.isLit() && furnaceBurnEvent.isBurning()) { // CraftBukkit - respect event output - flag = true; -- if (flag2) { -+ if (flag2 && furnaceBurnEvent.willConsumeFuel()) { // Paper - add consumeFuel to FurnaceBurnEvent - Item item = itemStack.getItem(); - itemStack.shrink(1); - if (itemStack.isEmpty()) { -@@ -176,11 +_,28 @@ - } +@@ -166,24 +_,48 @@ + int maxStackSize = entity.getMaxStackSize(); + ItemStack burnResult = recipe.value().assemble(input); + if (!burnResult.isEmpty() && canBurn(entity.items, maxStackSize, burnResult)) { ++ // CraftBukkit start ++ org.bukkit.event.inventory.FurnaceBurnEvent furnaceBurnEvent = new org.bukkit.event.inventory.FurnaceBurnEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), ++ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(fuel), ++ entity.getBurnDuration(level.fuelValues(), fuel) ++ ); ++ if (!furnaceBurnEvent.callEvent()) return; ++ // CraftBukkit end + if (!isLit) { +- int newLitTime = entity.getBurnDuration(level.fuelValues(), fuel); ++ int newLitTime = furnaceBurnEvent.getBurnTime(); // CraftBukkit - respect event output + entity.litTimeRemaining = newLitTime; + entity.litTotalTime = newLitTime; +- if (newLitTime > 0) { ++ if (newLitTime > 0 && furnaceBurnEvent.isBurning()) { // CraftBukkit - respect event output // TODO - snapshot - double check surrounding diff post squash ++ if (furnaceBurnEvent.willConsumeFuel()) { // Paper - add consumeFuel to FurnaceBurnEvent + consumeFuel(entity.items, fuel); ++ } // Paper - add consumeFuel to FurnaceBurnEvent + isLit = true; + changed = true; + } + } - if (furnace.isLit() && canBurn(level.registryAccess(), recipeHolder, singleRecipeInput, furnace.items, maxStackSize)) { -+ // CraftBukkit start -+ if (recipeHolder != null && furnace.cookingTimer == 0) { -+ org.bukkit.craftbukkit.inventory.CraftItemStack source = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(furnace.items.get(0)); -+ org.bukkit.inventory.CookingRecipe recipe = (org.bukkit.inventory.CookingRecipe) recipeHolder.toBukkitRecipe(); -+ -+ org.bukkit.event.inventory.FurnaceStartSmeltEvent event = new org.bukkit.event.inventory.FurnaceStartSmeltEvent( -+ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), -+ source, -+ recipe, -+ getTotalCookTime(level, furnace, furnace.recipeType, furnace.cookSpeedMultiplier) // Paper - cook speed multiplier API -+ ); -+ event.callEvent(); + if (isLit) { ++ // CraftBukkit start ++ if (entity.cookingTimer == 0) { ++ org.bukkit.event.inventory.FurnaceStartSmeltEvent event = new org.bukkit.event.inventory.FurnaceStartSmeltEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), ++ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(ingredient), ++ (org.bukkit.inventory.CookingRecipe) recipe.toBukkitRecipe(), ++ getTotalCookTime(level, entity, entity.recipeType, entity.cookSpeedMultiplier) // Paper - cook speed multiplier API ++ ); ++ event.callEvent(); + -+ furnace.cookingTotalTime = event.getTotalCookTime(); -+ } -+ // CraftBukkit end -+ - furnace.cookingTimer++; -- if (furnace.cookingTimer == furnace.cookingTotalTime) { -+ if (furnace.cookingTimer >= furnace.cookingTotalTime) { // Paper - cook speed multiplier API - furnace.cookingTimer = 0; -- furnace.cookingTotalTime = getTotalCookTime(level, furnace); -- if (burn(level.registryAccess(), recipeHolder, singleRecipeInput, furnace.items, maxStackSize)) { -+ furnace.cookingTotalTime = getTotalCookTime(level, furnace, furnace.recipeType, furnace.cookSpeedMultiplier); // Paper - cook speed multiplier API -+ if (burn(level.registryAccess(), recipeHolder, singleRecipeInput, furnace.items, maxStackSize, level, furnace.worldPosition)) { // CraftBukkit - furnace.setRecipeUsed(recipeHolder); - } ++ entity.cookingTotalTime = event.getTotalCookTime(); ++ } ++ // CraftBukkit end + entity.cookingTimer++; +- if (entity.cookingTimer == entity.cookingTotalTime) { ++ if (entity.cookingTimer >= entity.cookingTotalTime) { // Paper - cook speed multiplier API + entity.cookingTimer = 0; +- entity.cookingTotalTime = recipe.value().cookingTime(); +- burn(entity.items, ingredient, burnResult); ++ entity.cookingTotalTime = getTotalCookTime(level, entity, entity.recipeType, entity.cookSpeedMultiplier); // Paper - cook speed multiplier API ++ if (burn(entity.items, ingredient, burnResult, recipe, level, entity.worldPosition)) { // CraftBukkit - add level & pos // Paper - make burn return a boolean again, add recipe + entity.setRecipeUsed(recipe); ++ } // Paper + changed = true; + } + } else { +@@ -233,28 +_,63 @@ + } + } -@@ -234,17 +_,47 @@ - @Nullable RecipeHolder recipe, - SingleRecipeInput recipeInput, - NonNullList items, -- int maxStackSize -+ int maxStackSize, -+ net.minecraft.world.level.Level level, // CraftBukkit -+ BlockPos blockPos // CraftBukkit - ) { - if (recipe != null && canBurn(registryAccess, recipe, recipeInput, items, maxStackSize)) { -- ItemStack itemStack = items.get(0); -- ItemStack itemStack1 = recipe.value().assemble(recipeInput, registryAccess); -- ItemStack itemStack2 = items.get(2); -+ ItemStack itemStack = items.get(0); final ItemStack ingredient = itemStack; // Paper - OBFHELPER -+ ItemStack itemStack1 = recipe.value().assemble(recipeInput, registryAccess); ItemStack result = itemStack1; // Paper - OBFHELPER -+ ItemStack itemStack2 = items.get(2); final ItemStack existingResults = itemStack2; // Paper - OBFHELPER -+ // CraftBukkit start - fire FurnaceSmeltEvent -+ org.bukkit.craftbukkit.inventory.CraftItemStack apiIngredient = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(ingredient); -+ org.bukkit.inventory.ItemStack apiResult = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(result); +- private static void burn(final NonNullList items, final ItemStack inputItemStack, final ItemStack result) { ++ private static boolean burn(final NonNullList items, final ItemStack inputItemStack, ItemStack result, RecipeHolder recipe, final net.minecraft.world.level.Level level, final BlockPos blockPos) { // CraftBukkit + ItemStack resultItemStack = items.get(2); ++ // CraftBukkit start - fire FurnaceSmeltEvent ++ org.bukkit.craftbukkit.inventory.CraftItemStack apiIngredient = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(inputItemStack); ++ org.bukkit.inventory.ItemStack apiResult = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(result); + -+ org.bukkit.event.inventory.FurnaceSmeltEvent furnaceSmeltEvent = new org.bukkit.event.inventory.FurnaceSmeltEvent( -+ org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos), -+ apiIngredient, -+ apiResult, -+ (org.bukkit.inventory.CookingRecipe) recipe.toBukkitRecipe() // Paper - Add recipe to cook events -+ ); -+ if (!furnaceSmeltEvent.callEvent()) return false; ++ org.bukkit.event.inventory.FurnaceSmeltEvent furnaceSmeltEvent = new org.bukkit.event.inventory.FurnaceSmeltEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos), ++ apiIngredient, ++ apiResult, ++ (org.bukkit.inventory.CookingRecipe) recipe.toBukkitRecipe() // Paper - Add recipe to cook events ++ ); ++ if (!furnaceSmeltEvent.callEvent()) return false; + -+ apiResult = furnaceSmeltEvent.getResult(); -+ itemStack1 = result = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(apiResult); ++ apiResult = furnaceSmeltEvent.getResult(); ++ result = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(apiResult); + -+ if (!result.isEmpty()) { -+ if (existingResults.isEmpty()) { -+ items.set(SLOT_RESULT, result.copy()); -+ } else if (org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(existingResults).isSimilar(apiResult)) { -+ existingResults.grow(result.getCount()); -+ } else { -+ return false; -+ } ++ if (!result.isEmpty()) { ++ if (resultItemStack.isEmpty()) { ++ items.set(SLOT_RESULT, result.copy()); ++ } else if (org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(resultItemStack).isSimilar(apiResult)) { ++ resultItemStack.grow(result.getCount()); ++ } else { ++ return false; + } -+ -+ /* - if (itemStack2.isEmpty()) { - items.set(2, itemStack1.copy()); - } else if (ItemStack.isSameItemSameComponents(itemStack2, itemStack1)) { - itemStack2.grow(1); - } -+ */ -+ // CraftBukkit end ++ } ++ /* + if (resultItemStack.isEmpty()) { + items.set(2, result.copy()); + } else { + resultItemStack.grow(result.getCount()); + } ++ */ ++ // CraftBukkit end - if (itemStack.is(Blocks.WET_SPONGE.asItem()) && !items.get(1).isEmpty() && items.get(1).is(Items.BUCKET)) { - items.set(1, new ItemStack(Items.WATER_BUCKET)); -@@ -261,9 +_,16 @@ - return fuelValues.burnDuration(stack); + if (inputItemStack.is(Items.WET_SPONGE) && !items.get(1).isEmpty() && items.get(1).is(Items.BUCKET)) { + items.set(1, new ItemStack(Items.WATER_BUCKET)); + } + + inputItemStack.shrink(1); ++ return true; // Paper - make burn return a boolean again + } + + protected int getBurnDuration(final FuelValues fuelValues, final ItemStack itemStack) { + return fuelValues.burnDuration(itemStack); } -- public static int getTotalCookTime(ServerLevel level, AbstractFurnaceBlockEntity furnace) { -+ public static int getTotalCookTime(@Nullable ServerLevel level, AbstractFurnaceBlockEntity furnace, RecipeType recipeType, double cookSpeedMultiplier) { // Paper - cook speed multiplier API - SingleRecipeInput singleRecipeInput = new SingleRecipeInput(furnace.getItem(0)); -- return furnace.quickCheck.getRecipeFor(singleRecipeInput, level).map(recipeHolder -> recipeHolder.value().cookingTime()).orElse(200); +- public static int getTotalCookTime(final ServerLevel level, final AbstractFurnaceBlockEntity entity) { ++ public static int getTotalCookTime(final @Nullable ServerLevel level, final AbstractFurnaceBlockEntity entity, final RecipeType recipeType, final double cookSpeedMultiplier) { // Paper - cook speed multiplier API + SingleRecipeInput input = new SingleRecipeInput(entity.getItem(0)); +- return entity.quickCheck.getRecipeFor(input, level).map(recipeHolder -> recipeHolder.value().cookingTime()).orElse(200); + // Paper start - cook speed multiplier API + /* Scale the recipe's cooking time to the current cookSpeedMultiplier */ + int cookTime = level != null -+ ? furnace.quickCheck.getRecipeFor(singleRecipeInput, level).map(recipeHolder -> recipeHolder.value().cookingTime()).orElse(BURN_TIME_STANDARD) ++ ? entity.quickCheck.getRecipeFor(input, level).map(recipeHolder -> recipeHolder.value().cookingTime()).orElse(BURN_TIME_STANDARD) + /* passing a null level here is safe. world is only used for map extending recipes which won't happen here */ -+ : (net.minecraft.server.MinecraftServer.getServer().getRecipeManager().getRecipeFor(recipeType, singleRecipeInput, level).map(recipeHolder -> recipeHolder.value().cookingTime()).orElse(BURN_TIME_STANDARD)); ++ : (net.minecraft.server.MinecraftServer.getServer().getRecipeManager().getRecipeFor(recipeType, input, level).map(recipeHolder -> recipeHolder.value().cookingTime()).orElse(BURN_TIME_STANDARD)); + return (int) Math.ceil (cookTime / cookSpeedMultiplier); + // Paper end - cook speed multiplier API } @Override -@@ -307,7 +_,7 @@ - this.items.set(index, stack); - stack.limitSize(this.getMaxStackSize(stack)); - if (index == 0 && !flag && this.level instanceof ServerLevel serverLevel) { +@@ -298,7 +_,7 @@ + this.items.set(slot, itemStack); + itemStack.limitSize(this.getMaxStackSize(itemStack)); + if (slot == 0 && !same && this.level instanceof ServerLevel serverLevel) { - this.cookingTotalTime = getTotalCookTime(serverLevel, this); + this.cookingTotalTime = getTotalCookTime(serverLevel, this, this.recipeType, this.cookSpeedMultiplier); // Paper - cook speed multiplier API this.cookingTimer = 0; this.setChanged(); } -@@ -342,8 +_,8 @@ - public void awardUsedRecipes(Player player, List items) { +@@ -333,8 +_,8 @@ + public void awardUsedRecipes(final Player player, final List itemStacks) { } -- public void awardUsedRecipesAndPopExperience(ServerPlayer player) { -- List> recipesToAwardAndPopExperience = this.getRecipesToAwardAndPopExperience(player.level(), player.position()); -+ public void awardUsedRecipesAndPopExperience(ServerPlayer player, ItemStack itemstack, int amount) { // CraftBukkit -+ List> recipesToAwardAndPopExperience = this.getRecipesToAwardAndPopExperience(player.level(), player.position(), this.worldPosition, player, itemstack, amount); // CraftBukkit - overload for exp spawn events - player.awardRecipes(recipesToAwardAndPopExperience); +- public void awardUsedRecipesAndPopExperience(final ServerPlayer player) { +- List> recipesToAward = this.getRecipesToAwardAndPopExperience(player.level(), player.position()); ++ public void awardUsedRecipesAndPopExperience(final ServerPlayer player, final ItemStack itemStack, final int amount) { // CraftBukkit ++ List> recipesToAward = this.getRecipesToAwardAndPopExperience(player.level(), player.position(), this.worldPosition, player, itemStack, amount); // CraftBukkit - overload for exp spawn events + player.awardRecipes(recipesToAward); - for (RecipeHolder recipeHolder : recipesToAwardAndPopExperience) { -@@ -354,30 +_,60 @@ + for (RecipeHolder recipe : recipesToAward) { +@@ -345,30 +_,60 @@ } - public List> getRecipesToAwardAndPopExperience(ServerLevel level, Vec3 popVec) { -+ // CraftBukkit start -+ return this.getRecipesToAwardAndPopExperience(level, popVec, this.worldPosition, null, null, 0); + public List> getRecipesToAwardAndPopExperience(final ServerLevel level, final Vec3 position) { ++ // CraftBukkit start ++ return this.getRecipesToAwardAndPopExperience(level, position, this.worldPosition, null, null, 0); + } -+ public List> getRecipesToAwardAndPopExperience(ServerLevel level, Vec3 popVec, BlockPos blockPos, ServerPlayer serverPlayer, ItemStack itemStack, int amount) { -+ // CraftBukkit end - List> list = Lists.newArrayList(); ++ public List> getRecipesToAwardAndPopExperience(final ServerLevel level, final Vec3 position, final BlockPos blockPos, final @Nullable ServerPlayer serverPlayer, final @Nullable ItemStack itemStack, final int amount) { ++ // CraftBukkit end + List> recipesToAward = Lists.newArrayList(); for (Entry>> entry : this.recipesUsed.reference2IntEntrySet()) { - level.recipeAccess().byKey(entry.getKey()).ifPresent(recipeHolder -> { -+ if (!(recipeHolder.value() instanceof AbstractCookingRecipe)) return; // Paper - don't process non-cooking recipes - list.add((RecipeHolder)recipeHolder); -- createExperience(level, popVec, entry.getIntValue(), ((AbstractCookingRecipe)recipeHolder.value()).experience()); -+ createExperience(level, popVec, entry.getIntValue(), ((AbstractCookingRecipe)recipeHolder.value()).experience(), blockPos, serverPlayer, itemStack, amount); // Paper - don't process non-cooking recipes + level.recipeAccess().byKey(entry.getKey()).ifPresent(recipe -> { ++ if (!(recipe.value() instanceof AbstractCookingRecipe)) return; // Paper - don't process non-cooking recipes + recipesToAward.add((RecipeHolder)recipe); +- createExperience(level, position, entry.getIntValue(), ((AbstractCookingRecipe)recipe.value()).experience()); ++ createExperience(level, position, entry.getIntValue(), ((AbstractCookingRecipe)recipe.value()).experience(), blockPos, serverPlayer, itemStack, amount); // Paper - don't process non-cooking recipes }); } - return list; + return recipesToAward; } -- private static void createExperience(ServerLevel level, Vec3 popVec, int recipeIndex, float experience) { -+ private static void createExperience(ServerLevel level, Vec3 popVec, int recipeIndex, float experience, BlockPos blockPos, ServerPlayer serverPlayer, ItemStack itemStack, int amount) { // CraftBukkit - int floor = Mth.floor(recipeIndex * experience); - float fraction = Mth.frac(recipeIndex * experience); - if (fraction != 0.0F && level.random.nextFloat() < fraction) { - floor++; +- private static void createExperience(final ServerLevel level, final Vec3 position, final int amount, final float value) { ++ private static void createExperience(final ServerLevel level, final Vec3 position, final int amount, final float value, final BlockPos blockPos, final ServerPlayer player, final ItemStack itemStack, final int removeCount) { + int xpReward = Mth.floor(amount * value); + float xpFraction = Mth.frac(amount * value); + if (xpFraction != 0.0F && level.getRandom().nextFloat() < xpFraction) { + xpReward++; } -- ExperienceOrb.award(level, popVec, floor); +- ExperienceOrb.award(level, position, xpReward); + // CraftBukkit start - fire FurnaceExtractEvent / BlockExpEvent + org.bukkit.event.block.BlockExpEvent event; -+ if (amount != 0) { ++ if (removeCount != 0) { + event = new org.bukkit.event.inventory.FurnaceExtractEvent( -+ serverPlayer.getBukkitEntity(), ++ player.getBukkitEntity(), + org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos), + itemStack.asBukkitCopy(), -+ amount, -+ floor ++ removeCount, ++ xpReward + ); + } else { + event = new org.bukkit.event.block.BlockExpEvent( + org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos), -+ floor ++ xpReward + ); + } + event.callEvent(); -+ floor = event.getExpToDrop(); ++ xpReward = event.getExpToDrop(); + // CraftBukkit end + -+ ExperienceOrb.awardWithDirection(level, popVec, net.minecraft.world.phys.Vec3.ZERO, floor, org.bukkit.entity.ExperienceOrb.SpawnReason.FURNACE, serverPlayer, null); // Paper ++ ExperienceOrb.awardWithDirection(level, position, net.minecraft.world.phys.Vec3.ZERO, xpReward, org.bukkit.entity.ExperienceOrb.SpawnReason.FURNACE, player, null); // Paper } @Override - public void fillStackedContents(StackedItemContents stackedContents) { + public void fillStackedContents(final StackedItemContents contents) { + // Paper start - don't account fuel stack (fixes MC-243057) -+ stackedContents.accountStack(this.items.get(SLOT_INPUT)); -+ stackedContents.accountStack(this.items.get(SLOT_RESULT)); ++ contents.accountStack(this.items.get(SLOT_INPUT)); ++ contents.accountStack(this.items.get(SLOT_RESULT)); + // Paper end - don't account fuel stack (fixes MC-243057) for (ItemStack itemStack : this.items) { - stackedContents.accountStack(itemStack); + contents.accountStack(itemStack); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch index 6631c94aa4ed..88c69b254024 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/world/level/block/entity/BannerBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BannerBlockEntity.java -@@ -49,7 +_,7 @@ +@@ -48,7 +_,7 @@ @Override - protected void saveAdditional(ValueOutput output) { + protected void saveAdditional(final ValueOutput output) { super.saveAdditional(output); - if (!this.patterns.equals(BannerPatternLayers.EMPTY)) { + if (!this.patterns.equals(BannerPatternLayers.EMPTY) || serialisingForNetwork.get()) { // Paper - always send patterns to client output.store("patterns", BannerPatternLayers.CODEC, this.patterns); } -@@ -60,7 +_,7 @@ - protected void loadAdditional(ValueInput input) { +@@ -59,7 +_,7 @@ + protected void loadAdditional(final ValueInput input) { super.loadAdditional(input); this.name = parseCustomNameSafe(input, "CustomName"); - this.patterns = input.read("patterns", BannerPatternLayers.CODEC).orElse(BannerPatternLayers.EMPTY); @@ -18,14 +18,14 @@ } @Override -@@ -68,9 +_,18 @@ +@@ -67,9 +_,18 @@ return ClientboundBlockEntityDataPacket.create(this); } + // Paper start - always send patterns to client + ThreadLocal serialisingForNetwork = ThreadLocal.withInitial(() -> Boolean.FALSE); @Override - public CompoundTag getUpdateTag(HolderLookup.Provider registries) { + public CompoundTag getUpdateTag(final HolderLookup.Provider registries) { + final Boolean wasSerialisingForNetwork = serialisingForNetwork.get(); + try { + serialisingForNetwork.set(Boolean.TRUE); @@ -37,16 +37,16 @@ } public BannerPatternLayers getPatterns() { -@@ -90,7 +_,7 @@ +@@ -89,7 +_,7 @@ @Override - protected void applyImplicitComponents(DataComponentGetter componentGetter) { - super.applyImplicitComponents(componentGetter); -- this.patterns = componentGetter.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY); -+ this.setPatterns(componentGetter.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY)); // CraftBukkit - apply limits - this.name = componentGetter.get(DataComponents.CUSTOM_NAME); + protected void applyImplicitComponents(final DataComponentGetter components) { + super.applyImplicitComponents(components); +- this.patterns = components.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY); ++ this.setPatterns(components.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY)); // CraftBukkit - apply limits + this.name = components.get(DataComponents.CUSTOM_NAME); } -@@ -106,4 +_,13 @@ +@@ -105,4 +_,13 @@ output.discard("patterns"); output.discard("CustomName"); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch index 262672ce2551..c0cf3395c308 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java -@@ -24,6 +_,40 @@ +@@ -25,6 +_,40 @@ import net.minecraft.world.level.storage.ValueOutput; public class BarrelBlockEntity extends RandomizableContainerBlockEntity { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java.patch index 1c321829b731..d385be38a528 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java.patch @@ -3,20 +3,20 @@ @@ -66,14 +_,19 @@ protected abstract Component getDefaultName(); - public boolean canOpen(Player player) { + public boolean canOpen(final Player player) { - return this.lockKey.canUnlock(player); + return org.bukkit.craftbukkit.event.CraftEventFactory.callBlockLockCheckEvent(this, this.lockKey, this.getDisplayName(), player); // Paper - Call BlockLockCheckEvent } - public static void sendChestLockedNotifications(Vec3 pos, Player player, Component displayName) { + public static void sendChestLockedNotifications(final Vec3 pos, final Player player, final Component displayName) { + // Paper start - BlockLockCheckEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.sendChestLockedNotifications(pos)) { + return; + } + // Paper end - BlockLockCheckEvent Level level = player.level(); -- player.displayClientMessage(Component.translatable("container.isLocked", displayName), true); -+ player.displayClientMessage(Component.translatable("container.isLocked", displayName), true); // Paper - diff on change +- player.sendOverlayMessage(Component.translatable("container.isLocked", displayName)); ++ player.sendOverlayMessage(Component.translatable("container.isLocked", displayName)); // Paper - diff on change if (!level.isClientSide()) { - level.playSound(null, pos.x(), pos.y(), pos.z(), SoundEvents.CHEST_LOCKED, SoundSource.BLOCKS, 1.0F, 1.0F); + level.playSound(null, pos.x(), pos.y(), pos.z(), SoundEvents.CHEST_LOCKED, SoundSource.BLOCKS, 1.0F, 1.0F); // Paper - diff on change diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch index 74e7cf991841..e15d7d61fc34 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/BeaconBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BeaconBlockEntity.java -@@ -107,6 +_,51 @@ +@@ -111,6 +_,51 @@ return 3; } }; @@ -50,38 +50,30 @@ + } + // Paper end - Custom beacon ranges - static @Nullable Holder filterEffect(@Nullable Holder effect) { + private static @Nullable Holder filterEffect(final @Nullable Holder effect) { return VALID_EFFECTS.contains(effect) ? effect : null; -@@ -163,17 +_,26 @@ - blockEntity.lastCheckY++; - } - -- int i = blockEntity.levels; -+ int i = blockEntity.levels; final int originalLevels = i; // Paper - OBFHELPER - if (level.getGameTime() % 80L == 0L) { - if (!blockEntity.beamSections.isEmpty()) { - blockEntity.levels = updateBase(level, x, y, z); +@@ -174,10 +_,19 @@ } - if (blockEntity.levels > 0 && !blockEntity.beamSections.isEmpty()) { -- applyEffects(level, pos, blockEntity.levels, blockEntity.primaryPower, blockEntity.secondaryPower); -+ applyEffects(level, pos, blockEntity.levels, blockEntity.primaryPower, blockEntity.secondaryPower, blockEntity); // Paper - Custom beacon ranges + if (entity.levels > 0 && !entity.beamSections.isEmpty()) { +- applyEffects(level, pos, entity.levels, entity.primaryPower, entity.secondaryPower); ++ applyEffects(level, pos, entity.levels, entity.primaryPower, entity.secondaryPower, entity); // Paper - Custom beacon ranges playSound(level, pos, SoundEvents.BEACON_AMBIENT); } } + // Paper start - beacon activation/deactivation events -+ if (originalLevels <= 0 && blockEntity.levels > 0) { ++ if (previousLevels <= 0 && entity.levels > 0) { + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + new io.papermc.paper.event.block.BeaconActivatedEvent(block).callEvent(); -+ } else if (originalLevels > 0 && blockEntity.levels <= 0) { ++ } else if (previousLevels > 0 && entity.levels <= 0) { + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + new io.papermc.paper.event.block.BeaconDeactivatedEvent(block).callEvent(); + } + // Paper end - beacon activation/deactivation events - if (blockEntity.lastCheckY >= height) { - blockEntity.lastCheckY = level.getMinY() - 1; -@@ -224,35 +_,100 @@ + if (entity.lastCheckY >= lastSetBlock) { + entity.lastCheckY = level.getMinY() - 1; +@@ -228,7 +_,15 @@ @Override public void setRemoved() { @@ -97,46 +89,40 @@ super.setRemoved(); } -+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - pass beacon block entity - private static void applyEffects( - Level level, BlockPos pos, int beaconLevel, @Nullable Holder primaryEffect, @Nullable Holder secondaryEffect +@@ -238,29 +_,78 @@ + final int levels, + final @Nullable Holder primaryPower, + final @Nullable Holder secondaryPower ++ , final BeaconBlockEntity entity // Paper - pass beacon block entity ) { -+ // Paper start - pass beacon block entity -+ applyEffects(level, pos, beaconLevel, primaryEffect, secondaryEffect, null); -+ } -+ -+ private static void applyEffects( -+ Level level, BlockPos pos, int beaconLevel, @Nullable Holder primaryEffect, @Nullable Holder secondaryEffect, @Nullable BeaconBlockEntity blockEntity -+ ) { -+ // Paper end - pass beacon block entity - if (!level.isClientSide() && primaryEffect != null) { -- double d = beaconLevel * 10 + 10; -- int i = 0; -- if (beaconLevel >= 4 && Objects.equals(primaryEffect, secondaryEffect)) { -- i = 1; + if (!level.isClientSide() && primaryPower != null) { +- double range = levels * 10 + 10; +- int baseAmp = 0; +- if (levels >= 4 && Objects.equals(primaryPower, secondaryPower)) { +- baseAmp = 1; - } - -- int i1 = (9 + beaconLevel * 2) * 20; -- AABB aabb = new AABB(pos).inflate(d).expandTowards(0.0, level.getHeight(), 0.0); -- List entitiesOfClass = level.getEntitiesOfClass(Player.class, aabb); +- int durationTicks = (9 + levels * 2) * 20; +- AABB bb = new AABB(worldPosition).inflate(range).expandTowards(0.0, level.getHeight(), 0.0); +- List players = level.getEntitiesOfClass(Player.class, bb); - -- for (Player player : entitiesOfClass) { -- player.addEffect(new MobEffectInstance(primaryEffect, i1, i, true, true)); +- for (Player player : players) { +- player.addEffect(new MobEffectInstance(primaryPower, durationTicks, baseAmp, true, true)); - } - -- if (beaconLevel >= 4 && !Objects.equals(primaryEffect, secondaryEffect) && secondaryEffect != null) { -- for (Player player : entitiesOfClass) { -- player.addEffect(new MobEffectInstance(secondaryEffect, i1, 0, true, true)); -+ double d = computeBeaconRange(beaconLevel); // Paper - diff out applyEffects logic components - see below -+ int i = computeEffectAmplifier(beaconLevel, primaryEffect, secondaryEffect); // Paper - diff out applyEffects logic components - see below +- if (levels >= 4 && !Objects.equals(primaryPower, secondaryPower) && secondaryPower != null) { +- for (Player player : players) { +- player.addEffect(new MobEffectInstance(secondaryPower, durationTicks, 0, true, true)); ++ double range = computeBeaconRange(levels); // Paper - diff out applyEffects logic components - see below ++ int baseAmp = computeEffectAmplifier(levels, primaryPower, secondaryPower); // Paper - diff out applyEffects logic components - see below + -+ int i1 = computeEffectDuration(beaconLevel); // Paper - diff out applyEffects logic components - see below -+ List entitiesOfClass = getHumansInRange(level, pos, beaconLevel, blockEntity); // Paper - diff out applyEffects logic components - see below ++ int durationTicks = computeEffectDuration(levels); // Paper - diff out applyEffects logic components - see below ++ List players = getHumansInRange(level, worldPosition, levels, entity); // Paper - diff out applyEffects logic components - see below + -+ applyEffectsAndCallEvent(level, pos, entitiesOfClass, new MobEffectInstance(primaryEffect, i1, i, true, true), true); // Paper - BeaconEffectEvent ++ applyEffectsAndCallEvent(level, worldPosition, players, new MobEffectInstance(primaryPower, durationTicks, baseAmp, true, true), true); // Paper - BeaconEffectEvent + -+ if (hasSecondaryEffect(beaconLevel, primaryEffect, secondaryEffect)) { // Paper - diff out applyEffects logic components - see below -+ applyEffectsAndCallEvent(level, pos, entitiesOfClass, new MobEffectInstance(secondaryEffect, i1, 0, true, true), false); // Paper - BeaconEffectEvent ++ if (hasSecondaryEffect(levels, primaryPower, secondaryPower)) { // Paper - diff out applyEffects logic components - see below ++ applyEffectsAndCallEvent(level, worldPosition, players, new MobEffectInstance(secondaryPower, durationTicks, 0, true, true), false); // Paper - BeaconEffectEvent + } + } + } @@ -148,11 +134,11 @@ + } + + private static int computeEffectAmplifier(final int beaconLevel, @Nullable Holder primaryEffect, @Nullable Holder secondaryEffect) { -+ int i = 0; ++ int baseAmp = 0; + if (beaconLevel >= 4 && Objects.equals(primaryEffect, secondaryEffect)) { -+ i = 1; ++ baseAmp = 1; + } -+ return i; ++ return baseAmp; + } + + private static double computeBeaconRange(final int beaconLevel) { @@ -160,11 +146,11 @@ + } + + public static List getHumansInRange(final Level level, final BlockPos pos, final int beaconLevel, final @Nullable BeaconBlockEntity blockEntity) { -+ final double d = blockEntity != null ? blockEntity.getEffectRange() : computeBeaconRange(beaconLevel); -+ AABB aabb = new AABB(pos).inflate(d).expandTowards(0.0, level.getHeight(), 0.0); // Diff from applyEffects ++ final double range = blockEntity != null ? blockEntity.getEffectRange() : computeBeaconRange(beaconLevel); ++ AABB aabb = new AABB(pos).inflate(range).expandTowards(0.0, level.getHeight(), 0.0); // Diff from applyEffects + // Improve performance of human lookup by switching to a global player iteration when searching over 128 blocks + List list; -+ if (d <= 128.0) { ++ if (range <= 128.0) { + list = level.getEntitiesOfClass(Player.class, aabb); // Diff from applyEffect + } else { + list = new java.util.ArrayList<>(); @@ -198,18 +184,18 @@ + } + // Paper end - BeaconEffectEvent - public static void playSound(Level level, BlockPos pos, SoundEvent sound) { - level.playSound(null, pos, sound, SoundSource.BLOCKS, 1.0F, 1.0F); -@@ -280,7 +_,7 @@ + public static void playSound(final Level level, final BlockPos worldPosition, final SoundEvent event) { + level.playSound(null, worldPosition, event, SoundSource.BLOCKS, 1.0F, 1.0F); +@@ -288,7 +_,7 @@ } - private static @Nullable Holder loadEffect(ValueInput input, String key) { -- return input.read(key, BuiltInRegistries.MOB_EFFECT.holderByNameCodec()).filter(VALID_EFFECTS::contains).orElse(null); -+ return input.read(key, BuiltInRegistries.MOB_EFFECT.holderByNameCodec()).orElse(null); // CraftBukkit - persist manually set non-default beacon effects (SPIGOT-3598) + private static @Nullable Holder loadEffect(final ValueInput input, final String field) { +- return input.read(field, BuiltInRegistries.MOB_EFFECT.holderByNameCodec()).filter(VALID_EFFECTS::contains).orElse(null); ++ return input.read(field, BuiltInRegistries.MOB_EFFECT.holderByNameCodec()).orElse(null); // CraftBukkit - persist manually set non-default beacon effects (SPIGOT-3598) } @Override -@@ -288,8 +_,10 @@ +@@ -296,8 +_,10 @@ super.loadAdditional(input); this.primaryPower = loadEffect(input, "primary_effect"); this.secondaryPower = loadEffect(input, "secondary_effect"); @@ -220,20 +206,20 @@ } @Override -@@ -300,6 +_,7 @@ +@@ -308,6 +_,7 @@ output.putInt("Levels", this.levels); output.storeNullable("CustomName", ComponentSerialization.CODEC, this.name); this.lockKey.addToTag(output); + output.putDouble(PAPER_RANGE_TAG, this.effectRange); // Paper - Custom beacon ranges } - public void setCustomName(@Nullable Component name) { -@@ -313,7 +_,7 @@ + public void setCustomName(final @Nullable Component name) { +@@ -321,7 +_,7 @@ @Override - public @Nullable AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) { + public @Nullable AbstractContainerMenu createMenu(final int containerId, final Inventory inventory, final Player player) { - if (this.lockKey.canUnlock(player)) { + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockLockCheckEvent(this, this.lockKey, this.getDisplayName(), player)) { // Paper - Call BlockLockCheckEvent - return new BeaconMenu(containerId, playerInventory, this.dataAccess, ContainerLevelAccess.create(this.level, this.getBlockPos())); + return new BeaconMenu(containerId, inventory, this.dataAccess, ContainerLevelAccess.create(this.level, this.getBlockPos())); } else { BaseContainerBlockEntity.sendChestLockedNotifications(this.getBlockPos().getCenter(), player, this.getDisplayName()); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch index 0ee4679347a5..9e05c00cee64 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -@@ -85,6 +_,7 @@ +@@ -86,6 +_,7 @@ public static final int MIN_OCCUPATION_TICKS_NECTARLESS = 600; private List stored = Lists.newArrayList(); public @Nullable BlockPos savedFlowerPos; + public int maxBees = MAX_OCCUPANTS; // CraftBukkit - allow setting max amount of bees a hive can hold - public BeehiveBlockEntity(BlockPos pos, BlockState blockState) { - super(BlockEntityType.BEEHIVE, pos, blockState); -@@ -118,7 +_,7 @@ + public BeehiveBlockEntity(final BlockPos worldPosition, final BlockState blockState) { + super(BlockEntityType.BEEHIVE, worldPosition, blockState); +@@ -119,7 +_,7 @@ } public boolean isFull() { @@ -16,33 +16,34 @@ + return this.stored.size() == this.maxBees; // CraftBukkit } - public void emptyAllLivingFromHive(@Nullable Player player, BlockState state, BeehiveBlockEntity.BeeReleaseStatus releaseStatus) { -@@ -127,7 +_,7 @@ - for (Entity entity : list) { - if (entity instanceof Bee bee && player.position().distanceToSqr(entity.position()) <= 16.0) { + public void emptyAllLivingFromHive(final @Nullable Player player, final BlockState state, final BeehiveBlockEntity.BeeReleaseStatus releaseReason) { +@@ -128,7 +_,7 @@ + for (Entity released : releasedFromHive) { + if (released instanceof Bee bee && player.position().distanceToSqr(released.position()) <= 16.0) { if (!this.isSedated()) { - bee.setTarget(player); + bee.setTarget(player, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER); // CraftBukkit } else { bee.setStayOutOfHiveCountdown(400); } -@@ -137,8 +_,14 @@ +@@ -138,10 +_,15 @@ } - private List releaseAllOccupants(BlockState state, BeehiveBlockEntity.BeeReleaseStatus releaseStatus) { + private List releaseAllOccupants(final BlockState state, final BeehiveBlockEntity.BeeReleaseStatus releaseStatus) { + // CraftBukkit start - This allows us to bypass the night/rain/emergency check + return this.releaseBees(state, releaseStatus, false); + } -+ -+ public List releaseBees(BlockState state, BeehiveBlockEntity.BeeReleaseStatus releaseStatus, boolean force) { -+ // CraftBukkit end - This allows us to bypass t he night/rain/emergecny check - List list = Lists.newArrayList(); -- this.stored.removeIf(data -> releaseOccupant(this.level, this.worldPosition, state, data.toOccupant(), list, releaseStatus, this.savedFlowerPos)); -+ this.stored.removeIf(data -> releaseOccupant(this.level, this.worldPosition, state, data.toOccupant(), list, releaseStatus, this.savedFlowerPos, force)); // CraftBukkit - This allows us to bypass t he night/rain/emergecny check - if (!list.isEmpty()) { ++ public List releaseBees(final BlockState state, final BeehiveBlockEntity.BeeReleaseStatus releaseStatus, final boolean force) { ++ // CraftBukkit end - This allows us to bypass the night/rain/emergency check + List spawned = Lists.newArrayList(); + this.stored + .removeIf( +- occupantEntry -> releaseOccupant(this.level, this.worldPosition, state, occupantEntry.toOccupant(), spawned, releaseStatus, this.savedFlowerPos) ++ occupantEntry -> releaseOccupant(this.level, this.worldPosition, state, occupantEntry.toOccupant(), spawned, releaseStatus, this.savedFlowerPos, force) // CraftBukkit - This allows us to bypass the night/rain/emergency check + ); + if (!spawned.isEmpty()) { super.setChanged(); - } -@@ -151,6 +_,11 @@ +@@ -155,6 +_,12 @@ return this.stored.size(); } @@ -51,13 +52,14 @@ + this.stored.clear(); + } + // Paper end - Add EntityBlockStorage clearEntities - public static int getHoneyLevel(BlockState state) { - return state.getValue(BeehiveBlock.HONEY_LEVEL); ++ + public static int getHoneyLevel(final BlockState blockState) { + return blockState.getValue(BeehiveBlock.HONEY_LEVEL); } -@@ -161,7 +_,16 @@ +@@ -165,7 +_,16 @@ } - public void addOccupant(Bee bee) { + public void addOccupant(final Bee bee) { - if (this.stored.size() < 3) { + if (this.stored.size() < this.maxBees) { // CraftBukkit + // CraftBukkit start @@ -72,7 +74,7 @@ bee.stopRiding(); bee.ejectPassengers(); bee.dropLeash(); -@@ -186,7 +_,7 @@ +@@ -190,7 +_,7 @@ this.level.gameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Context.of(bee, this.getBlockState())); } @@ -81,99 +83,99 @@ super.setChanged(); } } -@@ -204,8 +_,21 @@ - BeehiveBlockEntity.BeeReleaseStatus releaseStatus, - @Nullable BlockPos storedFlowerPos - ) { -- if (level.environmentAttributes().getValue(EnvironmentAttributes.BEES_STAY_IN_HIVE, pos) -- && releaseStatus != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { -+ // CraftBukkit start -+ return releaseOccupant(level, pos, state, occupant, storedInHives, releaseStatus, storedFlowerPos, false); +@@ -199,6 +_,12 @@ + this.stored.add(new BeehiveBlockEntity.BeeData(occupant)); + } + ++ // CraftBukkit start ++ private static boolean releaseOccupant(final Level level, final BlockPos blockPos, final BlockState state, final BeehiveBlockEntity.Occupant beeData, final @Nullable List spawned, final BeehiveBlockEntity.BeeReleaseStatus releaseStatus, final @Nullable BlockPos savedFlowerPos) { ++ return releaseOccupant(level, blockPos, state, beeData, spawned, releaseStatus, savedFlowerPos, false); + } -+ private static boolean releaseOccupant( -+ Level level, -+ BlockPos pos, -+ BlockState state, -+ BeehiveBlockEntity.Occupant occupant, -+ @Nullable List storedInHives, -+ BeehiveBlockEntity.BeeReleaseStatus releaseStatus, -+ @Nullable BlockPos storedFlowerPos, -+ boolean force -+ ) { -+ if (!force && level.environmentAttributes().getValue(EnvironmentAttributes.BEES_STAY_IN_HIVE, pos) && releaseStatus != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { -+ // CraftBukkit end ++ // CraftBukkit end ++ + private static boolean releaseOccupant( + final Level level, + final BlockPos blockPos, +@@ -207,8 +_,9 @@ + final @Nullable List spawned, + final BeehiveBlockEntity.BeeReleaseStatus releaseStatus, + final @Nullable BlockPos savedFlowerPos ++ , final boolean force // CraftBukkit + ) { +- if (level.environmentAttributes().getValue(EnvironmentAttributes.BEES_STAY_IN_HIVE, blockPos) ++ if (!force && level.environmentAttributes().getValue(EnvironmentAttributes.BEES_STAY_IN_HIVE, blockPos) // CraftBukkit + && releaseStatus != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { return false; } else { - Direction direction = state.getValue(BeehiveBlock.FACING); -@@ -216,6 +_,17 @@ +@@ -220,6 +_,17 @@ } else { - Entity entity = occupant.createEntity(level, pos); + Entity entity = beeData.createEntity(level, blockPos); if (entity != null) { + // CraftBukkit start + if (entity instanceof Bee) { + float bbWidth = entity.getBbWidth(); -+ double d = flag ? 0.0 : 0.55 + bbWidth / 2.0F; -+ double d1 = pos.getX() + 0.5 + d * direction.getStepX(); -+ double d2 = pos.getY() + 0.5 - entity.getBbHeight() / 2.0F; -+ double d3 = pos.getZ() + 0.5 + d * direction.getStepZ(); -+ entity.snapTo(d1, d2, d3, entity.getYRot(), entity.getXRot()); ++ double delta = frontBlocked ? 0.0 : 0.55 + bbWidth / 2.0F; ++ double spawnX = blockPos.getX() + 0.5 + delta * facing.getStepX(); ++ double spawnY = blockPos.getY() + 0.5 - entity.getBbHeight() / 2.0F; ++ double spawnZ = blockPos.getZ() + 0.5 + delta * facing.getStepZ(); ++ entity.snapTo(spawnX, spawnY, spawnZ, entity.getYRot(), entity.getXRot()); + } + if (!level.addFreshEntity(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BEEHIVE)) return false; // CraftBukkit - SpawnReason, moved from below + // CraftBukkit end if (entity instanceof Bee bee) { - if (storedFlowerPos != null && !bee.hasSavedFlowerPos() && level.random.nextFloat() < 0.9F) { - bee.setSavedFlowerPos(storedFlowerPos); -@@ -231,7 +_,13 @@ - i--; + RandomSource random = level.getRandom(); + if (savedFlowerPos != null && !bee.hasSavedFlowerPos() && random.nextFloat() < 0.9F) { +@@ -236,7 +_,13 @@ + levelIncrease--; } -- level.setBlockAndUpdate(pos, state.setValue(BeehiveBlock.HONEY_LEVEL, honeyLevel + i)); +- level.setBlockAndUpdate(blockPos, state.setValue(BeehiveBlock.HONEY_LEVEL, honeyLevel + levelIncrease)); + // Paper start - Fire EntityChangeBlockEvent in more places -+ BlockState newBlockState = state.setValue(BeehiveBlock.HONEY_LEVEL, honeyLevel + i); ++ BlockState newBlockState = state.setValue(BeehiveBlock.HONEY_LEVEL, honeyLevel + levelIncrease); + -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, newBlockState)) { -+ level.setBlockAndUpdate(pos, newBlockState); ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, blockPos, newBlockState)) { ++ level.setBlockAndUpdate(blockPos, newBlockState); + } + // Paper end - Fire EntityChangeBlockEvent in more places } } } -@@ -240,17 +_,19 @@ - storedInHives.add(bee); +@@ -245,17 +_,19 @@ + spawned.add(bee); } + /* CraftBukkit start - move up float bbWidth = entity.getBbWidth(); - double d = flag ? 0.0 : 0.55 + bbWidth / 2.0F; - double d1 = pos.getX() + 0.5 + d * direction.getStepX(); - double d2 = pos.getY() + 0.5 - entity.getBbHeight() / 2.0F; - double d3 = pos.getZ() + 0.5 + d * direction.getStepZ(); - entity.snapTo(d1, d2, d3, entity.getYRot(), entity.getXRot()); + double delta = frontBlocked ? 0.0 : 0.55 + bbWidth / 2.0F; + double spawnX = blockPos.getX() + 0.5 + delta * facing.getStepX(); + double spawnY = blockPos.getY() + 0.5 - entity.getBbHeight() / 2.0F; + double spawnZ = blockPos.getZ() + 0.5 + delta * facing.getStepZ(); + entity.snapTo(spawnX, spawnY, spawnZ, entity.getYRot(), entity.getXRot()); + */ // CraftBukkit end } - level.playSound(null, pos, SoundEvents.BEEHIVE_EXIT, SoundSource.BLOCKS, 1.0F, 1.0F); - level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(entity, level.getBlockState(pos))); + level.playSound(null, blockPos, SoundEvents.BEEHIVE_EXIT, SoundSource.BLOCKS, 1.0F, 1.0F); + level.gameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Context.of(entity, level.getBlockState(blockPos))); - return level.addFreshEntity(entity); + return true; // CraftBukkit - moved up } else { return false; } -@@ -276,6 +_,11 @@ - flag = true; +@@ -283,6 +_,11 @@ + changed = true; iterator.remove(); } + // Paper start - Fix bees aging inside; use exitTickCounter to keep actual bee life + else if (level.paperConfig().entities.behavior.cooldownFailedBeehiveReleases) { -+ beeData.exitTickCounter = beeData.occupant.minTicksInHive / 2; ++ data.exitTickCounter = data.occupant.minTicksInHive / 2; + } + // Paper end - Fix bees aging inside; use exitTickCounter to keep actual bee life } } -@@ -297,9 +_,10 @@ +@@ -304,9 +_,10 @@ @Override - protected void loadAdditional(ValueInput input) { + protected void loadAdditional(final ValueInput input) { super.loadAdditional(input); - this.stored.clear(); + this.stored = Lists.newArrayList(); // CraftBukkit - SPIGOT-7790: create new copy (may be modified in physics event triggered by honey change) @@ -183,7 +185,7 @@ } @Override -@@ -307,12 +_,13 @@ +@@ -314,12 +_,13 @@ super.saveAdditional(output); output.store("bees", BeehiveBlockEntity.Occupant.LIST_CODEC, this.getBees()); output.storeNullable("flower_pos", BlockPos.CODEC, this.savedFlowerPos); @@ -191,21 +193,21 @@ } @Override - protected void applyImplicitComponents(DataComponentGetter componentGetter) { - super.applyImplicitComponents(componentGetter); + protected void applyImplicitComponents(final DataComponentGetter components) { + super.applyImplicitComponents(components); - this.stored.clear(); + this.stored = Lists.newArrayList(); // CraftBukkit - SPIGOT-7790: create new copy (may be modified in physics event triggered by honey change) - List list = componentGetter.getOrDefault(DataComponents.BEES, Bees.EMPTY).bees(); - list.forEach(this::storeBee); + List bees = components.getOrDefault(DataComponents.BEES, Bees.EMPTY).bees(); + bees.forEach(this::storeBee); } -@@ -340,15 +_,18 @@ +@@ -347,15 +_,18 @@ - static class BeeData { + private static class BeeData { private final BeehiveBlockEntity.Occupant occupant; + private int exitTickCounter; // Paper - Fix bees aging inside hives; separate counter for checking if bee should exit to reduce exit attempts private int ticksInHive; - BeeData(BeehiveBlockEntity.Occupant occupant) { + private BeeData(final BeehiveBlockEntity.Occupant occupant) { this.occupant = occupant; this.ticksInHive = occupant.ticksInHive(); + this.exitTickCounter = this.ticksInHive; // Paper - Fix bees aging inside hives @@ -218,19 +220,3 @@ } public BeehiveBlockEntity.Occupant toOccupant() { -@@ -422,6 +_,7 @@ - } - - private static void setBeeReleaseData(int ticksInHive, Bee bee) { -+ if (!bee.ageLocked) { // Paper - Honor ageLock - int age = bee.getAge(); - if (age < 0) { - bee.setAge(Math.min(0, age + ticksInHive)); -@@ -430,6 +_,7 @@ - } - - bee.setInLoveTime(Math.max(0, bee.getInLoveTime() - ticksInHive)); -+ } // Paper - Honor ageLock - } - } - } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BellBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BellBlockEntity.java.patch index 810b2c47e5e5..dc9dcd3abff4 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BellBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BellBlockEntity.java.patch @@ -1,26 +1,26 @@ --- a/net/minecraft/world/level/block/entity/BellBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BellBlockEntity.java -@@ -60,6 +_,11 @@ +@@ -62,6 +_,11 @@ - if (blockEntity.ticks >= 50) { - blockEntity.shaking = false; + if (entity.ticks >= 50) { + entity.shaking = false; + // Paper start - Fix bell block entity memory leak -+ if (!blockEntity.resonating) { -+ blockEntity.nearbyEntities.clear(); ++ if (!entity.resonating) { ++ entity.nearbyEntities.clear(); + } + // Paper end - Fix bell block entity memory leak - blockEntity.ticks = 0; + entity.ticks = 0; } -@@ -73,6 +_,7 @@ - blockEntity.resonationTicks++; +@@ -75,6 +_,7 @@ + entity.resonationTicks++; } else { - resonationEndAction.run(level, pos, blockEntity.nearbyEntities); -+ blockEntity.nearbyEntities.clear(); // Paper - Fix bell block entity memory leak - blockEntity.resonating = false; + onResonationEnd.run(level, pos, entity.nearbyEntities); ++ entity.nearbyEntities.clear(); // Paper - Fix bell block entity memory leak + entity.resonating = false; } } -@@ -113,6 +_,8 @@ +@@ -115,6 +_,8 @@ } } } @@ -28,33 +28,33 @@ + this.nearbyEntities.removeIf(e -> !e.isAlive()); // Paper - Fix bell block entity memory leak } - private static boolean areRaidersNearby(BlockPos pos, List raiders) { -@@ -129,7 +_,10 @@ + private static boolean areRaidersNearby(final BlockPos bellPos, final List nearbyEntities) { +@@ -128,7 +_,10 @@ } - private static void makeRaidersGlow(Level level, BlockPos pos, List raiders) { -- raiders.stream().filter(raider -> isRaiderWithinRange(pos, raider)).forEach(BellBlockEntity::glow); + private static void makeRaidersGlow(final Level level, final BlockPos blockPos, final List nearbyEntities) { +- nearbyEntities.stream().filter(e -> isRaiderWithinRange(blockPos, e)).forEach(BellBlockEntity::glow); + // Paper start - call bell resonate event and bell reveal raider event -+ final List inRangeRaiders = raiders.stream().filter(raider -> isRaiderWithinRange(pos, raider)).map(e -> (org.bukkit.entity.LivingEntity) e.getBukkitEntity()).toList(); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBellResonateEvent(level, pos, inRangeRaiders).forEach(e -> glow(e, pos)); ++ final List inRangeRaiders = nearbyEntities.stream().filter(e -> isRaiderWithinRange(blockPos, e)).map(e -> (org.bukkit.entity.LivingEntity) e.getBukkitEntity()).toList(); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBellResonateEvent(level, blockPos, inRangeRaiders).forEach(e -> glow(e, blockPos)); + // Paper end - call bell resonate event and bell reveal raider event } - private static void showBellParticles(Level level, BlockPos pos, List raiders) { + private static void showBellParticles(final Level level, final BlockPos bellPos, final List nearbyEntities) { @@ -159,7 +_,16 @@ - return raider.isAlive() && !raider.isRemoved() && pos.closerToCenterThan(raider.position(), 48.0) && raider.getType().is(EntityTypeTags.RAIDERS); + return entity.isAlive() && !entity.isRemoved() && blockPos.closerToCenterThan(entity.position(), 48.0) && entity.is(EntityTypeTags.RAIDERS); } + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Add BellRevealRaiderEvent - private static void glow(LivingEntity entity) { + private static void glow(final LivingEntity raider) { + // Paper start - Add BellRevealRaiderEvent -+ glow(entity, null); ++ glow(raider, null); + } + -+ private static void glow(LivingEntity entity, @javax.annotation.Nullable BlockPos pos) { -+ if (pos != null && !new io.papermc.paper.event.block.BellRevealRaiderEvent(org.bukkit.craftbukkit.block.CraftBlock.at(entity.level(), pos), (org.bukkit.entity.Raider) entity.getBukkitEntity()).callEvent()) ++ private static void glow(LivingEntity raider, @org.jspecify.annotations.Nullable BlockPos pos) { ++ if (pos != null && !new io.papermc.paper.event.block.BellRevealRaiderEvent(org.bukkit.craftbukkit.block.CraftBlock.at(raider.level(), pos), (org.bukkit.entity.Raider) raider.getBukkitEntity()).callEvent()) + return; + // Paper end - Add BellRevealRaiderEvent - entity.addEffect(new MobEffectInstance(MobEffects.GLOWING, 60)); + raider.addEffect(new MobEffectInstance(MobEffects.GLOWING, 60)); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch index 4767a1f8501f..1d32cbf29e53 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/entity/BlockEntity.java +++ b/net/minecraft/world/level/block/entity/BlockEntity.java -@@ -37,6 +_,10 @@ +@@ -40,6 +_,10 @@ import org.slf4j.Logger; - public abstract class BlockEntity implements DebugValueSource { + public abstract class BlockEntity implements DebugValueSource, TypedInstance> { + // CraftBukkit start - data containers + private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); + public final org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer; @@ -11,34 +11,39 @@ private static final Codec> TYPE_CODEC = BuiltInRegistries.BLOCK_ENTITY_TYPE.byNameCodec(); private static final Logger LOGGER = LogUtils.getLogger(); private final BlockEntityType type; -@@ -51,6 +_,7 @@ - this.worldPosition = pos.immutable(); +@@ -54,6 +_,7 @@ + this.worldPosition = worldPosition.immutable(); this.validateBlockState(blockState); this.blockState = blockState; + this.persistentDataContainer = new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(DATA_TYPE_REGISTRY); // Paper - always init } - private void validateBlockState(BlockState state) { -@@ -67,6 +_,7 @@ - int intOr = tag.getIntOr("x", 0); - int intOr1 = tag.getIntOr("y", 0); - int intOr2 = tag.getIntOr("z", 0); -+ if (chunkPos != null) { // Paper - allow reading non-validated pos from tag - used to parse block entities on items - int sectionPosCoord = SectionPos.blockToSectionCoord(intOr); - int sectionPosCoord1 = SectionPos.blockToSectionCoord(intOr2); - if (sectionPosCoord != chunkPos.x || sectionPosCoord1 != chunkPos.z) { -@@ -74,6 +_,7 @@ - intOr = chunkPos.getBlockX(SectionPos.sectionRelative(intOr)); - intOr2 = chunkPos.getBlockZ(SectionPos.sectionRelative(intOr2)); + private void validateBlockState(final BlockState blockState) { +@@ -66,10 +_,11 @@ + return this.type.isValid(blockState); + } + +- public static BlockPos getPosFromTag(final ChunkPos base, final CompoundTag entityTag) { ++ public static BlockPos getPosFromTag(final @Nullable ChunkPos base, final CompoundTag entityTag) { // Paper - allow reading non-validated pos from tag - nullable + int x = entityTag.getIntOr("x", 0); + int y = entityTag.getIntOr("y", 0); + int z = entityTag.getIntOr("z", 0); ++ if (base != null) { // Paper - allow reading non-validated pos from tag - used to parse block entities on items + int sectionX = SectionPos.blockToSectionCoord(x); + int sectionZ = SectionPos.blockToSectionCoord(z); + if (sectionX != base.x() || sectionZ != base.z()) { +@@ -77,6 +_,7 @@ + x = base.getBlockX(SectionPos.sectionRelative(x)); + z = base.getBlockZ(SectionPos.sectionRelative(z)); } + } // Paper - allow reading non-validated pos from tag - used to parse block entities on items - return new BlockPos(intOr, intOr1, intOr2); + return new BlockPos(x, y, z); } -@@ -91,6 +_,12 @@ +@@ -94,6 +_,12 @@ } - protected void loadAdditional(ValueInput input) { + protected void loadAdditional(final ValueInput input) { + // Paper start - read persistent data container + this.persistentDataContainer.clear(); // Paper - clear instead of init + @@ -47,9 +52,9 @@ + // Paper end - read persistent data container } - public final void loadWithComponents(ValueInput input) { -@@ -140,6 +_,11 @@ - public void saveWithoutMetadata(ValueOutput output) { + public final void loadWithComponents(final ValueInput input) { +@@ -143,6 +_,11 @@ + public void saveWithoutMetadata(final ValueOutput output) { this.saveAdditional(output); output.store("components", DataComponentMap.CODEC, this.components); + // CraftBukkit start - store container @@ -59,10 +64,10 @@ + // CraftBukkit end } - public final CompoundTag saveCustomOnly(HolderLookup.Provider registries) { -@@ -155,6 +_,11 @@ + public final CompoundTag saveCustomOnly(final HolderLookup.Provider registries) { +@@ -158,6 +_,11 @@ - public void saveCustomOnly(ValueOutput output) { + public void saveCustomOnly(final ValueOutput output) { this.saveAdditional(output); + // Paper start - store PDC here as well + if (!this.persistentDataContainer.isEmpty()) { @@ -71,34 +76,34 @@ + // Paper end } - public void saveId(ValueOutput output) { -@@ -285,6 +_,12 @@ + public void saveId(final ValueOutput output) { +@@ -293,6 +_,12 @@ } - public final void applyComponents(DataComponentMap components, DataComponentPatch patch) { + public final void applyComponents(final DataComponentMap prototype, final DataComponentPatch patch) { + // CraftBukkit start -+ this.applyComponentsSet(components, patch); ++ this.applyComponentsSet(prototype, patch); + } + -+ public final Set> applyComponentsSet(DataComponentMap components, DataComponentPatch patch) { ++ public final Set> applyComponentsSet(final DataComponentMap prototype, final DataComponentPatch patch) { + // CraftBukkit end - final Set> set = new HashSet<>(); - set.add(DataComponents.BLOCK_ENTITY_DATA); - set.add(DataComponents.BLOCK_STATE); -@@ -304,6 +_,10 @@ + final Set> implicitComponents = new HashSet<>(); + implicitComponents.add(DataComponents.BLOCK_ENTITY_DATA); + implicitComponents.add(DataComponents.BLOCK_STATE); +@@ -316,6 +_,10 @@ }); - DataComponentPatch dataComponentPatch = patch.forget(set::contains); - this.components = dataComponentPatch.split().added(); + DataComponentPatch newPatch = patch.forget(implicitComponents::contains); + this.components = newPatch.split().added(); + // CraftBukkit start -+ set.remove(DataComponents.BLOCK_ENTITY_DATA); // Remove as never actually added by applyImplicitComponents -+ return set; ++ implicitComponents.remove(DataComponents.BLOCK_ENTITY_DATA); // Remove as never actually added by applyImplicitComponents ++ return implicitComponents; + // CraftBukkit end } - protected void collectImplicitComponents(DataComponentMap.Builder components) { -@@ -339,6 +_,27 @@ + protected void collectImplicitComponents(final DataComponentMap.Builder components) { +@@ -351,6 +_,27 @@ @Override - public void registerDebugValues(ServerLevel level, DebugValueSource.Registration registrar) { + public void registerDebugValues(final ServerLevel level, final DebugValueSource.Registration registration) { } + + // CraftBukkit start - add method @@ -122,5 +127,5 @@ + } + // Paper end - Sanitize sent data - record BlockEntityPathElement(BlockEntity blockEntity) implements ProblemReporter.PathElement { + private record BlockEntityPathElement(BlockEntity blockEntity) implements ProblemReporter.PathElement { @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch index a3402fa98d97..266d51acd6f3 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java -@@ -41,6 +_,7 @@ +@@ -43,6 +_,7 @@ private static final Component DEFAULT_NAME = Component.translatable("container.brewing"); private NonNullList items = NonNullList.withSize(5, ItemStack.EMPTY); public int brewTime; @@ -8,15 +8,15 @@ private boolean[] lastPotionCount; private Item ingredient; public int fuel; -@@ -50,6 +_,7 @@ - return switch (index) { +@@ -56,6 +_,7 @@ + return switch (dataId) { case 0 -> BrewingStandBlockEntity.this.brewTime; case 1 -> BrewingStandBlockEntity.this.fuel; + case 2 -> BrewingStandBlockEntity.this.recipeBrewTime; // Paper - Add recipeBrewTime default -> 0; }; } -@@ -62,14 +_,54 @@ +@@ -68,14 +_,54 @@ break; case 1: BrewingStandBlockEntity.this.fuel = value; @@ -70,73 +70,73 @@ + } + // CraftBukkit end - public BrewingStandBlockEntity(BlockPos pos, BlockState blockState) { - super(BlockEntityType.BREWING_STAND, pos, blockState); -@@ -98,8 +_,21 @@ - public static void serverTick(Level level, BlockPos pos, BlockState state, BrewingStandBlockEntity blockEntity) { - ItemStack itemStack = blockEntity.items.get(4); - if (blockEntity.fuel <= 0 && itemStack.is(ItemTags.BREWING_FUEL)) { -- blockEntity.fuel = 20; -- itemStack.shrink(1); + public BrewingStandBlockEntity(final BlockPos worldPosition, final BlockState blockState) { + super(BlockEntityType.BREWING_STAND, worldPosition, blockState); +@@ -104,8 +_,21 @@ + public static void serverTick(final Level level, final BlockPos pos, final BlockState selfState, final BrewingStandBlockEntity entity) { + ItemStack fuel = entity.items.get(4); + if (entity.fuel <= 0 && fuel.is(ItemTags.BREWING_FUEL)) { +- entity.fuel = 20; +- fuel.shrink(1); + // CraftBukkit start + org.bukkit.event.inventory.BrewingStandFuelEvent event = new org.bukkit.event.inventory.BrewingStandFuelEvent( + org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), -+ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), ++ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(fuel), + 20 + ); + if (!event.callEvent()) { + return; + } + -+ blockEntity.fuel = event.getFuelPower(); -+ if (blockEntity.fuel > 0 && event.isConsuming()) { -+ itemStack.shrink(1); ++ entity.fuel = event.getFuelPower(); ++ if (entity.fuel > 0 && event.isConsuming()) { ++ fuel.shrink(1); + } + // CraftBukkit end - setChanged(level, pos, state); + setChanged(level, pos, selfState); } -@@ -110,7 +_,7 @@ - blockEntity.brewTime--; - boolean flag1 = blockEntity.brewTime == 0; - if (flag1 && isBrewable) { -- doBrew(level, pos, blockEntity.items); -+ doBrew(level, pos, blockEntity.items, blockEntity); // CraftBukkit - } else if (!isBrewable || !itemStack1.is(blockEntity.ingredient)) { - blockEntity.brewTime = 0; +@@ -116,7 +_,7 @@ + entity.brewTime--; + boolean isDoneBrewing = entity.brewTime == 0; + if (isDoneBrewing && brewable) { +- doBrew(level, pos, entity.items); ++ doBrew(level, pos, entity.items, entity); // CraftBukkit + } else if (!brewable || !ingredient.is(entity.ingredient)) { + entity.brewTime = 0; } -@@ -118,7 +_,14 @@ - setChanged(level, pos, state); - } else if (isBrewable && blockEntity.fuel > 0) { - blockEntity.fuel--; -- blockEntity.brewTime = 400; +@@ -124,7 +_,14 @@ + setChanged(level, pos, selfState); + } else if (brewable && entity.fuel > 0) { + entity.fuel--; +- entity.brewTime = 400; + // CraftBukkit start + org.bukkit.event.block.BrewingStartEvent event = new org.bukkit.event.block.BrewingStartEvent( + org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), -+ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack1), 400); ++ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(ingredient), 400); + event.callEvent(); -+ blockEntity.recipeBrewTime = event.getRecipeBrewTime(); // Paper - use recipe brew time from event -+ blockEntity.brewTime = event.getBrewingTime(); // 400 -> event.getTotalBrewTime() // Paper - use brewing time from event ++ entity.recipeBrewTime = event.getRecipeBrewTime(); // Paper - use recipe brew time from event ++ entity.brewTime = event.getBrewingTime(); // 400 -> event.getTotalBrewTime() // Paper - use brewing time from event + // CraftBukkit end - blockEntity.ingredient = itemStack1.getItem(); - setChanged(level, pos, state); + entity.ingredient = ingredient.getItem(); + setChanged(level, pos, selfState); } -@@ -169,13 +_,37 @@ +@@ -175,13 +_,37 @@ } } -- private static void doBrew(Level level, BlockPos pos, NonNullList items) { -+ private static void doBrew(Level level, BlockPos pos, NonNullList items, BrewingStandBlockEntity brewingStandBlockEntity) { // CraftBukkit - ItemStack itemStack = items.get(3); +- private static void doBrew(final Level level, final BlockPos pos, final NonNullList items) { ++ private static void doBrew(final Level level, final BlockPos pos, final NonNullList items, final BrewingStandBlockEntity entity) { // CraftBukkit + ItemStack ingredient = items.get(3); PotionBrewing potionBrewing = level.potionBrewing(); + // CraftBukkit start -+ org.bukkit.inventory.InventoryHolder owner = brewingStandBlockEntity.getOwner(); ++ org.bukkit.inventory.InventoryHolder owner = entity.getOwner(); + java.util.List brewResults = new java.util.ArrayList<>(3); - for (int i = 0; i < 3; i++) { -- items.set(i, potionBrewing.mix(itemStack, items.get(i))); + for (int dest = 0; dest < 3; dest++) { +- items.set(dest, potionBrewing.mix(ingredient, items.get(dest))); - } -+ brewResults.add(i, org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(potionBrewing.mix(itemStack, items.get(i)))); ++ brewResults.add(dest, org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(potionBrewing.mix(ingredient, items.get(dest)))); + } + + if (owner != null) { @@ -144,37 +144,37 @@ + org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), + (org.bukkit.inventory.BrewerInventory) owner.getInventory(), + brewResults, -+ brewingStandBlockEntity.fuel ++ entity.fuel + ); + if (!event.callEvent()) { + return; + } + -+ for (int i = 0; i < 3; i++) { -+ if (i < brewResults.size()) { -+ items.set(i, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(brewResults.get(i))); ++ for (int dest = 0; dest < 3; dest++) { ++ if (dest < brewResults.size()) { ++ items.set(dest, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(brewResults.get(dest))); + } else { -+ items.set(i, ItemStack.EMPTY); ++ items.set(dest, ItemStack.EMPTY); + } + } + } + // CraftBukkit end - itemStack.shrink(1); - ItemStack craftingRemainder = itemStack.getItem().getCraftingRemainder(); -@@ -214,13 +_,13 @@ + ingredient.shrink(1); + ItemStackTemplate remainder = ingredient.getItem().getCraftingRemainder(); +@@ -220,13 +_,13 @@ @Override - public boolean canPlaceItem(int index, ItemStack stack) { + public boolean canPlaceItem(final int slot, final ItemStack itemStack) { + PotionBrewing potionBrewing = this.level != null ? this.level.potionBrewing() : PotionBrewing.EMPTY; // Paper - move up - if (index == 3) { + if (slot == 3) { - PotionBrewing potionBrewing = this.level != null ? this.level.potionBrewing() : PotionBrewing.EMPTY; - return potionBrewing.isIngredient(stack); + return potionBrewing.isIngredient(itemStack); } else { - return index == 4 - ? stack.is(ItemTags.BREWING_FUEL) -- : (stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE)) -+ : (stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE) || potionBrewing.isCustomInput(stack)) // Paper - Custom Potion Mixes - && this.getItem(index).isEmpty(); + return slot == 4 + ? itemStack.is(ItemTags.BREWING_FUEL) +- : (itemStack.is(Items.POTION) || itemStack.is(Items.SPLASH_POTION) || itemStack.is(Items.LINGERING_POTION) || itemStack.is(Items.GLASS_BOTTLE)) ++ : (itemStack.is(Items.POTION) || itemStack.is(Items.SPLASH_POTION) || itemStack.is(Items.LINGERING_POTION) || itemStack.is(Items.GLASS_BOTTLE) || potionBrewing.isCustomInput(itemStack)) // Paper - Custom Potion Mixes + && this.getItem(slot).isEmpty(); } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch index 9121a4882188..d3a465b684db 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/entity/BrushableBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BrushableBlockEntity.java -@@ -66,9 +_,26 @@ +@@ -67,9 +_,26 @@ return false; } else { - this.coolDownEndsAtTick = startTick + 10L; + this.coolDownEndsAtTick = gameTime + 10L; + // Paper start - EntityChangeBlockEvent + // The vanilla logic here is *so* backwards, we'd be moving basically *all* following calls down. + // Instead, compute vanilla ourselves up here and just replace the below usages with our computed values for a free diff-on-change. @@ -14,63 +14,65 @@ + final BlockState nextBrokenBlockState = this.getBlockState().setValue(BlockStateProperties.DUSTED, nextCompletionStage); + if (enoughBrushesToBreak || differentCompletionStages) { + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent( -+ brusher, this.worldPosition, enoughBrushesToBreak ? computeTurnsTo().defaultBlockState() : nextBrokenBlockState ++ user, this.worldPosition, enoughBrushesToBreak ? this.computeTurnsTo().defaultBlockState() : nextBrokenBlockState + )) { -+ brushCount--; ++ this.brushCount--; + return false; + } + } + // Paper end - EntityChangeBlockEvent - this.unpackLootTable(level, brusher, stack); -- int completionState = this.getCompletionState(); + this.unpackLootTable(level, user, brush); +- int previousCompletionState = this.getCompletionState(); - if (++this.brushCount >= 10) { -+ int completionState = currentCompletionStage; // Paper - EntityChangeBlockEvent - use precomputed - diff on change ++ int previousCompletionState = currentCompletionStage; // Paper - EntityChangeBlockEvent - use precomputed - diff on change + if (enoughBrushesToBreak) { // Paper - EntityChangeBlockEvent - use precomputed - diff on change - this.brushingCompleted(level, brusher, stack); + this.brushingCompleted(level, user, brush); return true; } else { -@@ -76,7 +_,7 @@ - int completionState1 = this.getCompletionState(); - if (completionState != completionState1) { - BlockState blockState = this.getBlockState(); -- BlockState blockState1 = blockState.setValue(BlockStateProperties.DUSTED, completionState1); -+ BlockState blockState1 = nextBrokenBlockState; // Paper - EntityChangeBlockEvent - use precomputed - diff on change - level.setBlock(this.getBlockPos(), blockState1, Block.UPDATE_ALL); +@@ -77,7 +_,7 @@ + int completionState = this.getCompletionState(); + if (previousCompletionState != completionState) { + BlockState previousState = this.getBlockState(); +- BlockState state = previousState.setValue(BlockStateProperties.DUSTED, completionState); ++ BlockState state = nextBrokenBlockState; // Paper - EntityChangeBlockEvent - use precomputed - diff on change + level.setBlock(this.getBlockPos(), state, Block.UPDATE_ALL); } -@@ -117,6 +_,11 @@ - this.dropContent(level, brusher, stack); +@@ -118,6 +_,12 @@ + this.dropContent(level, user, brush); BlockState blockState = this.getBlockState(); level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_BRUSH_BLOCK_COMPLETE, this.getBlockPos(), Block.getId(blockState)); + // Paper start - EntityChangeEvent - extract result block logic + this.brushingCompleteUpdateBlock(this.computeTurnsTo()); + } ++ + private Block computeTurnsTo() { + // Paper end - EntityChangeEvent - extract result block logic Block turnsInto; if (this.getBlockState().getBlock() instanceof BrushableBlock brushableBlock) { turnsInto = brushableBlock.getTurnsInto(); -@@ -124,6 +_,11 @@ +@@ -125,6 +_,12 @@ turnsInto = Blocks.AIR; } + // Paper start - EntityChangeEvent - extract result block logic + return turnsInto; + } ++ + public void brushingCompleteUpdateBlock(final Block turnsInto) { + // Paper end - EntityChangeEvent - extract result block logic level.setBlock(this.worldPosition, turnsInto.defaultBlockState(), Block.UPDATE_ALL); } -@@ -140,7 +_,12 @@ - double d5 = blockPos.getZ() + 0.5 * d1 + d2; - ItemEntity itemEntity = new ItemEntity(level, d3, d4, d5, this.item.split(level.random.nextInt(21) + 10)); - itemEntity.setDeltaMovement(Vec3.ZERO); -- level.addFreshEntity(itemEntity); +@@ -141,7 +_,12 @@ + double zo = dropPos.getZ() + 0.5 * centerRange + halfSize; + ItemEntity entity = new ItemEntity(level, xo, yo, zo, this.item.split(level.getRandom().nextInt(21) + 10)); + entity.setDeltaMovement(Vec3.ZERO); +- level.addFreshEntity(entity); + // CraftBukkit start -+ if (brusher instanceof final ServerPlayer serverPlayer) { ++ if (user instanceof final ServerPlayer serverPlayer) { + org.bukkit.block.Block bblock = org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.worldPosition); -+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, bblock.getState(), serverPlayer, java.util.List.of(itemEntity)); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, bblock.getState(), serverPlayer, java.util.List.of(entity)); + } + // CraftBukkit end this.item = ItemStack.EMPTY; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java.patch index f86a822e5e36..991f89beb316 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java +++ b/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java -@@ -20,6 +_,12 @@ +@@ -21,6 +_,12 @@ public VibrationSystem.User createVibrationUser() { return new CalibratedSculkSensorBlockEntity.VibrationUser(this.getBlockPos()); } @@ -12,8 +12,8 @@ + // Paper end - Configurable sculk sensor listener range protected class VibrationUser extends SculkSensorBlockEntity.VibrationUser { - public VibrationUser(final BlockPos blockPos1) { -@@ -28,6 +_,7 @@ + public VibrationUser(final BlockPos blockPos) { +@@ -30,6 +_,7 @@ @Override public int getListenerRadius() { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch index b55ef3a0b745..b22ba8ff3bdf 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch @@ -6,58 +6,56 @@ public final int[] cookingTime = new int[4]; + public final boolean[] stopCooking = new boolean[4]; // Paper - Add more Campfire API - public CampfireBlockEntity(BlockPos pos, BlockState blockState) { - super(BlockEntityType.CAMPFIRE, pos, blockState); -@@ -64,14 +_,44 @@ - ItemStack itemStack = campfire.items.get(i); + public CampfireBlockEntity(final BlockPos worldPosition, final BlockState blockState) { + super(BlockEntityType.CAMPFIRE, worldPosition, blockState); +@@ -64,12 +_,42 @@ + ItemStack itemStack = entity.items.get(slot); if (!itemStack.isEmpty()) { - flag = true; -+ if (!campfire.stopCooking[i]) { // Paper - Add more Campfire API - campfire.cookingProgress[i]++; + changed = true; ++ if (!entity.stopCooking[slot]) { // Paper - Add more Campfire API + entity.cookingProgress[slot]++; + } // Paper - Add more Campfire API - if (campfire.cookingProgress[i] >= campfire.cookingTime[i]) { - SingleRecipeInput singleRecipeInput = new SingleRecipeInput(itemStack); -- ItemStack itemStack1 = check.getRecipeFor(singleRecipeInput, level) + if (entity.cookingProgress[slot] >= entity.cookingTime[slot]) { + SingleRecipeInput input = new SingleRecipeInput(itemStack); +- ItemStack result = recipeCache.getRecipeFor(input, level).map(r -> r.value().assemble(input)).orElse(itemStack); + // Paper start - add recipe to cook events -+ final var optionalCookingRecipe = check.getRecipeFor(singleRecipeInput, level); -+ ItemStack itemStack1 = optionalCookingRecipe - .map(recipe -> recipe.value().assemble(singleRecipeInput, level.registryAccess())) - .orElse(itemStack); ++ final java.util.Optional> recipe = recipeCache.getRecipeFor(input, level); ++ ItemStack result = recipe.map(r -> r.value().assemble(input)).orElse(itemStack); + // Paper end - add recipe to cook events - if (itemStack1.isItemEnabled(level.enabledFeatures())) { -- Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), itemStack1); + if (result.isItemEnabled(level.enabledFeatures())) { +- Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), result); + // CraftBukkit start - fire BlockCookEvent + org.bukkit.craftbukkit.inventory.CraftItemStack source = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); -+ org.bukkit.inventory.ItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemStack1); ++ org.bukkit.inventory.ItemStack apiResult = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(result); + + org.bukkit.event.block.BlockCookEvent blockCookEvent = new org.bukkit.event.block.BlockCookEvent( + org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), + source, -+ result, -+ (org.bukkit.inventory.CookingRecipe) optionalCookingRecipe.map(RecipeHolder::toBukkitRecipe).orElse(null) // Paper -Add recipe to cook events ++ apiResult, ++ (org.bukkit.inventory.CookingRecipe) recipe.map(RecipeHolder::toBukkitRecipe).orElse(null) // Paper - Add recipe to cook events + ); + + if (!blockCookEvent.callEvent()) { + return; + } + -+ result = blockCookEvent.getResult(); -+ itemStack1 = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(result); ++ apiResult = blockCookEvent.getResult(); ++ result = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(apiResult); + // CraftBukkit end + // Paper start - Fix item locations dropped from campfires + double deviation = 0.05F * RandomSource.GAUSSIAN_SPREAD_FACTOR; -+ while (!itemStack1.isEmpty()) { -+ net.minecraft.world.entity.item.ItemEntity droppedItem = new net.minecraft.world.entity.item.ItemEntity(level, pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, itemStack1.split(level.random.nextInt(21) + 10)); -+ droppedItem.setDeltaMovement(level.random.triangle(0.0D, deviation), level.random.triangle(0.2D, deviation), level.random.triangle(0.0D, deviation)); ++ while (!result.isEmpty()) { ++ net.minecraft.world.entity.item.ItemEntity droppedItem = new net.minecraft.world.entity.item.ItemEntity(level, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, result.split(level.getRandom().nextInt(21) + 10)); ++ droppedItem.setDeltaMovement(level.getRandom().triangle(0.0, deviation), level.getRandom().triangle(0.2, deviation), level.getRandom().triangle(0.0, deviation)); + level.addFreshEntity(droppedItem); + } + // Paper end - Fix item locations dropped from campfires - campfire.items.set(i, ItemStack.EMPTY); + entity.items.set(slot, ItemStack.EMPTY); level.sendBlockUpdated(pos, state, state, Block.UPDATE_ALL); level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(state)); -@@ -143,6 +_,16 @@ - .ifPresentOrElse( - ints -> System.arraycopy(ints, 0, this.cookingTime, 0, Math.min(this.cookingTime.length, ints.length)), () -> Arrays.fill(this.cookingTime, 0) +@@ -142,6 +_,16 @@ + cookingTimes -> System.arraycopy(cookingTimes, 0, this.cookingTime, 0, Math.min(this.cookingTime.length, cookingTimes.length)), + () -> Arrays.fill(this.cookingTime, 0) ); + + // Paper start - Add more Campfire API @@ -72,7 +70,7 @@ } @Override -@@ -151,6 +_,13 @@ +@@ -150,6 +_,13 @@ ContainerHelper.saveAllItems(output, this.items, true); output.putIntArray("CookingTimes", this.cookingProgress); output.putIntArray("CookingTotalTimes", this.cookingTime); @@ -86,20 +84,20 @@ } @Override -@@ -180,7 +_,15 @@ +@@ -179,7 +_,15 @@ return false; } -- this.cookingTime[i] = recipeFor.get().value().cookingTime(); +- this.cookingTime[slot] = recipe.get().value().cookingTime(); + // CraftBukkit start + org.bukkit.event.block.CampfireStartEvent event = new org.bukkit.event.block.CampfireStartEvent( -+ org.bukkit.craftbukkit.block.CraftBlock.at(this.level,this.worldPosition), -+ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), -+ (org.bukkit.inventory.CampfireRecipe) recipeFor.get().toBukkitRecipe() ++ org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.worldPosition), ++ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(placeItem), ++ (org.bukkit.inventory.CampfireRecipe) recipe.get().toBukkitRecipe() + ); -+ this.level.getCraftServer().getPluginManager().callEvent(event); -+ this.cookingTime[i] = event.getTotalCookTime(); // i -> event.getTotalCookTime() ++ event.callEvent(); ++ this.cookingTime[slot] = event.getTotalCookTime(); // recipe.get().value().cookingTime() -> event.getTotalCookTime() + // CraftBukkit end - this.cookingProgress[i] = 0; - this.items.set(i, stack.consumeAndReturn(1, entity)); - level.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(entity, this.getBlockState())); + this.cookingProgress[slot] = 0; + this.items.set(slot, placeItem.consumeAndReturn(1, sourceEntity)); + serverLevel.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(sourceEntity, this.getBlockState())); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch index 7bda53a37e0c..5a386c38181c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/ChestBlockEntity.java +++ b/net/minecraft/world/level/block/entity/ChestBlockEntity.java -@@ -62,6 +_,40 @@ +@@ -67,6 +_,40 @@ }; private final ChestLidController chestLidController = new ChestLidController(); @@ -38,6 +38,6 @@ + } + // CraftBukkit end + - protected ChestBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { - super(type, pos, blockState); + protected ChestBlockEntity(final BlockEntityType type, final BlockPos worldPosition, final BlockState blockState) { + super(type, worldPosition, blockState); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch index b9714df750b2..5cd1132dcad6 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch @@ -34,14 +34,14 @@ + } + + @Override -+ public @javax.annotation.Nullable org.bukkit.Location getLocation() { ++ public org.bukkit.@org.jspecify.annotations.Nullable Location getLocation() { + if (this.level == null) return null; + return org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.worldPosition, this.level); + } + // CraftBukkit end + - public ChiseledBookShelfBlockEntity(BlockPos pos, BlockState blockState) { - super(BlockEntityType.CHISELED_BOOKSHELF, pos, blockState); + public ChiseledBookShelfBlockEntity(final BlockPos worldPosition, final BlockState blockState) { + super(BlockEntityType.CHISELED_BOOKSHELF, worldPosition, blockState); } @@ -68,7 +_,7 @@ @@ -53,20 +53,20 @@ @Override @@ -81,7 +_,7 @@ - ItemStack itemStack = Objects.requireNonNullElse(this.getItems().get(slot), ItemStack.EMPTY); + ItemStack retrievedItem = Objects.requireNonNullElse(this.getItems().get(slot), ItemStack.EMPTY); this.getItems().set(slot, ItemStack.EMPTY); - if (!itemStack.isEmpty()) { + if (!retrievedItem.isEmpty()) { - this.updateState(slot); + if (this.level != null) this.updateState(slot); // CraftBukkit - SPIGOT-7381: check for null world } - return itemStack; + return retrievedItem; @@ -91,7 +_,7 @@ - public void setItem(int slot, ItemStack stack) { - if (this.acceptsItemType(stack)) { - this.getItems().set(slot, stack); + public void setItem(final int slot, final ItemStack itemStack) { + if (this.acceptsItemType(itemStack)) { + this.getItems().set(slot, itemStack); - this.updateState(slot); + if (this.level != null) this.updateState(slot); // CraftBukkit - SPIGOT-7381: check for null world - } else if (stack.isEmpty()) { + } else if (itemStack.isEmpty()) { this.removeItem(slot, this.getMaxStackSize()); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CommandBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CommandBlockEntity.java.patch index aaa4eb961a99..15397f883fde 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CommandBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CommandBlockEntity.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/entity/CommandBlockEntity.java +++ b/net/minecraft/world/level/block/entity/CommandBlockEntity.java -@@ -27,6 +_,18 @@ - private boolean auto = false; - private boolean conditionMet = false; - private final BaseCommandBlock commandBlock = new BaseCommandBlock() { +@@ -32,6 +_,18 @@ + Objects.requireNonNull(CommandBlockEntity.this); + } + + // CraftBukkit start + @Override + public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper) { @@ -17,11 +17,11 @@ + // CraftBukkit end + @Override - public void setCommand(String command) { + public void setCommand(final String command) { super.setCommand(command); -@@ -47,7 +_,7 @@ +@@ -52,7 +_,7 @@ Vec3.atCenterOf(CommandBlockEntity.this.worldPosition), - new Vec2(0.0F, direction.toYRot()), + new Vec2(0.0F, facing.toYRot()), level, - LevelBasedPermissionSet.GAMEMASTER, + LevelBasedPermissionSet.forLevel(net.minecraft.server.permissions.PermissionLevel.byId(level.paperConfig().commandBlocks.permissionsLevel)), // Paper - configurable command block perm level diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch index da783a5a8b1d..f10559537eed 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch @@ -3,50 +3,52 @@ @@ -164,8 +_,20 @@ } - private static void applyEffects(Level level, BlockPos pos, List positions) { + private static void applyEffects(final Level level, final BlockPos worldPosition, final List effectBlocks) { + // CraftBukkit start -+ ConduitBlockEntity.applyEffects(level, pos, ConduitBlockEntity.getRange(positions)); ++ ConduitBlockEntity.applyEffects(level, worldPosition, ConduitBlockEntity.getRange(effectBlocks)); + } + -+ public static int getRange(List positions) { ++ public static int getRange(List effectBlocks) { + // CraftBukkit end - int size = positions.size(); - int i = size / 7 * 16; + int activeSize = effectBlocks.size(); + int effectRange = activeSize / 7 * 16; + // CraftBukkit start -+ return i; ++ return effectRange; + } + -+ private static void applyEffects(Level level, BlockPos pos, int i) { // i = effect range in blocks ++ private static void applyEffects(Level level, BlockPos worldPosition, int effectRange) { // i = effect range in blocks + // CraftBukkit end - int x = pos.getX(); - int y = pos.getY(); - int z = pos.getZ(); -@@ -174,20 +_,25 @@ - if (!entitiesOfClass.isEmpty()) { - for (Player player : entitiesOfClass) { - if (pos.closerThan(player.blockPosition(), i) && player.isInWaterOrRain()) { + int x = worldPosition.getX(); + int y = worldPosition.getY(); + int z = worldPosition.getZ(); +@@ -174,7 +_,7 @@ + if (!players.isEmpty()) { + for (Player player : players) { + if (worldPosition.closerThan(player.blockPosition(), effectRange) && player.isInWaterOrRain()) { - player.addEffect(new MobEffectInstance(MobEffects.CONDUIT_POWER, 260, 0, true, true)); + player.addEffect(new MobEffectInstance(MobEffects.CONDUIT_POWER, 260, 0, true, true), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONDUIT); // CraftBukkit } } } - } - - private static void updateAndAttackTarget(ServerLevel level, BlockPos pos, BlockState state, ConduitBlockEntity blockEntity, boolean canDestroy) { -+ // CraftBukkit start - add "damageTarget" boolean -+ updateAndAttackTarget(level, pos, state, blockEntity, canDestroy, true); +@@ -183,13 +_,19 @@ + private static void updateAndAttackTarget( + final ServerLevel level, final BlockPos worldPosition, final BlockState blockState, final ConduitBlockEntity entity, final boolean isActive + ) { ++ // CraftBukkit start - add "damageTarget" boolean ++ updateAndAttackTarget(level, worldPosition, blockState, entity, isActive, true); + } -+ public static void updateAndAttackTarget(ServerLevel level, BlockPos pos, BlockState state, ConduitBlockEntity blockEntity, boolean canDestroy, boolean damageTarget) { -+ // CraftBukkit end - add "damageTarget" boolean - EntityReference entityReference = updateDestroyTarget(blockEntity.destroyTarget, level, pos, canDestroy); - LivingEntity livingEntity = EntityReference.getLivingEntity(entityReference, level); -- if (livingEntity != null) { -+ if (damageTarget && livingEntity != null) { // CraftBukkit -+ if (livingEntity.hurtServer(level, level.damageSources().magic().eventBlockDamager(level, pos), 4.0F)) // CraftBukkit - move up ++ ++ public static void updateAndAttackTarget(ServerLevel level, BlockPos worldPosition, BlockState blockState, ConduitBlockEntity entity, boolean isActive, boolean damageTarget) { ++ // CraftBukkit end - add "damageTarget" boolean + EntityReference newDestroyTarget = updateDestroyTarget(entity.destroyTarget, level, worldPosition, isActive); + LivingEntity targetEntity = EntityReference.getLivingEntity(newDestroyTarget, level); +- if (targetEntity != null) { ++ if (damageTarget && targetEntity != null) { // CraftBukkit ++ if (targetEntity.hurtServer(level, level.damageSources().magic().eventBlockDamager(level, worldPosition), 4.0F)) // CraftBukkit - move up level.playSound( - null, livingEntity.getX(), livingEntity.getY(), livingEntity.getZ(), SoundEvents.CONDUIT_ATTACK_TARGET, SoundSource.BLOCKS, 1.0F, 1.0F + null, targetEntity.getX(), targetEntity.getY(), targetEntity.getZ(), SoundEvents.CONDUIT_ATTACK_TARGET, SoundSource.BLOCKS, 1.0F, 1.0F ); -- livingEntity.hurtServer(level, level.damageSources().magic(), 4.0F); +- targetEntity.hurtServer(level, level.damageSources().magic(), 4.0F); } - if (!Objects.equals(entityReference, blockEntity.destroyTarget)) { + if (!Objects.equals(newDestroyTarget, entity.destroyTarget)) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java.patch index 6d5d58c6b485..2256d543153f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java.patch @@ -6,27 +6,27 @@ private double maxInteractionRange; + // CraftBukkit start + public boolean opened; -+ public void onAPIOpen(Level level, BlockPos blockPos, BlockState blockState) { -+ this.onOpen(level, blockPos, blockState); ++ public void onOpenAPI(Level level, BlockPos pos, BlockState blockState) { ++ this.onOpen(level, pos, blockState); + } + -+ public void onAPIClose(Level level, BlockPos blockPos, BlockState blockState) { -+ this.onClose(level, blockPos, blockState); ++ public void onCloseAPI(Level level, BlockPos pos, BlockState blockState) { ++ this.onClose(level, pos, blockState); + } + -+ public void openerAPICountChanged(Level level, BlockPos blockPos, BlockState blockState, int count, int openCount) { -+ this.openerCountChanged(level, blockPos, blockState, count, openCount); ++ public void openerCountChangedAPI(Level level, BlockPos pos, BlockState blockState, int previous, int current) { ++ this.openerCountChanged(level, pos, blockState, previous, current); + } + // CraftBukkit end - protected abstract void onOpen(Level level, BlockPos pos, BlockState state); + protected abstract void onOpen(final Level level, final BlockPos pos, final BlockState blockState); -@@ -26,7 +_,19 @@ - public abstract boolean isOwnContainer(Player player); - - public void incrementOpeners(LivingEntity entity, Level level, BlockPos pos, BlockState state, double interactionRange) { +@@ -28,7 +_,19 @@ + public void incrementOpeners( + final LivingEntity entity, final Level level, final BlockPos pos, final BlockState blockState, final double maxInteractionRange + ) { + int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added - int i = this.openCount++; + int previous = this.openCount++; + + // CraftBukkit start - Call redstone event + if (level.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.TRAPPED_CHEST)) { @@ -38,16 +38,16 @@ + } + // CraftBukkit end + - if (i == 0) { - this.onOpen(level, pos, state); + if (previous == 0) { + this.onOpen(level, pos, blockState); level.gameEvent(entity, GameEvent.CONTAINER_OPEN, pos); -@@ -38,7 +_,20 @@ +@@ -40,7 +_,20 @@ } - public void decrementOpeners(LivingEntity entity, Level level, BlockPos pos, BlockState state) { + public void decrementOpeners(final LivingEntity entity, final Level level, final BlockPos pos, final BlockState blockState) { + int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added + if (this.openCount == 0) return; // Paper - Prevent ContainerOpenersCounter openCount from going negative - int i = this.openCount--; + int previous = this.openCount--; + + // CraftBukkit start - Call redstone event + if (level.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.TRAPPED_CHEST)) { @@ -60,13 +60,13 @@ + // CraftBukkit end + if (this.openCount == 0) { - this.onClose(level, pos, state); + this.onClose(level, pos, blockState); level.gameEvent(entity, GameEvent.CONTAINER_CLOSE, pos); -@@ -70,6 +_,7 @@ +@@ -74,6 +_,7 @@ } - int size = entitiesWithContainerOpen.size(); -+ if (this.opened) size++; // CraftBukkit - add dummy count from API - int i = this.openCount; - if (i != size) { - boolean flag = size != 0; + int openCount = containerUsers.size(); ++ if (this.opened) openCount++; // CraftBukkit - add dummy count from API + int prevCount = this.openCount; + if (prevCount != openCount) { + boolean isOpen = openCount != 0; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch index 14acb4180199..a312097f3c11 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/CrafterBlockEntity.java +++ b/net/minecraft/world/level/block/entity/CrafterBlockEntity.java -@@ -60,6 +_,47 @@ +@@ -67,6 +_,47 @@ } }; @@ -39,12 +39,12 @@ + } + + @Override -+ public @javax.annotation.Nullable org.bukkit.Location getLocation() { ++ public org.bukkit.@org.jspecify.annotations.Nullable Location getLocation() { + if (this.level == null) return null; + return org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.worldPosition, this.level); + } + // CraftBukkit end + - public CrafterBlockEntity(BlockPos pos, BlockState blockState) { - super(BlockEntityType.CRAFTER, pos, blockState); + public CrafterBlockEntity(final BlockPos worldPosition, final BlockState blockState) { + super(BlockEntityType.CRAFTER, worldPosition, blockState); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CreakingHeartBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CreakingHeartBlockEntity.java.patch index a9db73b06478..18cad5b8445d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CreakingHeartBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CreakingHeartBlockEntity.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/block/entity/CreakingHeartBlockEntity.java +++ b/net/minecraft/world/level/block/entity/CreakingHeartBlockEntity.java -@@ -110,7 +_,7 @@ - if (creakingHeart.creakingInfo == null) { - if (blockState.getValue(CreakingHeartBlock.STATE) == CreakingHeartState.AWAKE) { +@@ -111,7 +_,7 @@ + if (entity.creakingInfo == null) { + if (updatedState.getValue(CreakingHeartBlock.STATE) == CreakingHeartState.AWAKE) { if (serverLevel.isSpawningMonsters()) { -- Player nearestPlayer = level.getNearestPlayer(pos.getX(), pos.getY(), pos.getZ(), 32.0, false); -+ Player nearestPlayer = level.getNearestPlayerThatAffectsSpawning(pos.getX(), pos.getY(), pos.getZ(), 32.0, false); // Paper - Affects Spawning API - if (nearestPlayer != null) { - Creaking creaking = spawnProtector(serverLevel, creakingHeart); +- Player player = level.getNearestPlayer(pos.getX(), pos.getY(), pos.getZ(), 32.0, false); ++ Player player = level.getNearestPlayerThatAffectsSpawning(pos.getX(), pos.getY(), pos.getZ(), 32.0, false); // Paper - Affects Spawning API + if (player != null) { + Creaking creaking = spawnProtector(serverLevel, entity); if (creaking != null) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java.patch index 6e186effe97c..74e44965dbbc 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java +++ b/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java -@@ -23,6 +_,48 @@ +@@ -25,6 +_,48 @@ import org.jspecify.annotations.Nullable; - public class DecoratedPotBlockEntity extends BlockEntity implements RandomizableContainer, ContainerSingleItem.BlockContainerSingleItem { + public class DecoratedPotBlockEntity extends BlockEntity implements ContainerSingleItem.BlockContainerSingleItem, RandomizableContainer { + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList<>(); @@ -49,7 +49,7 @@ public static final String TAG_SHERDS = "sherds"; public static final String TAG_ITEM = "item"; public static final int EVENT_POT_WOBBLES = 1; -@@ -45,8 +_,8 @@ +@@ -47,8 +_,8 @@ output.store("sherds", PotDecorations.CODEC, this.decorations); } @@ -60,10 +60,10 @@ } } -@@ -68,7 +_,14 @@ +@@ -70,7 +_,14 @@ @Override - public CompoundTag getUpdateTag(HolderLookup.Provider registries) { + public CompoundTag getUpdateTag(final HolderLookup.Provider registries) { - return this.saveCustomOnly(registries); + // Paper start - hide unnecessary update data + // Like chests, decorated pots should not allow clients to inspect their contents without breaking them. diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch index 50aa6814a631..5864a5c95b24 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch @@ -39,6 +39,6 @@ + } + // CraftBukkit end + - protected DispenserBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { - super(type, pos, blockState); + protected DispenserBlockEntity(final BlockEntityType type, final BlockPos worldPosition, final BlockState blockState) { + super(type, worldPosition, blockState); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch index b6f64612fd73..bb2bc131a7fc 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch @@ -38,105 +38,103 @@ + } + // CraftBukkit end + - public HopperBlockEntity(BlockPos pos, BlockState blockState) { - super(BlockEntityType.HOPPER, pos, blockState); + public HopperBlockEntity(final BlockPos worldPosition, final BlockState blockState) { + super(BlockEntityType.HOPPER, worldPosition, blockState); this.facing = blockState.getValue(HopperBlock.FACING); -@@ -99,7 +_,14 @@ - blockEntity.tickedGameTime = level.getGameTime(); - if (!blockEntity.isOnCooldown()) { - blockEntity.setCooldown(0); -- tryMoveItems(level, pos, state, blockEntity, () -> suckInItems(level, blockEntity)); +@@ -99,7 +_,12 @@ + entity.tickedGameTime = level.getGameTime(); + if (!entity.isOnCooldown()) { + entity.setCooldown(0); +- tryMoveItems(level, pos, state, entity, () -> suckInItems(level, entity)); + // Spigot start -+ boolean result = tryMoveItems(level, pos, state, blockEntity, () -> { -+ return suckInItems(level, blockEntity); -+ }); -+ if (!result && blockEntity.level.spigotConfig.hopperCheck > 1) { -+ blockEntity.setCooldown(blockEntity.level.spigotConfig.hopperCheck); ++ boolean result = tryMoveItems(level, pos, state, entity, () -> suckInItems(level, entity)); ++ if (!result && entity.level.spigotConfig.hopperCheck > 1) { ++ entity.setCooldown(entity.level.spigotConfig.hopperCheck); + } + // Spigot end } } -@@ -118,7 +_,7 @@ +@@ -120,7 +_,7 @@ } - if (flag) { -- blockEntity.setCooldown(8); -+ blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot + if (changed) { +- entity.setCooldown(8); ++ entity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot setChanged(level, pos, state); return true; } -@@ -151,14 +_,47 @@ - ItemStack item = blockEntity.getItem(i); - if (!item.isEmpty()) { - int count = item.getCount(); -- ItemStack itemStack = addItem(blockEntity, attachedContainer, blockEntity.removeItem(i, 1), opposite); +@@ -153,14 +_,47 @@ + ItemStack itemStack = self.getItem(slot); + if (!itemStack.isEmpty()) { + int originalCount = itemStack.getCount(); +- ItemStack result = addItem(self, container, self.removeItem(slot, 1), direction); + // CraftBukkit start - Call event when pushing items into other inventories -+ ItemStack original = item.copy(); ++ ItemStack original = itemStack.copy(); + org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( -+ blockEntity.removeItem(i, level.spigotConfig.hopperAmount) ++ self.removeItem(slot, level.spigotConfig.hopperAmount) + ); // Spigot + + org.bukkit.inventory.Inventory destinationInventory; + // Have to special case large chests as they work oddly -+ if (attachedContainer instanceof final net.minecraft.world.CompoundContainer compoundContainer) { ++ if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); -+ } else if (attachedContainer.getOwner() != null) { -+ destinationInventory = attachedContainer.getOwner().getInventory(); ++ } else if (container.getOwner() != null) { ++ destinationInventory = container.getOwner().getInventory(); + } else { -+ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(attachedContainer); ++ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container); + } + + org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent( -+ blockEntity.getOwner().getInventory(), ++ self.getOwner().getInventory(), + oitemstack, + destinationInventory, + true + ); + if (!event.callEvent()) { -+ blockEntity.setItem(i, original); -+ blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot ++ self.setItem(slot, original); ++ self.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot + return false; + } + int origCount = event.getItem().getAmount(); // Spigot -+ ItemStack itemStack = HopperBlockEntity.addItem(blockEntity, attachedContainer, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), opposite); ++ ItemStack result = HopperBlockEntity.addItem(self, container, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), direction); + // CraftBukkit end + - if (itemStack.isEmpty()) { - attachedContainer.setChanged(); + if (result.isEmpty()) { + container.setChanged(); return true; } - item.setCount(count); -- if (count == 1) { + itemStack.setCount(originalCount); +- if (originalCount == 1) { + // Spigot start -+ item.shrink(origCount - itemStack.getCount()); -+ if (count <= level.spigotConfig.hopperAmount) { ++ itemStack.shrink(origCount - result.getCount()); ++ if (originalCount <= level.spigotConfig.hopperAmount) { + // Spigot end - blockEntity.setItem(i, item); + self.setItem(slot, itemStack); } } -@@ -221,7 +_,7 @@ +@@ -223,7 +_,7 @@ Direction direction = Direction.DOWN; - for (int i : getSlots(sourceContainer, direction)) { -- if (tryTakeInItemFromSlot(hopper, sourceContainer, i, direction)) { -+ if (tryTakeInItemFromSlot(hopper, sourceContainer, i, direction, level)) { // Spigot + for (int slot : getSlots(container, direction)) { +- if (tryTakeInItemFromSlot(hopper, container, slot, direction)) { ++ if (tryTakeInItemFromSlot(hopper, container, slot, direction, level)) { // Spigot return true; } } -@@ -241,18 +_,56 @@ +@@ -245,18 +_,55 @@ } } -- private static boolean tryTakeInItemFromSlot(Hopper hopper, Container container, int slot, Direction direction) { -+ private static boolean tryTakeInItemFromSlot(Hopper hopper, Container container, int slot, Direction direction, Level level) { // Spigot - ItemStack item = container.getItem(slot); - if (!item.isEmpty() && canTakeItemFromContainer(hopper, container, item, slot, direction)) { - int count = item.getCount(); -- ItemStack itemStack = addItem(container, hopper, container.removeItem(slot, 1), null); +- private static boolean tryTakeInItemFromSlot(final Hopper hopper, final Container container, final int slot, final Direction direction) { ++ private static boolean tryTakeInItemFromSlot(final Hopper hopper, final Container container, final int slot, final Direction direction, final Level level) { // Spigot + ItemStack itemStack = container.getItem(slot); + if (!itemStack.isEmpty() && canTakeItemFromContainer(hopper, container, itemStack, slot, direction)) { + int originalCount = itemStack.getCount(); +- ItemStack result = addItem(container, hopper, container.removeItem(slot, 1), null); + // CraftBukkit start - Call event on collection of items from inventories into the hopper -+ ItemStack original = item.copy(); ++ ItemStack original = itemStack.copy(); + org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( + container.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot + ); @@ -168,79 +166,79 @@ + return false; + } + int origCount = event.getItem().getAmount(); // Spigot -+ ItemStack itemStack = HopperBlockEntity.addItem(container, hopper, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), null); ++ ItemStack result = HopperBlockEntity.addItem(container, hopper, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), null); + // CraftBukkit end -+ - if (itemStack.isEmpty()) { + if (result.isEmpty()) { container.setChanged(); return true; } - item.setCount(count); -- if (count == 1) { + itemStack.setCount(originalCount); +- if (originalCount == 1) { + // Spigot start -+ item.shrink(origCount - itemStack.getCount()); -+ if (count <= level.spigotConfig.hopperAmount) { ++ itemStack.shrink(origCount - result.getCount()); ++ if (originalCount <= level.spigotConfig.hopperAmount) { + // Spigot end - container.setItem(slot, item); + container.setItem(slot, itemStack); } } -@@ -262,12 +_,20 @@ +@@ -266,12 +_,20 @@ - public static boolean addItem(Container container, ItemEntity item) { - boolean flag = false; + public static boolean addItem(final Container container, final ItemEntity entity) { + boolean changed = false; + // CraftBukkit start + org.bukkit.event.inventory.InventoryPickupItemEvent event = new org.bukkit.event.inventory.InventoryPickupItemEvent( -+ container.getOwner().getInventory(), (org.bukkit.entity.Item) item.getBukkitEntity() ++ container.getOwner().getInventory(), (org.bukkit.entity.Item) entity.getBukkitEntity() + ); + if (!event.callEvent()) { + return false; + } + // CraftBukkit end - ItemStack itemStack = item.getItem().copy(); - ItemStack itemStack1 = addItem(null, container, itemStack, null); - if (itemStack1.isEmpty()) { - flag = true; - item.setItem(ItemStack.EMPTY); -- item.discard(); -+ item.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause + ItemStack copy = entity.getItem().copy(); + ItemStack result = addItem(null, container, copy, null); + if (result.isEmpty()) { + changed = true; + entity.setItem(ItemStack.EMPTY); +- entity.discard(); ++ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause } else { - item.setItem(itemStack1); + entity.setItem(result); } -@@ -309,11 +_,18 @@ - boolean flag = false; - boolean isEmpty = destination.isEmpty(); - if (item.isEmpty()) { +@@ -317,11 +_,18 @@ + boolean success = false; + boolean wasEmpty = container.isEmpty(); + if (current.isEmpty()) { + // Spigot start - SPIGOT-6693, SimpleContainer#setItem + ItemStack leftover = ItemStack.EMPTY; // Paper - Make hoppers respect inventory max stack size -+ if (!stack.isEmpty() && stack.getCount() > destination.getMaxStackSize()) { -+ leftover = stack; // Paper - Make hoppers respect inventory max stack size -+ stack = stack.split(destination.getMaxStackSize()); ++ if (!itemStack.isEmpty() && itemStack.getCount() > container.getMaxStackSize()) { ++ leftover = itemStack; // Paper - Make hoppers respect inventory max stack size ++ itemStack = itemStack.split(container.getMaxStackSize()); + } + // Spigot end - destination.setItem(slot, stack); -- stack = ItemStack.EMPTY; -+ stack = leftover; // Paper - Make hoppers respect inventory max stack size - flag = true; - } else if (canMergeItems(item, stack)) { -- int i = stack.getMaxStackSize() - item.getCount(); -+ int i = Math.min(stack.getMaxStackSize(), destination.getMaxStackSize()) - item.getCount(); // Paper - Make hoppers respect inventory max stack size - int min = Math.min(stack.getCount(), i); - stack.shrink(min); - item.grow(min); -@@ -327,7 +_,7 @@ - min = 1; + container.setItem(slot, itemStack); +- itemStack = ItemStack.EMPTY; ++ itemStack = leftover; // Paper - Make hoppers respect inventory max stack size + success = true; + } else if (canMergeItems(current, itemStack)) { +- int space = itemStack.getMaxStackSize() - current.getCount(); ++ int space = Math.min(itemStack.getMaxStackSize(), container.getMaxStackSize()) - current.getCount(); // Paper - Make hoppers respect inventory max stack size + int count = Math.min(itemStack.getCount(), space); + itemStack.shrink(count); + current.grow(count); +@@ -335,7 +_,7 @@ + skipTickCount = 1; } -- hopperBlockEntity.setCooldown(8 - min); -+ hopperBlockEntity.setCooldown(hopperBlockEntity.level.spigotConfig.hopperTransfer - min); // Spigot +- hopperBlockEntity.setCooldown(8 - skipTickCount); ++ hopperBlockEntity.setCooldown(hopperBlockEntity.level.spigotConfig.hopperTransfer - skipTickCount); // Spigot } - destination.setChanged(); -@@ -337,12 +_,56 @@ - return stack; - } + container.setChanged(); +@@ -344,13 +_,57 @@ + return itemStack; + } ++ + // CraftBukkit start + private static @Nullable Container runHopperInventorySearchEvent( + Container container, @@ -258,15 +256,15 @@ + return (event.getInventory() != null) ? ((org.bukkit.craftbukkit.inventory.CraftInventory) event.getInventory()).getInventory() : null; + } + // CraftBukkit end -+ - private static @Nullable Container getAttachedContainer(Level level, BlockPos pos, HopperBlockEntity blockEntity) { -- return getContainerAt(level, pos.relative(blockEntity.facing)); + + private static @Nullable Container getAttachedContainer(final Level level, final BlockPos blockPos, final HopperBlockEntity self) { +- return getContainerAt(level, blockPos.relative(self.facing)); + // Paper start -+ BlockPos searchPosition = pos.relative(blockEntity.facing); ++ BlockPos searchPosition = blockPos.relative(self.facing); + Container inventory = getContainerAt(level, searchPosition); + if (org.bukkit.event.inventory.HopperInventorySearchEvent.getHandlerList().getRegisteredListeners().length == 0) return inventory; + -+ org.bukkit.craftbukkit.block.CraftBlock hopper = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); ++ org.bukkit.craftbukkit.block.CraftBlock hopper = org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos); + org.bukkit.craftbukkit.block.CraftBlock searchBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, searchPosition); + return HopperBlockEntity.runHopperInventorySearchEvent( + inventory, @@ -277,10 +275,10 @@ + // Paper end } - private static @Nullable Container getSourceContainer(Level level, Hopper hopper, BlockPos pos, BlockState state) { + private static @Nullable Container getSourceContainer(final Level level, final Hopper hopper, final BlockPos pos, final BlockState state) { - return getContainerAt(level, pos, state, hopper.getLevelX(), hopper.getLevelY() + 1.0, hopper.getLevelZ()); + // Paper start -+ final Container inventory = HopperBlockEntity.getContainerAt(level, pos, state, hopper.getLevelX(), hopper.getLevelY() + 1.0D, hopper.getLevelZ()); ++ final Container inventory = HopperBlockEntity.getContainerAt(level, pos, state, hopper.getLevelX(), hopper.getLevelY() + 1.0, hopper.getLevelZ()); + if (org.bukkit.event.inventory.HopperInventorySearchEvent.getHandlerList().getRegisteredListeners().length == 0) return inventory; + + final BlockPos hopperPos = BlockPos.containing(hopper.getLevelX(), hopper.getLevelY(), hopper.getLevelZ()); @@ -295,11 +293,11 @@ + // Paper end } - public static List getItemsAtAndAbove(Level level, Hopper hopper) { -@@ -364,6 +_,7 @@ + public static List getItemsAtAndAbove(final Level level, final Hopper hopper) { +@@ -374,6 +_,7 @@ } - private static @Nullable Container getBlockContainer(Level level, BlockPos pos, BlockState state) { + private static @Nullable Container getBlockContainer(final Level level, final BlockPos pos, final BlockState state) { + if (!level.spigotConfig.hopperCanLoadChunks && !level.hasChunkAt(pos)) return null; // Spigot Block block = state.getBlock(); if (block instanceof WorldlyContainerHolder) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/JigsawBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/JigsawBlockEntity.java.patch index 489b7dec1443..6d6aa3f05104 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/JigsawBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/JigsawBlockEntity.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/world/level/block/entity/JigsawBlockEntity.java +++ b/net/minecraft/world/level/block/entity/JigsawBlockEntity.java @@ -140,7 +_,12 @@ - public void generate(ServerLevel level, int maxDepth, boolean keepJigsaws) { - BlockPos blockPos = this.getBlockPos().relative(this.getBlockState().getValue(JigsawBlock.ORIENTATION).front()); - Registry registry = level.registryAccess().lookupOrThrow(Registries.TEMPLATE_POOL); -- Holder orThrow = registry.getOrThrow(this.pool); + public void generate(final ServerLevel level, final int levels, final boolean keepJigsaws) { + BlockPos position = this.getBlockPos().relative(this.getBlockState().getValue(JigsawBlock.ORIENTATION).front()); + Registry poolRegistry = level.registryAccess().lookupOrThrow(Registries.TEMPLATE_POOL); +- Holder pool = poolRegistry.getOrThrow(this.pool); + // Paper start - Replace getHolderOrThrow with a null check -+ Holder orThrow = registry.get(this.pool).orElse(null); -+ if (orThrow == null) { ++ Holder pool = poolRegistry.get(this.pool).orElse(null); ++ if (pool == null) { + return; + } + // Paper end - Replace getHolderOrThrow with a null check - JigsawPlacement.generateJigsaw(level, orThrow, this.target, maxDepth, blockPos, keepJigsaws); + JigsawPlacement.generateJigsaw(level, pool, this.target, levels, position, keepJigsaws); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java.patch index 004ae297513a..3ef3b38b10b1 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java.patch @@ -36,7 +36,7 @@ + } + + @Override -+ public @javax.annotation.Nullable org.bukkit.Location getLocation() { ++ public org.bukkit.@org.jspecify.annotations.Nullable Location getLocation() { + if (this.level == null) return null; + return org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.worldPosition, this.level); + } @@ -54,19 +54,17 @@ } @Override -@@ -157,11 +_,16 @@ +@@ -157,10 +_,15 @@ } @VisibleForTesting -- public void setSongItemWithoutPlaying(ItemStack stack) { -+ public void setSongItemWithoutPlaying(ItemStack stack, final long ticksSinceSongStarted) { // CraftBukkit - passed ticks since song started - this.item = stack; -- JukeboxSong.fromStack(this.level.registryAccess(), stack) -- .ifPresent(holder -> this.jukeboxSongPlayer.setSongWithoutPlaying((Holder)holder, 0L)); +- public void setSongItemWithoutPlaying(final ItemStack itemStack) { ++ public void setSongItemWithoutPlaying(final ItemStack itemStack, final long ticksSinceSongStarted) { // CraftBukkit - passed ticks since song started + this.item = itemStack; +- JukeboxSong.fromStack(itemStack).ifPresent(song -> this.jukeboxSongPlayer.setSongWithoutPlaying((Holder)song, 0L)); - this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock()); + this.jukeboxSongPlayer.song = null; // CraftBukkit - reset -+ JukeboxSong.fromStack(this.level != null ? this.level.registryAccess() : org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry(), stack) // Paper - fallback to other RegistryAccess if no level -+ .ifPresent(holder -> this.jukeboxSongPlayer.setSongWithoutPlaying((Holder)holder, ticksSinceSongStarted)); // CraftBukkit - passed ticks since song started ++ JukeboxSong.fromStack(itemStack).ifPresent(song -> this.jukeboxSongPlayer.setSongWithoutPlaying((Holder)song, ticksSinceSongStarted)); // CraftBukkit - passed ticks since song started + // CraftBukkit start - add null check for level + if (this.level != null) { + this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock()); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch index d6f544105f2a..8715b52b205d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/LecternBlockEntity.java +++ b/net/minecraft/world/level/block/entity/LecternBlockEntity.java -@@ -34,7 +_,53 @@ +@@ -36,11 +_,56 @@ public static final int NUM_DATA = 1; public static final int SLOT_BOOK = 0; public static final int NUM_SLOTS = 1; @@ -8,6 +8,10 @@ + // CraftBukkit start - add fields and methods + public final Container bookAccess = new LecternInventory(); + public class LecternInventory implements Container { + { + Objects.requireNonNull(LecternBlockEntity.this); + } + + public java.util.List transaction = new java.util.ArrayList<>(); + private int maxStack = 1; + @@ -51,17 +55,16 @@ + return LecternBlockEntity.this; + } + // CraftBukkit end -+ @Override public int getContainerSize() { return 1; -@@ -78,11 +_,19 @@ +@@ -84,11 +_,19 @@ @Override - public void setItem(int slot, ItemStack stack) { + public void setItem(final int slot, final ItemStack itemStack) { + // CraftBukkit start + if (slot == 0) { -+ LecternBlockEntity.this.setBook(stack); ++ LecternBlockEntity.this.setBook(itemStack); + if (LecternBlockEntity.this.getLevel() != null) { + LecternBlock.resetBookState(null, LecternBlockEntity.this.getLevel(), LecternBlockEntity.this.getBlockPos(), LecternBlockEntity.this.getBlockState(), LecternBlockEntity.this.hasBook()); + } @@ -76,17 +79,17 @@ } @Override -@@ -160,7 +_,7 @@ - if (i != this.page) { - this.page = i; +@@ -170,7 +_,7 @@ + if (newPage != this.page) { + this.page = newPage; this.setChanged(); - LecternBlock.signalPageChange(this.getLevel(), this.getBlockPos(), this.getBlockState()); + if (this.level != null) LecternBlock.signalPageChange(this.getLevel(), this.getBlockPos(), this.getBlockState()); // CraftBukkit } } -@@ -181,6 +_,36 @@ - return stack; +@@ -192,6 +_,36 @@ + return book; } + // CraftBukkit start @@ -119,24 +122,24 @@ + } + }; + // CraftBukkit end - private CommandSourceStack createCommandSourceStack(@Nullable Player player, ServerLevel level) { - String string; - Component component; -@@ -194,7 +_,7 @@ + private CommandSourceStack createCommandSourceStack(final @Nullable Player player, final ServerLevel level) { + String textName; + Component displayName; +@@ -205,7 +_,7 @@ - Vec3 vec3 = Vec3.atCenterOf(this.worldPosition); + Vec3 pos = Vec3.atCenterOf(this.worldPosition); return new CommandSourceStack( -- CommandSource.NULL, vec3, Vec2.ZERO, level, LevelBasedPermissionSet.GAMEMASTER, string, component, level.getServer(), player -+ this.commandSource, vec3, Vec2.ZERO, level, LevelBasedPermissionSet.GAMEMASTER, string, component, level.getServer(), player // CraftBukkit - commandSource +- CommandSource.NULL, pos, Vec2.ZERO, level, LevelBasedPermissionSet.GAMEMASTER, textName, displayName, level.getServer(), player ++ this.commandSource, pos, Vec2.ZERO, level, LevelBasedPermissionSet.GAMEMASTER, textName, displayName, level.getServer(), player // CraftBukkit - commandSource ); } -@@ -235,7 +_,7 @@ +@@ -246,7 +_,7 @@ @Override - public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) { + public AbstractContainerMenu createMenu(final int containerId, final Inventory inventory, final Player player) { - return new LecternMenu(containerId, this.bookAccess, this.dataAccess); -+ return new LecternMenu(containerId, this.bookAccess, this.dataAccess, playerInventory); // CraftBukkit ++ return new LecternMenu(containerId, this.bookAccess, this.dataAccess, inventory); // CraftBukkit } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java.patch index 587d8177a4e8..796a821f5366 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +++ b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -@@ -114,4 +_,13 @@ +@@ -117,4 +_,13 @@ output.discard("LootTable"); output.discard("LootTableSeed"); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java.patch index 27421fe52fed..ea19fc3705be 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java +++ b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java @@ -35,8 +_,18 @@ - this.catalystListener = new SculkCatalystBlockEntity.CatalystListener(blockState, new BlockPositionSource(pos)); + this.catalystListener = new SculkCatalystBlockEntity.CatalystListener(blockState, new BlockPositionSource(worldPosition)); } + // Paper start - Fix NPE in SculkBloomEvent world access @@ -12,9 +12,9 @@ + } + // Paper end - Fix NPE in SculkBloomEvent world access + - public static void serverTick(Level level, BlockPos pos, BlockState state, SculkCatalystBlockEntity sculkCatalyst) { -+ org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = sculkCatalyst.getBlockPos(); // CraftBukkit - SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep. - sculkCatalyst.catalystListener.getSculkSpreader().updateCursors(level, pos, level.getRandom(), true); + public static void serverTick(final Level level, final BlockPos pos, final BlockState state, final SculkCatalystBlockEntity entity) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = entity.getBlockPos(); // CraftBukkit - SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPos up to five methods deep. + entity.catalystListener.getSculkSpreader().updateCursors(level, pos, level.getRandom(), true); + org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = null; // CraftBukkit } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java.patch index aa3908f8b08e..cbe5a9a1731f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java +++ b/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java -@@ -21,6 +_,7 @@ +@@ -22,6 +_,7 @@ private final VibrationSystem.Listener vibrationListener; private final VibrationSystem.User vibrationUser; public int lastVibrationFrequency = 0; + @Nullable public Integer rangeOverride = null; // Paper - Configurable sculk sensor listener range - protected SculkSensorBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { - super(type, pos, blockState); -@@ -42,14 +_,22 @@ + protected SculkSensorBlockEntity(final BlockEntityType type, final BlockPos worldPosition, final BlockState blockState) { + super(type, worldPosition, blockState); +@@ -43,14 +_,22 @@ super.loadAdditional(input); this.lastVibrationFrequency = input.getIntOr("last_vibration_frequency", 0); this.vibrationData = input.read("listener", VibrationSystem.Data.CODEC).orElseGet(VibrationSystem.Data::new); @@ -17,7 +17,7 @@ + protected static final String PAPER_LISTENER_RANGE_NBT_KEY = "Paper.ListenerRange"; // Paper - Configurable sculk sensor listener range @Override - protected void saveAdditional(ValueOutput output) { + protected void saveAdditional(final ValueOutput output) { super.saveAdditional(output); output.putInt("last_vibration_frequency", this.lastVibrationFrequency); output.store("listener", VibrationSystem.Data.CODEC, this.vibrationData); @@ -32,7 +32,7 @@ @Override public VibrationSystem.Data getVibrationData() { -@@ -86,6 +_,7 @@ +@@ -89,6 +_,7 @@ @Override public int getListenerRadius() { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java.patch index 1079d36b8d11..8bdd7e75a099 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java.patch @@ -1,21 +1,21 @@ --- a/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java +++ b/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java -@@ -87,6 +_,13 @@ +@@ -89,6 +_,13 @@ } - public static @Nullable ServerPlayer tryGetPlayer(@Nullable Entity entity) { + public static @Nullable ServerPlayer tryGetPlayer(final @Nullable Entity sourceEntity) { + // Paper start - check global player list where appropriate; ensure level is the same for sculk events -+ final ServerPlayer player = tryGetPlayer0(entity); -+ return player != null && player.level() == entity.level() ? player : null; ++ final ServerPlayer player = tryGetPlayer0(sourceEntity); ++ return player != null && sourceEntity != null && player.level() == sourceEntity.level() ? player : null; + } + @Nullable -+ private static ServerPlayer tryGetPlayer0(@Nullable Entity entity) { ++ private static ServerPlayer tryGetPlayer0(final @Nullable Entity sourceEntity) { + // Paper end - check global player list where appropriate - if (entity instanceof ServerPlayer serverPlayer) { - return serverPlayer; - } else if (entity != null && entity.getControllingPassenger() instanceof ServerPlayer serverPlayer) { -@@ -162,7 +_,7 @@ - private boolean trySummonWarden(ServerLevel level) { + if (sourceEntity instanceof ServerPlayer player) { + return player; + } else if (sourceEntity != null && sourceEntity.getControllingPassenger() instanceof ServerPlayer player) { +@@ -165,7 +_,7 @@ + private boolean trySummonWarden(final ServerLevel level) { return this.warningLevel >= 4 && SpawnUtil.trySpawnMob( - EntityType.WARDEN, EntitySpawnReason.TRIGGERED, level, this.getBlockPos(), 20, 5, 6, SpawnUtil.Strategy.ON_TOP_OF_COLLIDER, false diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShelfBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShelfBlockEntity.java.patch index ca4946a67577..c070757f53da 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShelfBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShelfBlockEntity.java.patch @@ -39,12 +39,12 @@ + } + + @Override -+ public @javax.annotation.Nullable org.bukkit.Location getLocation() { ++ public org.bukkit.@Nullable Location getLocation() { + if (this.level == null) return null; + return org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.worldPosition, this.level); + } + // CraftBukkit end + - public ShelfBlockEntity(BlockPos pos, BlockState blockState) { - super(BlockEntityType.SHELF, pos, blockState); + public ShelfBlockEntity(final BlockPos worldPosition, final BlockState blockState) { + super(BlockEntityType.SHELF, worldPosition, blockState); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch index 28174a42b059..5e0eb33688b7 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch @@ -40,8 +40,8 @@ + } + // CraftBukkit end + - public ShulkerBoxBlockEntity(@Nullable DyeColor color, BlockPos pos, BlockState blockState) { - super(BlockEntityType.SHULKER_BOX, pos, blockState); + public ShulkerBoxBlockEntity(final @Nullable DyeColor color, final BlockPos worldPosition, final BlockState blockState) { + super(BlockEntityType.SHULKER_BOX, worldPosition, blockState); this.color = color; @@ -171,6 +_,7 @@ } @@ -50,12 +50,12 @@ + if (this.opened) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount); if (this.openCount == 1) { - this.level.gameEvent(user.getLivingEntity(), GameEvent.CONTAINER_OPEN, this.worldPosition); -@@ -184,6 +_,7 @@ - public void stopOpen(ContainerUser user) { - if (!this.remove && !user.getLivingEntity().isSpectator()) { + this.level.gameEvent(containerUser.getLivingEntity(), GameEvent.CONTAINER_OPEN, this.worldPosition); +@@ -186,6 +_,7 @@ + public void stopOpen(final ContainerUser containerUser) { + if (!this.remove && !containerUser.getLivingEntity().isSpectator()) { this.openCount--; + if (this.opened) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call. this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount); if (this.openCount <= 0) { - this.level.gameEvent(user.getLivingEntity(), GameEvent.CONTAINER_CLOSE, this.worldPosition); + this.level.gameEvent(containerUser.getLivingEntity(), GameEvent.CONTAINER_CLOSE, this.worldPosition); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch index 68c8a7c0eaaf..719a70f6d376 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch @@ -1,29 +1,29 @@ --- a/net/minecraft/world/level/block/entity/SignBlockEntity.java +++ b/net/minecraft/world/level/block/entity/SignBlockEntity.java -@@ -58,10 +_,15 @@ +@@ -59,10 +_,15 @@ } - public boolean isFacingFrontText(Player player) { + public boolean isFacingFrontText(final Player player) { + // Paper start - More Sign Block API + return this.isFacingFrontText(player.getX(), player.getZ()); + } -+ public boolean isFacingFrontText(double x, double z) { ++ public boolean isFacingFrontText(final double x, final double z) { + // Paper end - More Sign Block API - if (this.getBlockState().getBlock() instanceof SignBlock signBlock) { - Vec3 signHitboxCenterPosition = signBlock.getSignHitboxCenterPosition(this.getBlockState()); -- double d = player.getX() - (this.getBlockPos().getX() + signHitboxCenterPosition.x); -- double d1 = player.getZ() - (this.getBlockPos().getZ() + signHitboxCenterPosition.z); -+ double d = x - (this.getBlockPos().getX() + signHitboxCenterPosition.x); // Paper - More Sign Block API -+ double d1 = z - (this.getBlockPos().getZ() + signHitboxCenterPosition.z); // Paper - More Sign Block AP - float yRotationDegrees = signBlock.getYRotationDegrees(this.getBlockState()); - float f = (float)(Mth.atan2(d1, d) * 180.0F / (float)Math.PI) - 90.0F; - return Mth.degreesDifferenceAbs(yRotationDegrees, f) <= 90.0F; -@@ -129,11 +_,13 @@ + if (this.getBlockState().getBlock() instanceof SignBlock sign) { + Vec3 signPositionOffset = sign.getSignHitboxCenterPosition(this.getBlockState()); +- double xd = player.getX() - (this.getBlockPos().getX() + signPositionOffset.x); +- double zd = player.getZ() - (this.getBlockPos().getZ() + signPositionOffset.z); ++ double xd = x - (this.getBlockPos().getX() + signPositionOffset.x); // Paper - More Sign Block API ++ double zd = z - (this.getBlockPos().getZ() + signPositionOffset.z); // Paper - More Sign Block API + float signYRot = sign.getYRotationDegrees(this.getBlockState()); + float playerYRot = (float)(Mth.atan2(zd, xd) * 180.0F / (float)Math.PI) - 90.0F; + return Mth.degreesDifferenceAbs(signYRot, playerYRot) <= 90.0F; +@@ -130,11 +_,13 @@ - public void updateSignText(Player player, boolean isFrontText, List filteredText) { + public void updateSignText(final Player player, final boolean frontText, final List lines) { if (!this.isWaxed() && player.getUUID().equals(this.getPlayerWhoMayEdit()) && this.level != null) { -- this.updateText(signText -> this.setMessages(player, filteredText, signText), isFrontText); -+ this.updateText(signText -> this.setMessages(player, filteredText, signText, isFrontText), isFrontText); // CraftBukkit +- this.updateText(text -> this.setMessages(player, lines, text), frontText); ++ this.updateText(text -> this.setMessages(player, lines, text, frontText), frontText); // CraftBukkit this.setAllowedPlayerEditor(null); this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), Block.UPDATE_ALL); } else { @@ -33,43 +33,43 @@ } } -@@ -142,19 +_,41 @@ - return this.setText(updater.apply(text), isFrontText); +@@ -143,19 +_,41 @@ + return this.setText(function.apply(text), isFrontText); } -- private SignText setMessages(Player player, List filteredText, SignText text) { -+ private SignText setMessages(Player player, List filteredText, SignText text, boolean front) { // CraftBukkit +- private SignText setMessages(final Player player, final List lines, SignText text) { ++ private SignText setMessages(final Player player, final List lines, SignText text, final boolean front) { // CraftBukkit + SignText originalText = text; // CraftBukkit - for (int i = 0; i < filteredText.size(); i++) { - FilteredText filteredText1 = filteredText.get(i); - Style style = text.getMessage(i, player.isTextFilteringEnabled()).getStyle(); + for (int i = 0; i < lines.size(); i++) { + FilteredText line = lines.get(i); + Style currentTextStyle = text.getMessage(i, player.isTextFilteringEnabled()).getStyle(); if (player.isTextFilteringEnabled()) { -- text = text.setMessage(i, Component.literal(filteredText1.filteredOrEmpty()).setStyle(style)); -+ text = text.setMessage(i, Component.literal(net.minecraft.util.StringUtil.filterText(filteredText1.filteredOrEmpty())).setStyle(style)); // Paper - filter sign text to chat only +- text = text.setMessage(i, Component.literal(line.filteredOrEmpty()).setStyle(currentTextStyle)); ++ text = text.setMessage(i, Component.literal(net.minecraft.util.StringUtil.filterText(line.filteredOrEmpty())).setStyle(currentTextStyle)); // Paper - filter sign text to chat only } else { text = text.setMessage( -- i, Component.literal(filteredText1.raw()).setStyle(style), Component.literal(filteredText1.filteredOrEmpty()).setStyle(style) -+ i, Component.literal(filteredText1.raw()).setStyle(style), Component.literal(net.minecraft.util.StringUtil.filterText(filteredText1.filteredOrEmpty())).setStyle(style) // Paper - filter sign text to chat only +- i, Component.literal(line.raw()).setStyle(currentTextStyle), Component.literal(line.filteredOrEmpty()).setStyle(currentTextStyle) ++ i, Component.literal(line.raw()).setStyle(currentTextStyle), Component.literal(net.minecraft.util.StringUtil.filterText(line.filteredOrEmpty())).setStyle(currentTextStyle) // Paper - filter sign text to chat only ); } } + // CraftBukkit start + org.bukkit.entity.Player apiPlayer = ((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity(); -+ List lines = new java.util.ArrayList<>(); // Paper - adventure ++ List componentLines = new java.util.ArrayList<>(); // Paper - adventure + -+ for (int i = 0; i < filteredText.size(); ++i) { -+ lines.add(io.papermc.paper.adventure.PaperAdventure.asAdventure(text.getMessage(i, player.isTextFilteringEnabled()))); // Paper - Adventure ++ for (int i = 0; i < lines.size(); ++i) { ++ componentLines.add(io.papermc.paper.adventure.PaperAdventure.asAdventure(text.getMessage(i, player.isTextFilteringEnabled()))); // Paper - Adventure + } + -+ org.bukkit.event.block.SignChangeEvent event = new org.bukkit.event.block.SignChangeEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.worldPosition), apiPlayer, new java.util.ArrayList<>(lines), (front) ? org.bukkit.block.sign.Side.FRONT : org.bukkit.block.sign.Side.BACK); // Paper - Adventure ++ org.bukkit.event.block.SignChangeEvent event = new org.bukkit.event.block.SignChangeEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.worldPosition), apiPlayer, new java.util.ArrayList<>(componentLines), (front) ? org.bukkit.block.sign.Side.FRONT : org.bukkit.block.sign.Side.BACK); // Paper - Adventure + if (!event.callEvent()) { + return originalText; + } + + Component[] components = org.bukkit.craftbukkit.block.CraftSign.sanitizeLines(event.lines()); // Paper - Adventure + for (int i = 0; i < components.length; i++) { -+ if (!java.util.Objects.equals(lines.get(i), event.line(i))) { // Paper - Adventure ++ if (!java.util.Objects.equals(componentLines.get(i), event.line(i))) { // Paper - Adventure + text = text.setMessage(i, components[i]); + } + } @@ -78,19 +78,19 @@ return text; } -@@ -193,7 +_,23 @@ - Style style = component.getStyle(); +@@ -194,7 +_,23 @@ + Style style = message.getStyle(); switch (style.getClickEvent()) { - case ClickEvent.RunCommand runCommand: -- level.getServer().getCommands().performPrefixedCommand(createCommandSourceStack(player, level, pos), runCommand.command()); + case ClickEvent.RunCommand command: +- level.getServer().getCommands().performPrefixedCommand(createCommandSourceStack(player, level, pos), command.command()); + // Paper start - Fix commands from signs not firing command events -+ String command = runCommand.command().startsWith("/") ? runCommand.command() : "/" + runCommand.command(); ++ String commandLine = command.command().startsWith("/") ? command.command() : "/" + command.command(); + if (org.spigotmc.SpigotConfig.logCommands) { -+ LOGGER.info("{} issued server command: {}", player.getScoreboardName(), command); ++ LOGGER.info("{} issued server command: {}", player.getScoreboardName(), commandLine); + } + io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent event = new io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent( + (org.bukkit.entity.Player) player.getBukkitEntity(), -+ command, ++ commandLine, + new org.bukkit.craftbukkit.util.LazyPlayerSet(level.getServer()), + (org.bukkit.block.Sign) org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.worldPosition).getState(), + isFrontText ? org.bukkit.block.sign.Side.FRONT : org.bukkit.block.sign.Side.BACK @@ -100,14 +100,14 @@ + } + level.getServer().getCommands().performPrefixedCommand(createCommandSourceStack(((org.bukkit.craftbukkit.entity.CraftPlayer) event.getPlayer()).getHandle(), level, pos), event.getMessage()); + // Paper end - Fix commands from signs not firing command events - flag = true; + hasAnyClickCommand = true; break; - case ClickEvent.ShowDialog showDialog: -@@ -212,12 +_,55 @@ - return flag; + case ClickEvent.ShowDialog dialog: +@@ -213,11 +_,55 @@ + return hasAnyClickCommand; } -- private static CommandSourceStack createCommandSourceStack(@Nullable Player player, ServerLevel level, BlockPos pos) { +- private static CommandSourceStack createCommandSourceStack(final @Nullable Player player, final ServerLevel level, final BlockPos pos) { + // CraftBukkit start + private final CommandSource commandSource = new CommandSource() { + @@ -135,14 +135,10 @@ + } + }; + -+ private CommandSourceStack createCommandSourceStack(@Nullable Player player, ServerLevel level, BlockPos pos) { ++ private CommandSourceStack createCommandSourceStack(final @Nullable Player player, final ServerLevel level, final BlockPos pos) { + // CraftBukkit end - String string = player == null ? "Sign" : player.getPlainTextName(); - Component component = (Component)(player == null ? Component.literal("Sign") : player.getDisplayName()); -- return new CommandSourceStack( -- CommandSource.NULL, Vec3.atCenterOf(pos), Vec2.ZERO, level, LevelBasedPermissionSet.GAMEMASTER, string, component, level.getServer(), player -- ); -+ + String textName = player == null ? "Sign" : player.getPlainTextName(); + Component displayName = (Component)(player == null ? Component.literal("Sign") : player.getDisplayName()); + // Paper start - Fix commands from signs not firing command events + CommandSource commandSource = level.paperConfig().misc.showSignClickCommandFailureMsgsToPlayer ? new io.papermc.paper.commands.DelegatingCommandSource(this.commandSource) { + @Override @@ -159,11 +155,13 @@ + } : this.commandSource; + // Paper end - Fix commands from signs not firing command events + // CraftBukkit - this -+ return new CommandSourceStack(commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, level, LevelBasedPermissionSet.GAMEMASTER, string, component, level.getServer(), player); // Paper - Fix commands from signs not firing command events + return new CommandSourceStack( +- CommandSource.NULL, Vec3.atCenterOf(pos), Vec2.ZERO, level, LevelBasedPermissionSet.GAMEMASTER, textName, displayName, level.getServer(), player ++ commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, level, LevelBasedPermissionSet.GAMEMASTER, textName, displayName, level.getServer(), player // Paper - Fix commands from signs not firing command events + ); } - @Override -@@ -235,12 +_,17 @@ +@@ -236,12 +_,17 @@ } public @Nullable UUID getPlayerWhoMayEdit() { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/TestBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/TestBlockEntity.java.patch index b515c4ea496c..d322861d25fc 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/TestBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/TestBlockEntity.java.patch @@ -3,7 +3,7 @@ @@ -38,6 +_,7 @@ @Override - protected void loadAdditional(ValueInput input) { + protected void loadAdditional(final ValueInput input) { + super.loadAdditional(input); // Paper - load the PDC this.mode = input.read("mode", TestBlockMode.CODEC).orElse(TestBlockMode.FAIL); this.message = input.getStringOr("message", ""); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/TestInstanceBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/TestInstanceBlockEntity.java.patch index f6bbb048d83e..5654b523a96f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/TestInstanceBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/TestInstanceBlockEntity.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/level/block/entity/TestInstanceBlockEntity.java +++ b/net/minecraft/world/level/block/entity/TestInstanceBlockEntity.java -@@ -157,6 +_,7 @@ +@@ -163,6 +_,7 @@ @Override - protected void loadAdditional(ValueInput input) { + protected void loadAdditional(final ValueInput input) { + super.loadAdditional(input); // Paper - load the PDC input.read("data", TestInstanceBlockEntity.Data.CODEC).ifPresent(this::set); this.errorMarkers.clear(); this.errorMarkers.addAll(input.read("errors", TestInstanceBlockEntity.ErrorMarker.LIST_CODEC).orElse(List.of())); -@@ -329,7 +_,7 @@ +@@ -343,7 +_,7 @@ } private void removeEntities() { -- this.level.getEntities(null, this.getStructureBounds()).stream().filter(entity -> !(entity instanceof Player)).forEach(Entity::discard); -+ this.level.getEntities(null, this.getStructureBounds()).stream().filter(entity -> !(entity instanceof Player)).forEach((entity) -> entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD)); // Paper +- this.level.getEntities(null, this.getTestBounds()).stream().filter(entity -> !(entity instanceof Player)).forEach(Entity::discard); ++ this.level.getEntities(null, this.getTestBounds()).stream().filter(entity -> !(entity instanceof Player)).forEach((entity) -> entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD)); // Paper } private void forceLoadChunks() { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java.patch index c9cd6f58f8f0..18b4f318b5c7 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java.patch @@ -3,9 +3,9 @@ @@ -129,7 +_,7 @@ } - public @Nullable Vec3 getPortalPosition(ServerLevel level, BlockPos pos) { -- if (this.exitPortal == null && level.dimension() == Level.END) { -+ if (this.exitPortal == null && level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.END) { // CraftBukkit - work in alternate worlds - BlockPos blockPos = findOrCreateValidTeleportPos(level, pos); - blockPos = blockPos.above(10); - LOGGER.debug("Creating portal at {}", blockPos); + public @Nullable Vec3 getPortalPosition(final ServerLevel currentLevel, final BlockPos portalEntryPos) { +- if (this.exitPortal == null && currentLevel.dimension() == Level.END) { ++ if (this.exitPortal == null && currentLevel.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.END) { // CraftBukkit - work in alternate worlds + BlockPos exitPortalPos = findOrCreateValidTeleportPos(currentLevel, portalEntryPos); + exitPortalPos = exitPortalPos.above(10); + LOGGER.debug("Creating portal at {}", exitPortalPos); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/PlayerDetector.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/PlayerDetector.java.patch index d67916b09b0a..406af683d0a3 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/PlayerDetector.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/PlayerDetector.java.patch @@ -3,9 +3,9 @@ @@ -22,7 +_,7 @@ public interface PlayerDetector { - PlayerDetector NO_CREATIVE_PLAYERS = (level, entitySelector, pos, maxDistance, requireLineOfSight) -> entitySelector.getPlayers( -- level, player -> player.blockPosition().closerThan(pos, maxDistance) && !player.isCreative() && !player.isSpectator() -+ level, player -> player.blockPosition().closerThan(pos, maxDistance) && !player.isCreative() && !player.isSpectator() && player.affectsSpawning // Paper - Affects Spawning API + PlayerDetector NO_CREATIVE_PLAYERS = (level, selector, pos, requiredPlayerRange, requireLineOfSight) -> selector.getPlayers( +- level, p -> p.blockPosition().closerThan(pos, requiredPlayerRange) && !p.isCreative() && !p.isSpectator() ++ level, p -> p.blockPosition().closerThan(pos, requiredPlayerRange) && !p.isCreative() && !p.isSpectator() && p.affectsSpawning // Paper - Affects Spawning API ) .stream() .filter(player -> !requireLineOfSight || inLineOfSight(level, pos.getCenter(), player.getEyePosition())) diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java.patch index 97db3c8b6051..97ff70e7bc79 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java +++ b/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java -@@ -216,7 +_,14 @@ +@@ -219,7 +_,14 @@ nextSpawnData.getEquipment().ifPresent(mob::equip); } @@ -8,7 +8,7 @@ + // Paper start - TrialSpawnerSpawnEvent + SpawnReason + entity.spawnedViaMobSpawner = true; // Mark entity as spawned via spawner + entity.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.TRIAL_SPAWNER; // Paper - Entity#getEntitySpawnReason -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callTrialSpawnerSpawnEvent(entity, pos).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callTrialSpawnerSpawnEvent(entity, spawnerPos).isCancelled()) { + return Optional.empty(); + } + if (!level.tryAddFreshEntityWithPassengers(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.TRIAL_SPAWNER)) { @@ -16,33 +16,28 @@ return Optional.empty(); } -@@ -231,10 +_,24 @@ - } - - public void ejectReward(ServerLevel level, BlockPos pos, ResourceKey lootTable) { -- LootTable lootTable1 = level.getServer().reloadableRegistries().getLootTable(lootTable); -+ LootTable lootTable1 = level.getServer().reloadableRegistries().getLootTable(lootTable); final LootTable sourceLootTable = lootTable1; // Paper - obfhelper - LootParams lootParams = new LootParams.Builder(level).create(LootContextParamSets.EMPTY); - ObjectArrayList randomItems = lootTable1.getRandomItems(lootParams); - if (!randomItems.isEmpty()) { +@@ -238,6 +_,20 @@ + LootParams params = new LootParams.Builder(level).create(LootContextParamSets.EMPTY); + ObjectArrayList lootDrops = lootTable.getRandomItems(params); + if (!lootDrops.isEmpty()) { + // CraftBukkit start + org.bukkit.event.block.BlockDispenseLootEvent spawnerDispenseLootEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDispenseLootEvent( + level, + pos, + null, -+ randomItems, -+ sourceLootTable ++ lootDrops, ++ lootTable + ); + if (spawnerDispenseLootEvent.isCancelled()) { + return; + } + -+ randomItems = new ObjectArrayList<>(spawnerDispenseLootEvent.getDispensedLoot().stream().map(org.bukkit.craftbukkit.inventory.CraftItemStack::asNMSCopy).toList()); ++ lootDrops = new ObjectArrayList<>(spawnerDispenseLootEvent.getDispensedLoot().stream().map(org.bukkit.craftbukkit.inventory.CraftItemStack::asNMSCopy).toList()); + // CraftBukkit end - for (ItemStack itemStack : randomItems) { - DefaultDispenseItemBehavior.spawnItem(level, itemStack, 2, Direction.UP, Vec3.atBottomCenterOf(pos).relative(Direction.UP, 1.2)); + for (ItemStack item : lootDrops) { + DefaultDispenseItemBehavior.spawnItem(level, item, 2, Direction.UP, Vec3.atBottomCenterOf(pos).relative(Direction.UP, 1.2)); } -@@ -400,6 +_,35 @@ +@@ -401,6 +_,35 @@ this.requiredPlayerRange ); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/TrialSpawnerStateData.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/TrialSpawnerStateData.java.patch index f06bd4ea7c52..2cf57ffee996 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/TrialSpawnerStateData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/trialspawner/TrialSpawnerStateData.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/trialspawner/TrialSpawnerStateData.java +++ b/net/minecraft/world/level/block/entity/trialspawner/TrialSpawnerStateData.java -@@ -186,7 +_,7 @@ +@@ -187,7 +_,7 @@ mob.dropPreservedEquipment(level); } @@ -8,4 +8,4 @@ + entity.remove(Entity.RemovalReason.DISCARDED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - Add bukkit remove cause; } }); - if (!spawner.ominousConfig().spawnPotentialsDefinition().isEmpty()) { + if (!trialSpawner.ominousConfig().spawnPotentialsDefinition().isEmpty()) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java.patch index 3af49bdb93d6..cf97617da22f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java.patch @@ -1,88 +1,96 @@ --- a/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java +++ b/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java -@@ -256,7 +_,13 @@ - if (!list.isEmpty()) { - player.awardStat(Stats.ITEM_USED.get(stack.getItem())); - stack.consume(config.keyItem().getCount(), player); -- unlock(level, state, pos, config, serverData, sharedData, list); +@@ -280,7 +_,13 @@ + if (!itemsToEject.isEmpty()) { + player.awardStat(Stats.ITEM_USED.get(stackToInsert.getItem())); + stackToInsert.consume(config.keyItem().getCount(), player); +- unlock(serverLevel, blockState, pos, config, serverData, sharedData, itemsToEject); + // CraftBukkit start -+ LootTable sourceLootTable = level.getServer().reloadableRegistries().getLootTable(config.lootTable()); -+ org.bukkit.event.block.BlockDispenseLootEvent vaultDispenseLootEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDispenseLootEvent(level, pos, player, list, sourceLootTable); ++ LootTable sourceLootTable = serverLevel.getServer().reloadableRegistries().getLootTable(config.lootTable()); ++ org.bukkit.event.block.BlockDispenseLootEvent vaultDispenseLootEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDispenseLootEvent(serverLevel, pos, player, itemsToEject, sourceLootTable); + if (vaultDispenseLootEvent.isCancelled()) return; -+ list = vaultDispenseLootEvent.getDispensedLoot().stream().map(org.bukkit.craftbukkit.inventory.CraftItemStack::asNMSCopy).toList(); ++ itemsToEject = vaultDispenseLootEvent.getDispensedLoot().stream().map(org.bukkit.craftbukkit.inventory.CraftItemStack::asNMSCopy).toList(); + // CraftBukkit end -+ unlock(level, state, pos, config, serverData, sharedData, list, player); // Paper - Vault API ++ unlock(serverLevel, blockState, pos, config, serverData, sharedData, itemsToEject, player); // Paper - Vault API serverData.addToRewardedPlayers(player); - sharedData.updateConnectedPlayersWithinRange(level, pos, serverData, config, config.deactivationRange()); + sharedData.updateConnectedPlayersWithinRange(serverLevel, pos, serverData, config, config.deactivationRange()); } -@@ -265,8 +_,30 @@ - } - - static void setVaultState(ServerLevel level, BlockPos pos, BlockState oldState, BlockState newState, VaultConfig config, VaultSharedData sharedData) { -- VaultState vaultState = oldState.getValue(VaultBlock.STATE); -- VaultState vaultState1 = newState.getValue(VaultBlock.STATE); -+ // Paper start - Vault API -+ setVaultState(level, pos, oldState, newState, config,sharedData, null); +@@ -296,8 +_,39 @@ + final VaultConfig config, + final VaultSharedData sharedData + ) { ++ // Paper start - Vault API ++ setVaultState(serverLevel, pos, currentBlockState, newBlockState, config, sharedData, null); + } + -+ static void setVaultState(ServerLevel level, BlockPos pos, BlockState oldState, BlockState newState, VaultConfig config, VaultSharedData sharedData, @Nullable Player associatedPlayer) { -+ VaultState vaultState = oldState.getValue(VaultBlock.STATE); final VaultState oldVaultState = vaultState; -+ VaultState vaultState1 = newState.getValue(VaultBlock.STATE); final VaultState newVaultState = vaultState1; ++ static void setVaultState( ++ final ServerLevel serverLevel, ++ final BlockPos pos, ++ final BlockState currentBlockState, ++ final BlockState newBlockState, ++ final VaultConfig config, ++ final VaultSharedData sharedData, ++ final @Nullable Player associatedPlayer ++ ) { + VaultState currentVaultState = currentBlockState.getValue(VaultBlock.STATE); + VaultState newVaultState = newBlockState.getValue(VaultBlock.STATE); ++ final VaultState oldVaultState = currentVaultState; + org.bukkit.entity.Player apiAssociatedPlayer = null; + if (associatedPlayer != null) { + apiAssociatedPlayer = (org.bukkit.entity.Player) associatedPlayer.getBukkitEntity(); + } else if (newVaultState == VaultState.ACTIVE) { + final Set connectedPlayers = sharedData.getConnectedPlayers(); + if (!connectedPlayers.isEmpty()) { // Used over sharedData#hasConnectedPlayers to ensure belows iterator#next is always okay. -+ apiAssociatedPlayer = level.getCraftServer().getPlayer(connectedPlayers.iterator().next()); ++ apiAssociatedPlayer = serverLevel.getCraftServer().getPlayer(connectedPlayers.iterator().next()); + } + } + final io.papermc.paper.event.block.VaultChangeStateEvent event = new io.papermc.paper.event.block.VaultChangeStateEvent( -+ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), ++ org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, pos), + apiAssociatedPlayer, -+ org.bukkit.craftbukkit.block.data.CraftBlockData.toBukkit(oldVaultState, org.bukkit.block.data.type.Vault.State.class), -+ org.bukkit.craftbukkit.block.data.CraftBlockData.toBukkit(newVaultState, org.bukkit.block.data.type.Vault.State.class) ++ org.bukkit.craftbukkit.block.data.CraftBlockData.fromVanilla(oldVaultState, org.bukkit.block.data.type.Vault.State.class), ++ org.bukkit.craftbukkit.block.data.CraftBlockData.fromVanilla(newVaultState, org.bukkit.block.data.type.Vault.State.class) + ); + if (!event.callEvent()) return; -+ // Paper end - Vault API - level.setBlock(pos, newState, Block.UPDATE_ALL); - vaultState.onTransition(level, pos, vaultState1, config, sharedData, newState.getValue(VaultBlock.OMINOUS)); ++ // Paper end - Vault API + serverLevel.setBlock(pos, newBlockState, Block.UPDATE_ALL); + currentVaultState.onTransition(serverLevel, pos, newVaultState, config, sharedData, newBlockState.getValue(VaultBlock.OMINOUS)); } -@@ -278,6 +_,11 @@ - ItemStack randomDisplayItemFromLootTable = getRandomDisplayItemFromLootTable( - level, pos, config.overrideLootTableToDisplay().orElse(config.lootTable()) - ); +@@ -309,6 +_,11 @@ + sharedData.setDisplayItem(ItemStack.EMPTY); + } else { + ItemStack displayItem = getRandomDisplayItemFromLootTable(serverLevel, pos, config.overrideLootTableToDisplay().orElse(config.lootTable())); + // CraftBukkit start -+ org.bukkit.event.block.VaultDisplayItemEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callVaultDisplayItemEvent(level, pos, randomDisplayItemFromLootTable); ++ org.bukkit.event.block.VaultDisplayItemEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callVaultDisplayItemEvent(serverLevel, pos, displayItem); + if (event.isCancelled()) return; -+ randomDisplayItemFromLootTable = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDisplayItem()); ++ displayItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDisplayItem()); + // CraftBukkit end - sharedData.setDisplayItem(randomDisplayItemFromLootTable); + sharedData.setDisplayItem(displayItem); } } -@@ -300,10 +_,24 @@ - VaultSharedData sharedData, - List itemsToEject +@@ -331,10 +_,25 @@ + final VaultSharedData sharedData, + final List itemsToEject ) { -+ // Paper start - Vault API -+ unlock(level, state, pos, config, serverData, sharedData, itemsToEject, null); ++ // Paper start - Vault API ++ unlock(serverLevel, blockState, pos, config, serverData, sharedData, itemsToEject, null); + } ++ + private static void unlock( -+ ServerLevel level, -+ BlockState state, -+ BlockPos pos, -+ VaultConfig config, -+ VaultServerData serverData, -+ VaultSharedData sharedData, -+ List itemsToEject, ++ final ServerLevel serverLevel, ++ final BlockState blockState, ++ final BlockPos pos, ++ final VaultConfig config, ++ final VaultServerData serverData, ++ final VaultSharedData sharedData, ++ final List itemsToEject, + final @Nullable Player associatedPlayer + ) { -+ // Paper end - Vault API ++ // Paper end - Vault API serverData.setItemsToEject(itemsToEject); sharedData.setDisplayItem(serverData.getNextItemToEject()); - serverData.pauseStateUpdatingUntil(level.getGameTime() + 14L); -- setVaultState(level, pos, state, state.setValue(VaultBlock.STATE, VaultState.UNLOCKING), config, sharedData); -+ setVaultState(level, pos, state, state.setValue(VaultBlock.STATE, VaultState.UNLOCKING), config, sharedData, associatedPlayer); // Paper - Vault API + serverData.pauseStateUpdatingUntil(serverLevel.getGameTime() + 14L); +- setVaultState(serverLevel, pos, blockState, blockState.setValue(VaultBlock.STATE, VaultState.UNLOCKING), config, sharedData); ++ setVaultState(serverLevel, pos, blockState, blockState.setValue(VaultBlock.STATE, VaultState.UNLOCKING), config, sharedData, associatedPlayer); // Paper - Vault API } - private static List resolveItemsToEject(ServerLevel level, VaultConfig config, BlockPos pos, Player player, ItemStack key) { + private static List resolveItemsToEject( diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultServerData.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultServerData.java.patch index 3b7f68bd0e66..923e9d99c960 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultServerData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/vault/VaultServerData.java.patch @@ -3,7 +3,7 @@ @@ -62,7 +_,12 @@ @VisibleForTesting - public void addToRewardedPlayers(Player player) { + public void addToRewardedPlayers(final Player player) { - this.rewardedPlayers.add(player.getUUID()); + // Paper start - Vault API + addToRewardedPlayers(player.getUUID()); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/grower/TreeGrower.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/grower/TreeGrower.java.patch index 40be0c223f58..19d3e28e7056 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/grower/TreeGrower.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/grower/TreeGrower.java.patch @@ -1,24 +1,24 @@ --- a/net/minecraft/world/level/block/grower/TreeGrower.java +++ b/net/minecraft/world/level/block/grower/TreeGrower.java -@@ -130,6 +_,7 @@ - .get(configuredMegaFeature) +@@ -132,6 +_,7 @@ + .get(megaFeatureKey) .orElse(null); - if (holder != null) { -+ this.setTreeType(holder); // CraftBukkit - for (int i = 0; i >= -1; i--) { - for (int i1 = 0; i1 >= -1; i1--) { - if (isTwoByTwoSapling(state, level, pos, i, i1)) { -@@ -162,6 +_,7 @@ - if (holder1 == null) { + if (featureHolder != null) { ++ this.setTreeType(featureHolder); // CraftBukkit + for (int dx = 0; dx >= -1; dx--) { + for (int dz = 0; dz >= -1; dz--) { + if (isTwoByTwoSapling(state, level, pos, dx, dz)) { +@@ -164,6 +_,7 @@ + if (featureHolder == null) { return false; } else { -+ this.setTreeType(holder1); // CraftBukkit - ConfiguredFeature configuredFeature2 = holder1.value(); - BlockState blockState1 = level.getFluidState(pos).createLegacyBlock(); - level.setBlock(pos, blockState1, Block.UPDATE_NONE); -@@ -196,4 +_,58 @@ - - return false; ++ this.setTreeType(featureHolder); // CraftBukkit + ConfiguredFeature feature = featureHolder.value(); + BlockState emptyBlock = level.getFluidState(pos).createLegacyBlock(); + level.setBlock(pos, emptyBlock, Block.UPDATE_NONE); +@@ -210,4 +_,58 @@ + : OptionalInt.empty(); + } } + + // CraftBukkit start diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonBaseBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonBaseBlock.java.patch index 656349adc962..e0e76a8fab22 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonBaseBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonBaseBlock.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/level/block/piston/PistonBaseBlock.java +++ b/net/minecraft/world/level/block/piston/PistonBaseBlock.java -@@ -152,6 +_,12 @@ +@@ -150,6 +_,12 @@ @Override - protected boolean triggerEvent(BlockState state, Level level, BlockPos pos, int id, int param) { + protected boolean triggerEvent(final BlockState state, final Level level, final BlockPos pos, final int b0, final int b1) { Direction direction = state.getValue(FACING); + // Paper start - Protect Bedrock and End Portal/Frames from being destroyed; prevent retracting when we're facing the wrong way (we were replaced before retraction could occur) -+ Direction directionQueuedAs = Direction.from3DDataValue(param & 7); // Paper - copied from below ++ Direction directionQueuedAs = Direction.from3DDataValue(b1 & 7); // Paper - copied from below + if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits && direction != directionQueuedAs) { + return false; + } + // Paper end - Protect Bedrock and End Portal/Frames from being destroyed - BlockState blockState = state.setValue(EXTENDED, true); + BlockState extendedState = state.setValue(EXTENDED, true); if (!level.isClientSide()) { - boolean neighborSignal = this.getNeighborSignal(level, pos, direction); -@@ -183,10 +_,17 @@ + boolean extend = this.getNeighborSignal(level, pos, direction); +@@ -182,10 +_,17 @@ .defaultBlockState() .setValue(MovingPistonBlock.FACING, direction) .setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT); @@ -24,20 +24,20 @@ + } + } + // Paper end - Fix sticky pistons and BlockPistonRetractEvent - level.setBlock(pos, blockState1, Block.UPDATE_NONE | Block.UPDATE_KNOWN_SHAPE); + level.setBlock(pos, movingPistonState, Block.UPDATE_NONE | Block.UPDATE_KNOWN_SHAPE); level.setBlockEntity( MovingPistonBlock.newMovingBlockEntity( -- pos, blockState1, this.defaultBlockState().setValue(FACING, Direction.from3DDataValue(param & 7)), direction, false, true -+ pos, blockState1, this.defaultBlockState().setValue(FACING, Direction.from3DDataValue(param & 7)), direction, false, true // Paper - Protect Bedrock and End Portal/Frames from being destroyed; diff on change +- pos, movingPistonState, this.defaultBlockState().setValue(FACING, Direction.from3DDataValue(b1 & 7)), direction, false, true ++ pos, movingPistonState, this.defaultBlockState().setValue(FACING, Direction.from3DDataValue(b1 & 7)), direction, false, true // Paper - Protect Bedrock and End Portal/Frames from being destroyed; diff on change ) ); - level.updateNeighborsAt(pos, blockState1.getBlock()); -@@ -210,13 +_,27 @@ - || blockState2.getPistonPushReaction() != PushReaction.NORMAL - && !blockState2.is(Blocks.PISTON) - && !blockState2.is(Blocks.STICKY_PISTON)) { + level.updateNeighborsAt(pos, movingPistonState.getBlock()); +@@ -209,13 +_,27 @@ + || movingState.getPistonPushReaction() != PushReaction.NORMAL + && !movingState.is(Blocks.PISTON) + && !movingState.is(Blocks.STICKY_PISTON)) { + // Paper start - Fix sticky pistons and BlockPistonRetractEvent; fire BlockPistonRetractEvent for sticky pistons retracting nothing (air) -+ if (id == TRIGGER_CONTRACT && blockState1.isAir()) { ++ if (b0 == TRIGGER_CONTRACT && movingState.isAir()) { + if (!new org.bukkit.event.block.BlockPistonRetractEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), java.util.Collections.emptyList(), org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(direction)).callEvent()) { + return false; + } @@ -60,25 +60,25 @@ + // Paper end - Protect Bedrock and End Portal/Frames from being destroyed } - level.playSound(null, pos, SoundEvents.PISTON_CONTRACT, SoundSource.BLOCKS, 0.5F, level.random.nextFloat() * 0.15F + 0.6F); -@@ -227,7 +_,7 @@ - } - - public static boolean isPushable(BlockState state, Level level, BlockPos pos, Direction movementDirection, boolean allowDestroy, Direction pistonFacing) { + level.playSound(null, pos, SoundEvents.PISTON_CONTRACT, SoundSource.BLOCKS, 0.5F, random.nextFloat() * 0.15F + 0.6F); +@@ -233,7 +_,7 @@ + final boolean allowDestroyable, + final Direction connectionDirection + ) { - if (pos.getY() < level.getMinY() || pos.getY() > level.getMaxY() || !level.getWorldBorder().isWithinBounds(pos)) { -+ if (pos.getY() < level.getMinY() || pos.getY() > level.getMaxY() || !level.getWorldBorder().isWithinBounds(pos) || !level.getWorldBorder().isWithinBounds(pos.relative(movementDirection))) { // Paper - Fix piston world border check ++ if (pos.getY() < level.getMinY() || pos.getY() > level.getMaxY() || !level.getWorldBorder().isWithinBounds(pos) || !level.getWorldBorder().isWithinBounds(pos.relative(direction))) { // Paper - Fix piston world border check return false; } else if (state.isAir()) { return true; -@@ -283,12 +_,54 @@ - BlockState[] blockStates = new BlockState[toPush.size() + toDestroy.size()]; - Direction direction = extending ? facing : facing.getOpposite(); - int i = 0; +@@ -289,12 +_,58 @@ + BlockState[] toUpdate = new BlockState[toPush.size() + toDestroy.size()]; + Direction pushDirection = extending ? direction : direction.getOpposite(); + int updateIndex = 0; + // CraftBukkit start -+ final org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); ++ final org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, pistonPos); + -+ final List moved = pistonStructureResolver.getToPush(); -+ final List broken = pistonStructureResolver.getToDestroy(); ++ final List moved = resolver.getToPush(); ++ final List broken = resolver.getToDestroy(); + + List blocks = new java.util.AbstractList<>() { + @@ -100,9 +100,13 @@ + + final org.bukkit.event.block.BlockPistonEvent event; + if (extending) { -+ event = new org.bukkit.event.block.BlockPistonExtendEvent(bukkitBlock, blocks, org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(direction)); ++ event = new org.bukkit.event.block.BlockPistonExtendEvent( ++ bukkitBlock, blocks, org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(pushDirection) ++ ); + } else { -+ event = new org.bukkit.event.block.BlockPistonRetractEvent(bukkitBlock, blocks, org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(direction)); ++ event = new org.bukkit.event.block.BlockPistonRetractEvent( ++ bukkitBlock, blocks, org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(pushDirection) ++ ); + } + if (!event.callEvent()) { + for (BlockPos brokenPos : broken) { @@ -110,49 +114,51 @@ + } + for (BlockPos movedPos : moved) { + level.sendBlockUpdated(movedPos, Blocks.AIR.defaultBlockState(), level.getBlockState(movedPos), Block.UPDATE_ALL); -+ movedPos = movedPos.relative(direction); ++ movedPos = movedPos.relative(pushDirection); + level.sendBlockUpdated(movedPos, Blocks.AIR.defaultBlockState(), level.getBlockState(movedPos), Block.UPDATE_ALL); + } + return false; + } + // CraftBukkit end - for (int i1 = toDestroy.size() - 1; i1 >= 0; i1--) { - BlockPos blockPos2 = toDestroy.get(i1); - BlockState blockState1 = level.getBlockState(blockPos2); - BlockEntity blockEntity = blockState1.hasBlockEntity() ? level.getBlockEntity(blockPos2) : null; -- dropResources(blockState1, level, blockPos2, blockEntity); -+ dropResources(blockState1, level, blockPos2, blockEntity, pos); // Paper - Add BlockBreakBlockEvent - if (!blockState1.is(BlockTags.FIRE) && level.isClientSide()) { - level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, blockPos2, getId(blockState1)); + for (int i = toDestroy.size() - 1; i >= 0; i--) { + BlockPos pos = toDestroy.get(i); + BlockState state = level.getBlockState(pos); + BlockEntity blockEntity = state.hasBlockEntity() ? level.getBlockEntity(pos) : null; +- dropResources(state, level, pos, blockEntity); ++ dropResources(state, level, pos, blockEntity, pistonPos); // Paper - Add BlockBreakBlockEvent + if (!state.is(BlockTags.FIRE) && level.isClientSide()) { + level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, pos, getId(state)); } -@@ -299,13 +_,26 @@ +@@ -305,13 +_,28 @@ } - for (int i1 = toPush.size() - 1; i1 >= 0; i1--) { -- BlockPos blockPos2 = toPush.get(i1); -- BlockState blockState1 = level.getBlockState(blockPos2); + for (int i = toPush.size() - 1; i >= 0; i--) { +- BlockPos pos = toPush.get(i); +- BlockState blockState = level.getBlockState(pos); + // Paper start - fix a variety of piston desync dupes + boolean allowDesync = io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPistonDuplication; -+ BlockPos blockPos2; -+ BlockPos oldPos = blockPos2 = toPush.get(i1); -+ BlockState blockState1 = allowDesync ? level.getBlockState(oldPos) : null; ++ BlockPos oldPos = toPush.get(i); ++ BlockPos pos = oldPos; ++ BlockState blockState = allowDesync ? level.getBlockState(oldPos) : null; + // Paper end - fix a variety of piston desync dupes - blockPos2 = blockPos2.relative(direction); - map.remove(blockPos2); - BlockState blockState2 = Blocks.MOVING_PISTON.defaultBlockState().setValue(FACING, facing); - level.setBlock(blockPos2, blockState2, Block.UPDATE_NONE | Block.UPDATE_MOVE_BY_PISTON); -- level.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockPos2, blockState2, list.get(i1), facing, extending, false)); + pos = pos.relative(pushDirection); + deleteAfterMove.remove(pos); + BlockState actualState = Blocks.MOVING_PISTON.defaultBlockState().setValue(FACING, direction); + level.setBlock(pos, actualState, Block.UPDATE_NONE | Block.UPDATE_MOVE_BY_PISTON); +- level.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, actualState, toPushShapes.get(i), direction, extending, false)); + // Paper start - fix a variety of piston desync dupes + if (!allowDesync) { -+ blockState1 = level.getBlockState(oldPos); -+ map.replace(oldPos, blockState1); ++ blockState = level.getBlockState(oldPos); ++ deleteAfterMove.replace(oldPos, blockState); + } -+ level.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockPos2, blockState2, allowDesync ? list.get(i1) : blockState1, facing, extending, false)); ++ level.setBlockEntity( ++ MovingPistonBlock.newMovingBlockEntity(pos, actualState, allowDesync ? toPushShapes.get(i) : blockState, direction, extending, false) ++ ); + if (!allowDesync) { + level.setBlock(oldPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_MOVE_BY_PISTON | Block.UPDATE_SKIP_ON_PLACE); // set air to prevent later physics updates from seeing this block + } + // Paper end - fix a variety of piston desync dupes - blockStates[i++] = blockState1; + toUpdate[updateIndex++] = blockState; } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java.patch index aaec6b4a7586..7886dd0655bd 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java.patch @@ -9,12 +9,12 @@ private boolean extending = false; private boolean isSourcePiston = false; private static final ThreadLocal NOCLIP = ThreadLocal.withInitial(() -> null); -@@ -309,7 +_,7 @@ +@@ -319,7 +_,7 @@ if (level.getBlockState(pos).is(Blocks.MOVING_PISTON)) { - BlockState blockState = Block.updateFromNeighbourShapes(blockEntity.movedState, level, pos); - if (blockState.isAir()) { -- level.setBlock(pos, blockEntity.movedState, Block.UPDATE_NONE | Block.UPDATE_MOVE_BY_PISTON | Block.UPDATE_KNOWN_SHAPE); -+ level.setBlock(pos, blockEntity.movedState, Block.UPDATE_NONE | Block.UPDATE_MOVE_BY_PISTON | Block.UPDATE_KNOWN_SHAPE | (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPistonDuplication ? 0 : Block.UPDATE_CLIENTS)); // Paper - fix a variety of piston desync dupes; force notify (flag 2), it's possible the set type by the piston block (which doesn't notify) set this block to air - Block.updateOrDestroy(blockEntity.movedState, blockState, level, pos, Block.UPDATE_ALL); + BlockState newState = Block.updateFromNeighbourShapes(entity.movedState, level, pos); + if (newState.isAir()) { +- level.setBlock(pos, entity.movedState, Block.UPDATE_NONE | Block.UPDATE_MOVE_BY_PISTON | Block.UPDATE_KNOWN_SHAPE); ++ level.setBlock(pos, entity.movedState, Block.UPDATE_NONE | Block.UPDATE_MOVE_BY_PISTON | Block.UPDATE_KNOWN_SHAPE | (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPistonDuplication ? 0 : Block.UPDATE_CLIENTS)); // Paper - fix a variety of piston desync dupes; force notify (flag 2), it's possible the set type by the piston block (which doesn't notify) set this block to air + Block.updateOrDestroy(entity.movedState, newState, level, pos, Block.UPDATE_ALL); } else { - if (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED)) { + if (newState.hasProperty(BlockStateProperties.WATERLOGGED) && newState.getValue(BlockStateProperties.WATERLOGGED)) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch index bde64a653281..ac55e4ac835f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/world/level/block/state/BlockBehaviour.java +++ b/net/minecraft/world/level/block/state/BlockBehaviour.java -@@ -167,13 +_,20 @@ +@@ -170,15 +_,22 @@ } - protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) { + org.spigotmc.AsyncCatcher.catchOp("block onPlace"); // Spigot } - protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + protected void affectNeighborsAfterRemoval(final BlockState state, final ServerLevel level, final BlockPos pos, final boolean movedByPiston) { } + // CraftBukkit start @@ -16,51 +16,53 @@ + } + // CraftBukkit end + - protected void onExplosionHit(BlockState state, ServerLevel level, BlockPos pos, Explosion explosion, BiConsumer dropConsumer) { + protected void onExplosionHit( + final BlockState state, final ServerLevel level, final BlockPos pos, final Explosion explosion, final BiConsumer onHit + ) { - if (!state.isAir() && explosion.getBlockInteraction() != Explosion.BlockInteraction.TRIGGER_BLOCK) { + if (!state.isAir() && explosion.getBlockInteraction() != Explosion.BlockInteraction.TRIGGER_BLOCK && state.isDestroyable()) { // Paper - Protect Bedrock and End Portal/Frames from being destroyed Block block = state.getBlock(); - boolean flag = explosion.getIndirectSourceEntity() instanceof Player; + boolean doDropExperienceHack = explosion.getIndirectSourceEntity() instanceof Player; if (block.dropFromExplosion(explosion)) { -@@ -183,8 +_,10 @@ +@@ -188,8 +_,10 @@ .withParameter(LootContextParams.TOOL, ItemStack.EMPTY) .withOptionalParameter(LootContextParams.BLOCK_ENTITY, blockEntity) .withOptionalParameter(LootContextParams.THIS_ENTITY, explosion.getDirectSourceEntity()); - if (explosion.getBlockInteraction() == Explosion.BlockInteraction.DESTROY_WITH_DECAY) { -- builder.withParameter(LootContextParams.EXPLOSION_RADIUS, explosion.radius()); +- params.withParameter(LootContextParams.EXPLOSION_RADIUS, explosion.radius()); + // CraftBukkit start - add yield + if (explosion instanceof net.minecraft.world.level.ServerExplosion serverExplosion && serverExplosion.yield < 1.0F) { -+ builder.withParameter(LootContextParams.EXPLOSION_RADIUS, 1.0F / serverExplosion.yield); ++ params.withParameter(LootContextParams.EXPLOSION_RADIUS, 1.0F / serverExplosion.yield); + // CraftBukkit end } - state.spawnAfterBreak(level, pos, ItemStack.EMPTY, flag); -@@ -256,7 +_,7 @@ + state.spawnAfterBreak(level, pos, ItemStack.EMPTY, doDropExperienceHack); +@@ -269,7 +_,7 @@ } - protected boolean canBeReplaced(BlockState state, BlockPlaceContext useContext) { -- return state.canBeReplaced() && (useContext.getItemInHand().isEmpty() || !useContext.getItemInHand().is(this.asItem())); -+ return state.canBeReplaced() && (useContext.getItemInHand().isEmpty() || !useContext.getItemInHand().is(this.asItem())) && (state.isDestroyable() || (useContext.getPlayer() != null && useContext.getPlayer().getAbilities().instabuild)); // Paper - Protect Bedrock and End Portal/Frames from being destroyed + protected boolean canBeReplaced(final BlockState state, final BlockPlaceContext context) { +- return state.canBeReplaced() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem())); ++ return state.canBeReplaced() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem())) && (state.isDestroyable() || (context.getPlayer() != null && context.getPlayer().getAbilities().instabuild)); // Paper - Protect Bedrock and End Portal/Frames from being destroyed } - protected boolean canBeReplaced(BlockState state, Fluid fluid) { -@@ -466,6 +_,15 @@ + protected boolean canBeReplaced(final BlockState state, final Fluid fluid) { +@@ -484,6 +_,15 @@ this.instrument = properties.instrument; this.replaceable = properties.replaceable; } + // Paper start - Perf: impl cached craft block data, lazy load to fix issue with loading at the wrong time -+ private org.bukkit.craftbukkit.block.data.@Nullable CraftBlockData cachedCraftBlockData; ++ private org.bukkit.craftbukkit.block.data.@Nullable CraftBlockData cachedBlockData; + -+ public org.bukkit.craftbukkit.block.data.CraftBlockData createCraftBlockData() { -+ if (this.cachedCraftBlockData == null) this.cachedCraftBlockData = org.bukkit.craftbukkit.block.data.CraftBlockData.createData(this.asState()); -+ return (org.bukkit.craftbukkit.block.data.CraftBlockData) this.cachedCraftBlockData.clone(); ++ public org.bukkit.craftbukkit.block.data.CraftBlockData asBlockData() { ++ if (this.cachedBlockData == null) this.cachedBlockData = org.bukkit.craftbukkit.block.data.CraftBlockData.createData(this.asState()); ++ return (org.bukkit.craftbukkit.block.data.CraftBlockData) this.cachedBlockData.clone(); + } + // Paper end - Perf: impl cached craft block data, lazy load to fix issue with loading at the wrong time + private boolean calculateSolid() { if (this.owner.properties.forceSolidOn) { -@@ -485,12 +_,14 @@ +@@ -503,12 +_,14 @@ } } @@ -75,7 +77,7 @@ this.legacySolid = this.calculateSolid(); this.occlusionShape = this.canOcclude ? this.owner.getOcclusionShape(this.asState()) : Shapes.empty(); -@@ -529,6 +_,11 @@ +@@ -548,6 +_,11 @@ public boolean isSolid() { return this.legacySolid; } @@ -85,9 +87,9 @@ + } + // Paper end - Protect Bedrock and End Portal/Frames from being destroyed - public boolean isValidSpawn(BlockGetter level, BlockPos pos, EntityType entityType) { - return this.getBlock().properties.isValidSpawn.test(this.asState(), level, pos, entityType); -@@ -550,19 +_,19 @@ + public boolean isValidSpawn(final BlockGetter level, final BlockPos pos, final EntityType type) { + return this.getBlock().properties.isValidSpawn.test(this.asState(), level, pos, type); +@@ -569,19 +_,19 @@ return this.occlusionShape; } @@ -112,7 +114,16 @@ return this.isAir; } -@@ -632,14 +_,14 @@ +@@ -595,7 +_,7 @@ + } + + public MapColor getMapColor(final BlockGetter level, final BlockPos pos) { +- return this.mapColor; ++ return this.mapColor; // Paper - diff on change - ensure level and pos are never used for CraftBlockData#getMapColor + } + + public BlockState rotate(final Rotation rotation) { +@@ -651,14 +_,14 @@ } public PushReaction getPistonPushReaction() { @@ -129,10 +140,10 @@ return this.canOcclude; } -@@ -726,7 +_,13 @@ +@@ -749,7 +_,13 @@ } - public void onPlace(Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + public void onPlace(final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) { - this.getBlock().onPlace(this.asState(), level, pos, oldState, movedByPiston); + // CraftBukkit start + this.onPlace(level, pos, oldState, movedByPiston, null); @@ -143,17 +154,17 @@ + // CraftBukkit end } - public void affectNeighborsAfterRemoval(ServerLevel level, BlockPos pos, boolean movedByPiston) { -@@ -751,6 +_,7 @@ + public void affectNeighborsAfterRemoval(final ServerLevel level, final BlockPos pos, final boolean movedByPiston) { +@@ -776,6 +_,7 @@ - public void spawnAfterBreak(ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { - this.getBlock().spawnAfterBreak(this.asState(), level, pos, stack, dropExperience); -+ if (dropExperience) {this.getBlock().popExperience(level, pos, this.getBlock().getExpDrop(this.asState(), level, pos, stack, true));} // Paper - Properly handle xp dropping + public void spawnAfterBreak(final ServerLevel level, final BlockPos pos, final ItemStack tool, final boolean dropExperience) { + this.getBlock().spawnAfterBreak(this.asState(), level, pos, tool, dropExperience); ++ if (dropExperience) {this.getBlock().popExperience(level, pos, this.getBlock().getExpDrop(this.asState(), level, pos, tool, true));} // Paper - Properly handle xp dropping } - public List getDrops(LootParams.Builder lootParams) { -@@ -857,11 +_,11 @@ - return this.getBlock().builtInRegistryHolder().is(block); + public List getDrops(final LootParams.Builder params) { +@@ -860,11 +_,11 @@ + return this.getBlock() instanceof EntityBlock ? ((EntityBlock)this.getBlock()).getTicker(level, this.asState(), type) : null; } - public FluidState getFluidState() { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockState.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockState.java.patch index d13dde221670..7164d1227d06 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockState.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockState.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/block/state/BlockState.java +++ b/net/minecraft/world/level/block/state/BlockState.java -@@ -10,6 +_,17 @@ +@@ -8,6 +_,17 @@ public class BlockState extends BlockBehaviour.BlockStateBase { - public static final Codec CODEC = codec(BuiltInRegistries.BLOCK.byNameCodec(), Block::defaultBlockState).stable(); + public static final Codec CODEC = codec(BuiltInRegistries.BLOCK.byNameCodec(), Block::defaultBlockState, Block::getStateDefinition).stable(); + // Paper start - optimise getType calls -+ @javax.annotation.Nullable org.bukkit.Material cachedMaterial; ++ org.bukkit.@org.jspecify.annotations.Nullable Material cachedMaterial; + + public final org.bukkit.Material getBukkitMaterial() { + if (this.cachedMaterial == null) { @@ -15,6 +15,6 @@ + } + // Paper end - optimise getType calls + - public BlockState(Block owner, Reference2ObjectArrayMap, Comparable> values, MapCodec propertiesCodec) { - super(owner, values, propertiesCodec); + public BlockState(final Block owner, final Property[] propertyKeys, final Comparable[] propertyValues) { + super(owner, propertyKeys, propertyValues); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/state/StateHolder.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/state/StateHolder.java.patch new file mode 100644 index 000000000000..7770ed3eef7b --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/state/StateHolder.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/state/StateHolder.java ++++ b/net/minecraft/world/level/block/state/StateHolder.java +@@ -142,7 +_,7 @@ + return length == 0 ? Stream.empty() : IntStream.range(0, length).mapToObj(i -> createValue(this.propertyKeys[i], this.propertyValues[i])); + } + +- private static > Property.Value createValue(final Property propertyKey, final Comparable propertyValue) { ++ public static > Property.Value createValue(final Property propertyKey, final Comparable propertyValue) { // Paper - public + return new Property.Value<>(propertyKey, (T)propertyValue); + } + diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/EnumProperty.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/EnumProperty.java.patch index 94537a6a1d4b..d4296e9e0b13 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/EnumProperty.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/EnumProperty.java.patch @@ -5,8 +5,8 @@ } - @Override -- public boolean equals(Object other) { -+ public boolean equals_unused(Object other) { // Paper - Perf: Optimize hashCode/equals - return this == other || other instanceof EnumProperty enumProperty && super.equals(other) && this.values.equals(enumProperty.values); +- public boolean equals(final Object o) { ++ public boolean equals_unused(final Object o) { // Paper - Perf: Optimize hashCode/equals + return this == o || o instanceof EnumProperty that && super.equals(o) && this.values.equals(that.values); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/IntegerProperty.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/IntegerProperty.java.patch index a3b33fce2067..93d336613a43 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/IntegerProperty.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/IntegerProperty.java.patch @@ -5,8 +5,8 @@ } - @Override -- public boolean equals(Object other) { -+ public boolean equals_unused(Object other) { // Paper - Perf: Optimize hashCode/equals - return this == other || other instanceof IntegerProperty integerProperty && super.equals(other) && this.values.equals(integerProperty.values); +- public boolean equals(final Object o) { ++ public boolean equals_unused(final Object o) { // Paper - Perf: Optimize hashCode/equals + return this == o || o instanceof IntegerProperty that && super.equals(o) && this.values.equals(that.values); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/Property.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/Property.java.patch index 9087bd5e8570..0afc95954a3b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/Property.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/state/properties/Property.java.patch @@ -3,9 +3,9 @@ @@ -71,7 +_,7 @@ @Override - public boolean equals(Object other) { -- return this == other || other instanceof Property property && this.clazz.equals(property.clazz) && this.name.equals(property.name); -+ return this == other; // Paper - Perf: Optimize hashCode/equals + public boolean equals(final Object o) { +- return this == o || o instanceof Property that && this.clazz.equals(that.clazz) && this.name.equals(that.name); ++ return this == o; // Paper - Perf: Optimize hashCode/equals } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/border/WorldBorder.java.patch b/paper-server/patches/sources/net/minecraft/world/level/border/WorldBorder.java.patch index 33463ef911fb..6a0b645686f7 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/border/WorldBorder.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/border/WorldBorder.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/world/level/border/WorldBorder.java +++ b/net/minecraft/world/level/border/WorldBorder.java -@@ -33,6 +_,8 @@ - double centerZ; - int absoluteMaxSize = 29999984; - WorldBorder.BorderExtent extent = new WorldBorder.StaticBorderExtent(5.999997E7F); +@@ -37,6 +_,8 @@ + private double centerZ; + private int absoluteMaxSize = 29999984; + private WorldBorder.BorderExtent extent = new WorldBorder.StaticBorderExtent(5.999997E7F); + public net.minecraft.server.level.@org.jspecify.annotations.Nullable ServerLevel world; // CraftBukkit + private int lastTick = -1; // Paper - Prevent ticking virtual world borders multiple times per server tick public WorldBorder() { this(WorldBorder.Settings.DEFAULT); -@@ -54,6 +_,20 @@ - return this.isWithinBounds(chunkPos.getMinBlockX(), chunkPos.getMinBlockZ()) && this.isWithinBounds(chunkPos.getMaxBlockX(), chunkPos.getMaxBlockZ()); +@@ -58,6 +_,20 @@ + return this.isWithinBounds(pos.getMinBlockX(), pos.getMinBlockZ()) && this.isWithinBounds(pos.getMaxBlockX(), pos.getMaxBlockZ()); } + // Paper start - Bound treasure maps to world border @@ -27,16 +27,18 @@ + } + // Paper end - Bound treasure maps to world border + - public boolean isWithinBounds(AABB box) { - return this.isWithinBounds(box.minX, box.minZ, box.maxX - 1.0E-5F, box.maxZ - 1.0E-5F); + public boolean isWithinBounds(final AABB aabb) { + return this.isWithinBounds(aabb.minX, aabb.minZ, aabb.maxX - 1.0E-5F, aabb.maxZ - 1.0E-5F); } -@@ -158,6 +_,14 @@ +@@ -161,7 +_,15 @@ + return this.centerZ; } - public void setCenter(double x, double z) { -+ // Paper start - Add worldborder events +- public void setCenter(final double x, final double z) { ++ // Paper start - Add worldborder events ++ public void setCenter(double x, double z) { + if (this.world != null) { -+ io.papermc.paper.event.world.border.WorldBorderCenterChangeEvent event = new io.papermc.paper.event.world.border.WorldBorderCenterChangeEvent(world.getWorld(), world.getWorld().getWorldBorder(), new org.bukkit.Location(world.getWorld(), this.getCenterX(), 0, this.getCenterZ()), new org.bukkit.Location(world.getWorld(), x, 0, z)); ++ io.papermc.paper.event.world.border.WorldBorderCenterChangeEvent event = new io.papermc.paper.event.world.border.WorldBorderCenterChangeEvent(this.world.getWorld(), this.world.getWorld().getWorldBorder(), new org.bukkit.Location(this.world.getWorld(), this.getCenterX(), 0, this.getCenterZ()), new org.bukkit.Location(this.world.getWorld(), x, 0, z)); + if (!event.callEvent()) return; + x = event.getNewCenter().getX(); + z = event.getNewCenter().getZ(); @@ -45,13 +47,15 @@ this.centerX = x; this.centerZ = z; this.extent.onCenterChange(); -@@ -181,6 +_,17 @@ +@@ -184,7 +_,18 @@ + return this.extent.getLerpTarget(); } - public void setSize(double size) { -+ // Paper start - Add worldborder events +- public void setSize(final double size) { ++ // Paper start - Add worldborder events ++ public void setSize(double size) { + if (this.world != null) { -+ io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent event = new io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent(world.getWorld(), world.getWorld().getWorldBorder(), io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent.Type.INSTANT_MOVE, getSize(), size, 0); ++ io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent event = new io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent(this.world.getWorld(), this.world.getWorld().getWorldBorder(), io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent.Type.INSTANT_MOVE, getSize(), size, 0); + if (!event.callEvent()) return; + if (event.getType() == io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent.Type.STARTED_MOVE && event.getDurationTicks() > 0) { // If changed to a timed transition + lerpSizeBetween(event.getOldSize(), event.getNewSize(), event.getDurationTicks(), this.world.getGameTime()); @@ -63,36 +67,38 @@ this.extent = new WorldBorder.StaticBorderExtent(size); this.setDirty(); -@@ -190,6 +_,20 @@ +@@ -193,7 +_,21 @@ + } } - public void lerpSizeBetween(double oldSize, double newSize, long time, long startTime) { -+ // Paper start - Add worldborder events +- public void lerpSizeBetween(final double from, final double to, final long ticks, final long gameTime) { ++ // Paper start - Add worldborder events ++ public void lerpSizeBetween(final double from, double to, long ticks, final long gameTime) { + if (this.world != null) { + io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent.Type type; -+ if (oldSize == newSize) { // new size = old size ++ if (from == to) { + type = io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent.Type.INSTANT_MOVE; // Use INSTANT_MOVE because below it creates a Static border if they are equal. + } else { + type = io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent.Type.STARTED_MOVE; + } -+ io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent event = new io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent(world.getWorld(), world.getWorld().getWorldBorder(), type, oldSize, newSize, time); ++ io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent event = new io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent(this.world.getWorld(), this.world.getWorld().getWorldBorder(), type, from, to, ticks); + if (!event.callEvent()) return; -+ newSize = event.getNewSize(); -+ time = event.getDurationTicks(); ++ to = event.getNewSize(); ++ ticks = event.getDurationTicks(); + } + // Paper end - Add worldborder events - this.extent = (WorldBorder.BorderExtent)(oldSize == newSize - ? new WorldBorder.StaticBorderExtent(newSize) - : new WorldBorder.MovingBorderExtent(oldSize, newSize, time, startTime)); -@@ -205,6 +_,7 @@ + this.extent = (WorldBorder.BorderExtent)(from == to + ? new WorldBorder.StaticBorderExtent(to) + : new WorldBorder.MovingBorderExtent(from, to, ticks, gameTime)); +@@ -209,6 +_,7 @@ } - public void addListener(BorderChangeListener listener) { + public void addListener(final BorderChangeListener listener) { + if (this.listeners.contains(listener)) return; // CraftBukkit this.listeners.add(listener); } -@@ -278,6 +_,12 @@ +@@ -282,6 +_,12 @@ } public void tick() { @@ -105,34 +111,34 @@ this.extent = this.extent.update(); } -@@ -298,6 +_,22 @@ +@@ -302,6 +_,22 @@ } } + // Paper start - add back applySettings -+ public void applySettings(net.minecraft.world.level.border.WorldBorder.Settings settings) { ++ public void applySettings(WorldBorder.Settings settings) { + this.setCenter(settings.centerX(), settings.centerZ()); + this.setDamagePerBlock(settings.damagePerBlock()); + this.setSafeZone(settings.safeZone()); + this.setWarningBlocks(settings.warningBlocks()); + this.setWarningTime(settings.warningTime()); + if (settings.lerpTime() > 0L) { -+ final long startTime = (this.world != null) ? this.world.getGameTime() : 0; // Virtual Borders don't have a World -+ this.lerpSizeBetween(settings.size(), settings.lerpTarget(), settings.lerpTime(), startTime); ++ final long gameTime = (this.world != null) ? this.world.getGameTime() : 0; // Virtual Borders don't have a World ++ this.lerpSizeBetween(settings.size(), settings.lerpTarget(), settings.lerpTime(), gameTime); + } else { + this.setSize(settings.size()); + } + } + // Paper end - add back applySettings + - interface BorderExtent { - double getMinX(float partialTick); + private interface BorderExtent { + double getMinX(final float deltaPartialTick); -@@ -432,6 +_,7 @@ +@@ -438,6 +_,7 @@ this.previousSize = this.size; this.size = this.calculateSize(); if (this.lerpProgress <= 0L) { -+ if (world != null) new io.papermc.paper.event.world.border.WorldBorderBoundsChangeFinishEvent(world.getWorld(), world.getWorld().getWorldBorder(), this.from, this.to, this.lerpDuration).callEvent(); // Paper - Add worldborder events ++ if (WorldBorder.this.world != null) new io.papermc.paper.event.world.border.WorldBorderBoundsChangeFinishEvent(WorldBorder.this.world.getWorld(), WorldBorder.this.world.getWorld().getWorldBorder(), this.from, this.to, this.lerpDuration).callEvent(); // Paper - Add worldborder events WorldBorder.this.setDirty(); return WorldBorder.this.new StaticBorderExtent(this.to); } else { diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkAccess.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkAccess.java.patch index 05cc529bf4e7..e04a19590b68 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkAccess.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkAccess.java.patch @@ -13,20 +13,20 @@ public final Map blockEntities = new Object2ObjectOpenHashMap<>(); protected final LevelHeightAccessor levelHeightAccessor; protected final LevelChunkSection[] sections; -+ // CraftBukkit start - SPIGOT-6814: move to IChunkAccess to account for 1.17 to 1.18 chunk upgrading. ++ // CraftBukkit start - SPIGOT-6814: move to ChunkAccess to account for 1.17 to 1.18 chunk upgrading. + private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); + public org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer(ChunkAccess.DATA_TYPE_REGISTRY); + // CraftBukkit end public ChunkAccess( - ChunkPos chunkPos, + final ChunkPos chunkPos, @@ -89,7 +_,8 @@ - LevelChunkSection @Nullable [] sections, - @Nullable BlendingData blendingData + final LevelChunkSection @Nullable [] sections, + final @Nullable BlendingData blendingData ) { - this.chunkPos = chunkPos; -+ this.locX = chunkPos.x; this.locZ = chunkPos.z; // Paper - reduce need for field lookups -+ this.chunkPos = chunkPos; this.coordinateKey = ChunkPos.asLong(locX, locZ); // Paper - cache long key ++ this.locX = chunkPos.x(); this.locZ = chunkPos.z(); // Paper - reduce need for field lookups ++ this.chunkPos = chunkPos; this.coordinateKey = ChunkPos.pack(this.locX, this.locZ); // Paper - cache long key this.upgradeData = upgradeData; this.levelHeightAccessor = levelHeightAccessor; this.sections = new LevelChunkSection[levelHeightAccessor.getSectionsCount()]; @@ -36,7 +36,7 @@ + public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper + - public @Nullable BlockState setBlockState(BlockPos pos, BlockState state) { + public @Nullable BlockState setBlockState(final BlockPos pos, final BlockState state) { return this.setBlockState(pos, state, Block.UPDATE_ALL); } @@ -266,6 +_,7 @@ @@ -56,26 +56,27 @@ } public abstract ChunkStatus getPersistedStatus(); -@@ -438,6 +_,22 @@ - throw new ReportedException(crashReport); +@@ -438,6 +_,23 @@ + throw new ReportedException(report); } } ++ + // CraftBukkit start -+ public void setBiome(int x, int y, int z, Holder biome) { ++ public void setNoiseBiome(int quartX, int quartY, int quartZ, Holder biome) { + try { -+ int minY = QuartPos.fromBlock(this.getMinY()); -+ int maxY = minY + QuartPos.fromBlock(this.getHeight()) - 1; -+ int clampedY = Mth.clamp(y, minY, maxY); -+ int sectionIndex = this.getSectionIndex(QuartPos.toBlock(clampedY)); -+ this.sections[sectionIndex].setBiome(x & 3, clampedY & 3, z & 3, biome); ++ int quartMinY = QuartPos.fromBlock(this.getMinY()); ++ int quartMaxY = quartMinY + QuartPos.fromBlock(this.getHeight()) - 1; ++ int clampedQuartY = Mth.clamp(quartY, quartMinY, quartMaxY); ++ int sectionIndex = this.getSectionIndex(QuartPos.toBlock(clampedQuartY)); ++ this.sections[sectionIndex].setNoiseBiome(quartX & 3, clampedQuartY & 3, quartZ & 3, biome); + } catch (Throwable throwable) { + CrashReport report = CrashReport.forThrowable(throwable, "Setting biome"); -+ CrashReportCategory reportCategory = report.addCategory("Biome being set"); -+ reportCategory.setDetail("Location", () -> CrashReportCategory.formatLocation(this, x, y, z)); ++ CrashReportCategory category = report.addCategory("Biome being set"); ++ category.setDetail("Location", () -> CrashReportCategory.formatLocation(this, quartX, quartY, quartZ)); + throw new ReportedException(report); + } + } + // CraftBukkit end - public void fillBiomesFromNoise(BiomeResolver resolver, Climate.Sampler sampler) { + public void fillBiomesFromNoise(final BiomeResolver biomeResolver, final Climate.Sampler sampler) { ChunkPos pos = this.getPos(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch index 376a148b6da3..44a2ccd05e44 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch @@ -1,104 +1,108 @@ --- a/net/minecraft/world/level/chunk/ChunkGenerator.java +++ b/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -104,8 +_,8 @@ +@@ -105,8 +_,8 @@ protected abstract MapCodec codec(); -- public ChunkGeneratorStructureState createState(HolderLookup structureSetLookup, RandomState randomState, long seed) { -- return ChunkGeneratorStructureState.createForNormal(randomState, seed, this.biomeSource, structureSetLookup); -+ public ChunkGeneratorStructureState createState(HolderLookup structureSetLookup, RandomState randomState, long seed, org.spigotmc.SpigotWorldConfig conf) { // Spigot -+ return ChunkGeneratorStructureState.createForNormal(randomState, seed, this.biomeSource, structureSetLookup, conf); // Spigot +- public ChunkGeneratorStructureState createState(final HolderLookup structureSets, final RandomState randomState, final long legacyLevelSeed) { +- return ChunkGeneratorStructureState.createForNormal(randomState, legacyLevelSeed, this.biomeSource, structureSets); ++ public ChunkGeneratorStructureState createState(final HolderLookup structureSets, final RandomState randomState, final long legacyLevelSeed, final org.spigotmc.SpigotWorldConfig conf) { // Spigot ++ return ChunkGeneratorStructureState.createForNormal(randomState, legacyLevelSeed, this.biomeSource, structureSets, conf); // Spigot } - public Optional>> getTypeNameForDataFixer() { -@@ -129,6 +_,24 @@ + public Optional getTypeNameForDataFixer() { +@@ -127,11 +_,29 @@ + ); + + public @Nullable Pair> findNearestMapStructure( +- final ServerLevel level, final HolderSet wantedStructures, final BlockPos pos, final int maxSearchRadius, final boolean createReference ++ final ServerLevel level, HolderSet wantedStructures, BlockPos pos, int maxSearchRadius, boolean createReference + ) { if (SharedConstants.DEBUG_DISABLE_FEATURES) { return null; } else { + // Paper start - StructuresLocateEvent + final org.bukkit.World bukkitWorld = level.getWorld(); + final org.bukkit.Location origin = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level); -+ final List apiStructures = structure.stream().map(org.bukkit.craftbukkit.generator.structure.CraftStructure::minecraftHolderToBukkit).toList(); ++ final List apiStructures = wantedStructures.stream().map(org.bukkit.craftbukkit.generator.structure.CraftStructure::minecraftHolderToBukkit).toList(); + if (!apiStructures.isEmpty()) { -+ final io.papermc.paper.event.world.StructuresLocateEvent event = new io.papermc.paper.event.world.StructuresLocateEvent(bukkitWorld, origin, apiStructures, radius, skipKnownStructures); ++ final io.papermc.paper.event.world.StructuresLocateEvent event = new io.papermc.paper.event.world.StructuresLocateEvent(bukkitWorld, origin, apiStructures, maxSearchRadius, createReference); + if (!event.callEvent()) { + return null; + } + if (event.getResult() != null) { + return Pair.of(io.papermc.paper.util.MCUtil.toBlockPos(event.getResult().pos()), org.bukkit.craftbukkit.generator.structure.CraftStructure.bukkitToMinecraftHolder(event.getResult().structure())); + } -+ pos = org.bukkit.craftbukkit.util.CraftLocation.toBlockPosition(event.getOrigin()); -+ radius = event.getRadius(); -+ skipKnownStructures = event.shouldFindUnexplored(); -+ structure = HolderSet.direct(org.bukkit.craftbukkit.generator.structure.CraftStructure::bukkitToMinecraftHolder, event.getStructures()); ++ pos = org.bukkit.craftbukkit.util.CraftLocation.toBlockPos(event.getOrigin()); ++ maxSearchRadius = event.getRadius(); ++ createReference = event.shouldFindUnexplored(); ++ wantedStructures = HolderSet.direct(org.bukkit.craftbukkit.generator.structure.CraftStructure::bukkitToMinecraftHolder, event.getStructures()); + } + // Paper end ChunkGeneratorStructureState generatorState = level.getChunkSource().getGeneratorState(); - Map>> map = new Object2ObjectArrayMap<>(); + Map>> placementScans = new Object2ObjectArrayMap<>(); -@@ -223,6 +_,7 @@ - BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); +@@ -226,6 +_,7 @@ + BlockPos.MutableBlockPos structurePos = new BlockPos.MutableBlockPos(); - for (ChunkPos chunkPos : ringPositionsFor) { -+ if (!level.paperConfig().environment.locateStructuresOutsideWorldBorder && !level.getWorldBorder().isChunkInBounds(chunkPos.x, chunkPos.z)) { continue; } // Paper - Bound treasure maps to world border - mutableBlockPos.set(SectionPos.sectionToBlockCoord(chunkPos.x, 8), 32, SectionPos.sectionToBlockCoord(chunkPos.z, 8)); - double d1 = mutableBlockPos.distSqr(pos); - boolean flag = pair == null || d1 < d; -@@ -255,11 +_,15 @@ - int spacing = spreadPlacement.spacing(); + for (ChunkPos chunkPos : positions) { ++ if (!level.paperConfig().environment.locateStructuresOutsideWorldBorder && !level.getWorldBorder().isChunkInBounds(chunkPos.x(), chunkPos.z())) { continue; } // Paper - Bound treasure maps to world border + structurePos.set(SectionPos.sectionToBlockCoord(chunkPos.x(), 8), 32, SectionPos.sectionToBlockCoord(chunkPos.z(), 8)); + double distSqr = structurePos.distSqr(pos); + boolean isClosest = closestPos == null || distSqr < closest; +@@ -260,9 +_,12 @@ + for (int x = -radius; x <= radius; x++) { + boolean xEdge = x == -radius || x == radius; - for (int i = -z; i <= z; i++) { -- boolean flag = i == -z || i == z; +- for (int z = -radius; z <= radius; z++) { +- boolean zEdge = z == -radius || z == radius; +- if (xEdge || zEdge) { + // Paper start - Perf: iterate over border chunks instead of entire square chunk area -+ final int radius = z; -+ boolean flag = i == -z || i == z; final boolean onBorderAlongZAxis = flag; // Paper - OBFHELPER - -- for (int i1 = -z; i1 <= z; i1++) { -- boolean flag1 = i1 == -z || i1 == z; -- if (flag || flag1) { -+ for (int i1 = -radius; i1 <= radius; i1 += onBorderAlongZAxis ? 1 : radius * 2) { -+ // boolean flag1 = i1 == -z || i1 == z; -+ // if (flag || flag1) { ++ for (int z = -radius; z <= radius; z += xEdge ? 1 : radius * 2) { ++ // boolean zEdge = z == -radius || z == radius; ++ // if (xEdge || zEdge) { + if (true) { -+ // Paper end - Perf: iterate over border chunks instead of entire square chunk area - int i2 = x + spacing * i; - int i3 = y + spacing * i1; - ChunkPos potentialStructureChunk = spreadPlacement.getPotentialStructureChunk(seed, i2, i3); -@@ -311,7 +_,7 @@ ++ // Paper end - Perf: iterate over border chunks instead of entire square chunk area + int sectorX = chunkOriginX + spacing * x; + int sectorZ = chunkOriginZ + spacing * z; + ChunkPos chunkTarget = config.getPotentialStructureChunk(seed, sectorX, sectorZ); +@@ -314,7 +_,7 @@ } } -- public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) { -+ public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) { // CraftBukkit - rename - ChunkPos pos = chunk.getPos(); - if (!SharedConstants.debugVoidTerrain(pos)) { - SectionPos sectionPos = SectionPos.of(pos, level.getMinSectionY()); -@@ -382,7 +_,14 @@ - int i3 = ints[i2]; - PlacedFeature placedFeature = stepFeatureData1.features().get(i3); - Supplier supplier1 = () -> registry1.getResourceKey(placedFeature).map(Object::toString).orElseGet(placedFeature::toString); -- worldgenRandom.setFeatureSeed(l, i3, i); +- public void applyBiomeDecoration(final WorldGenLevel level, final ChunkAccess chunk, final StructureManager structureManager) { ++ public void addVanillaDecorations(final WorldGenLevel level, final ChunkAccess chunk, final StructureManager structureManager) { // CraftBukkit - rename + ChunkPos centerPos = chunk.getPos(); + if (!SharedConstants.debugVoidTerrain(centerPos)) { + SectionPos sectionPos = SectionPos.of(centerPos, level.getMinSectionY()); +@@ -388,7 +_,14 @@ + Supplier currentlyGenerating = () -> featureRegistry.getResourceKey(feature) + .map(Object::toString) + .orElseGet(feature::toString); +- random.setFeatureSeed(decorationSeed, globalIndexOfFeature, stepIndex); + // Paper start - Configurable feature seeds; change populationSeed used in random -+ long featurePopulationSeed = l; -+ final long configFeatureSeed = level.getMinecraftWorld().paperConfig().featureSeeds.features.getLong(placedFeature.feature()); ++ long featurePopulationSeed = decorationSeed; ++ final long configFeatureSeed = level.getMinecraftWorld().paperConfig().featureSeeds.features.getLong(feature.feature()); + if (configFeatureSeed != -1) { -+ featurePopulationSeed = worldgenRandom.setDecorationSeed(configFeatureSeed, blockPos.getX(), blockPos.getZ()); // See WorldgenRandom.setDecorationSeed from above ++ featurePopulationSeed = random.setDecorationSeed(configFeatureSeed, origin.getX(), origin.getZ()); // See WorldgenRandom.setDecorationSeed from above + } -+ worldgenRandom.setFeatureSeed(featurePopulationSeed, i3, i); ++ random.setFeatureSeed(featurePopulationSeed, globalIndexOfFeature, stepIndex); + // Paper end - Configurable feature seeds try { - level.setCurrentlyGenerating(supplier1); -@@ -407,6 +_,32 @@ + level.setCurrentlyGenerating(currentlyGenerating); +@@ -416,6 +_,34 @@ } } } -+ // CraftBukkit start -+ public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) { ++ // CraftBukkit start ++ public void applyBiomeDecoration(final WorldGenLevel level, final ChunkAccess chunk, final StructureManager structureManager) { + this.applyBiomeDecoration(level, chunk, structureManager, true); + } + -+ public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager, boolean addVanillaDecorations) { ++ public void applyBiomeDecoration( ++ final WorldGenLevel level, final ChunkAccess chunk, final StructureManager structureManager, final boolean addVanillaDecorations ++ ) { + if (addVanillaDecorations) { + this.addVanillaDecorations(level, chunk, structureManager); + } @@ -107,8 +111,8 @@ + // only call when a populator is present (prevents unnecessary entity conversion) + if (!world.getPopulators().isEmpty()) { + org.bukkit.craftbukkit.generator.CraftLimitedRegion limitedRegion = new org.bukkit.craftbukkit.generator.CraftLimitedRegion(level, chunk.getPos()); -+ int x = chunk.getPos().x; -+ int z = chunk.getPos().z; ++ int x = chunk.getPos().x(); ++ int z = chunk.getPos().z(); + for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) { + WorldgenRandom seededrandom = new WorldgenRandom(new net.minecraft.world.level.levelgen.LegacyRandomSource(level.getSeed())); + seededrandom.setDecorationSeed(level.getSeed(), x, z); @@ -120,29 +124,29 @@ + } + // CraftBukkit end - private static BoundingBox getWritableArea(ChunkAccess chunk) { - ChunkPos pos = chunk.getPos(); -@@ -484,7 +_,7 @@ + private static BoundingBox getWritableArea(final ChunkAccess chunk) { + ChunkPos chunkPos = chunk.getPos(); +@@ -495,7 +_,7 @@ } } -- if (structurePlacement.isStructureChunk(structureState, pos.x, pos.z)) { -+ if (structurePlacement.isStructureChunk(structureState, pos.x, pos.z, structurePlacement instanceof net.minecraft.world.level.chunk.ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs - if (list.size() == 1) { +- if (featurePlacement.isStructureChunk(state, sourceChunkPos.x(), sourceChunkPos.z())) { ++ if (featurePlacement.isStructureChunk(state, sourceChunkPos.x(), sourceChunkPos.z(), featurePlacement instanceof net.minecraft.world.level.chunk.ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs + if (structures.size() == 1) { this.tryGenerateStructure( - list.get(0), -@@ -579,6 +_,14 @@ - predicate + structures.get(0), +@@ -590,6 +_,14 @@ + biomePredicate ); - if (structureStart.isValid()) { + if (start.isValid()) { + // CraftBukkit start -+ BoundingBox box = structureStart.getBoundingBox(); -+ org.bukkit.event.world.AsyncStructureSpawnEvent event = new org.bukkit.event.world.AsyncStructureSpawnEvent(structureManager.level.getMinecraftWorld().getWorld(), org.bukkit.craftbukkit.generator.structure.CraftStructure.minecraftToBukkit(structure), new org.bukkit.util.BoundingBox(box.minX(), box.minY(), box.minZ(), box.maxX(), box.maxY(), box.maxZ()), chunkPos.x, chunkPos.z); ++ BoundingBox box = start.getBoundingBox(); ++ org.bukkit.event.world.AsyncStructureSpawnEvent event = new org.bukkit.event.world.AsyncStructureSpawnEvent(structureManager.level.getMinecraftWorld().getWorld(), org.bukkit.craftbukkit.generator.structure.CraftStructure.minecraftToBukkit(structure), new org.bukkit.util.BoundingBox(box.minX(), box.minY(), box.minZ(), box.maxX(), box.maxY(), box.maxZ()), sourceChunkPos.x(), sourceChunkPos.z()); + org.bukkit.Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return true; + } + // CraftBukkit end - structureManager.setStartForStructure(sectionPos, structure, structureStart, chunk); + structureManager.setStartForStructure(sectionPos, structure, start, centerChunk); return true; } else { diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java.patch index 30d517aba8b1..7b2700f09563 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java.patch @@ -7,24 +7,24 @@ + public final org.spigotmc.SpigotWorldConfig conf; // Paper - Add missing structure set seed configs public static ChunkGeneratorStructureState createForFlat( -- RandomState randomState, long levelSeed, BiomeSource biomeSource, Stream> structureSets -+ RandomState randomState, long levelSeed, BiomeSource biomeSource, Stream> structureSets, org.spigotmc.SpigotWorldConfig conf // Spigot +- final RandomState randomState, final long levelSeed, final BiomeSource biomeSource, final Stream> structureOverrides ++ final RandomState randomState, final long levelSeed, final BiomeSource biomeSource, final Stream> structureOverrides, org.spigotmc.SpigotWorldConfig conf // Spigot ) { - List> list = structureSets.filter(structureSet -> hasBiomesForStructureSet(structureSet.value(), biomeSource)).toList(); -- return new ChunkGeneratorStructureState(randomState, biomeSource, levelSeed, 0L, list); -+ return new ChunkGeneratorStructureState(randomState, biomeSource, levelSeed, 0L, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot + List> structures = structureOverrides.filter(structureSet -> hasBiomesForStructureSet(structureSet.value(), biomeSource)).toList(); +- return new ChunkGeneratorStructureState(randomState, biomeSource, levelSeed, 0L, structures); ++ return new ChunkGeneratorStructureState(randomState, biomeSource, levelSeed, 0L, ChunkGeneratorStructureState.injectSpigot(structures, conf), conf); // Spigot } public static ChunkGeneratorStructureState createForNormal( -- RandomState randomState, long seed, BiomeSource biomeSource, HolderLookup structureSetLookup -+ RandomState randomState, long seed, BiomeSource biomeSource, HolderLookup structureSetLookup, org.spigotmc.SpigotWorldConfig conf // Spigot +- final RandomState randomState, final long levelSeed, final BiomeSource biomeSource, final HolderLookup allStructures ++ final RandomState randomState, final long levelSeed, final BiomeSource biomeSource, final HolderLookup allStructures, org.spigotmc.SpigotWorldConfig conf // Spigot ) { - List> list = structureSetLookup.listElements() + List> structures = allStructures.listElements() .filter(structureSet -> hasBiomesForStructureSet(structureSet.value(), biomeSource)) .collect(Collectors.toUnmodifiableList()); -- return new ChunkGeneratorStructureState(randomState, biomeSource, seed, seed, list); +- return new ChunkGeneratorStructureState(randomState, biomeSource, levelSeed, levelSeed, structures); - } -+ return new ChunkGeneratorStructureState(randomState, biomeSource, seed, seed, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot ++ return new ChunkGeneratorStructureState(randomState, biomeSource, levelSeed, levelSeed, ChunkGeneratorStructureState.injectSpigot(structures, conf), conf); // Spigot + } + // Paper start - Add missing structure set seed configs; horrible hack because spigot creates a ton of direct Holders which lose track of the identifying key + public static final class KeyedRandomSpreadStructurePlacement extends net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement { @@ -113,14 +113,14 @@ + } + // Spigot end - private static boolean hasBiomesForStructureSet(StructureSet structureSet, BiomeSource biomeSource) { - Stream> stream = structureSet.structures().stream().flatMap(structureEntry -> { -@@ -67,13 +_,14 @@ - } - - private ChunkGeneratorStructureState( -- RandomState randomState, BiomeSource biomeSource, long levelSeed, long concentricRingsSeed, List> possibleStructureSets -+ RandomState randomState, BiomeSource biomeSource, long levelSeed, long concentricRingsSeed, List> possibleStructureSets, org.spigotmc.SpigotWorldConfig conf // Paper - Add missing structure set seed configs + private static boolean hasBiomesForStructureSet(final StructureSet structureSet, final BiomeSource biomeSource) { + Stream> structureBiomes = structureSet.structures().stream().flatMap(entry -> { +@@ -71,13 +_,14 @@ + final BiomeSource biomeSource, + final long levelSeed, + final long concentricRingsSeed, +- final List> possibleStructureSets ++ final List> possibleStructureSets, org.spigotmc.SpigotWorldConfig conf // Paper - Add missing structure set seed configs ) { this.randomState = randomState; this.levelSeed = levelSeed; @@ -131,26 +131,26 @@ } public List> possibleStructureSets() { -@@ -118,7 +_,13 @@ +@@ -115,7 +_,13 @@ int spread = placement.spread(); - HolderSet holderSet = placement.preferredBiomes(); - RandomSource randomSource = RandomSource.create(); + HolderSet preferredBiomes = placement.preferredBiomes(); + RandomSource random = RandomSource.create(); + // Paper start - Add missing structure set seed configs + if (this.conf.strongholdSeed != null && structureSet.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { -+ randomSource.setSeed(this.conf.strongholdSeed); ++ random.setSeed(this.conf.strongholdSeed); + } else { + // Paper end - Add missing structure set seed configs - randomSource.setSeed(this.concentricRingsSeed); + random.setSeed(this.concentricRingsSeed); + } // Paper - Add missing structure set seed configs - double d = randomSource.nextDouble() * Math.PI * 2.0; - int i = 0; - int i1 = 0; -@@ -196,7 +_,7 @@ + double angle = random.nextDouble() * Math.PI * 2.0; + int positionInCircle = 0; + int circle = 0; +@@ -193,7 +_,7 @@ - for (int i = x - range; i <= x + range; i++) { - for (int i1 = z - range; i1 <= z + range; i1++) { -- if (structurePlacement.isStructureChunk(this, i, i1)) { -+ if (structurePlacement.isStructureChunk(this, i, i1, structurePlacement instanceof KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs + for (int testX = sourceX - range; testX <= sourceX + range; testX++) { + for (int testZ = sourceZ - range; testZ <= sourceZ + range; testZ++) { +- if (placement.isStructureChunk(this, testX, testZ)) { ++ if (placement.isStructureChunk(this, testX, testZ, placement instanceof KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs return true; } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/EmptyLevelChunk.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/EmptyLevelChunk.java.patch index bf030502e8ce..b55910d2927c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/EmptyLevelChunk.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/EmptyLevelChunk.java.patch @@ -12,5 +12,5 @@ + // Paper end + @Override - public @Nullable BlockState setBlockState(BlockPos pos, BlockState state, @Block.UpdateFlags int flags) { + public @Nullable BlockState setBlockState(final BlockPos pos, final BlockState state, @Block.UpdateFlags final int flags) { return null; diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/HashMapPalette.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/HashMapPalette.java.patch index f72ceacf8888..65b0eb71bf7f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/HashMapPalette.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/HashMapPalette.java.patch @@ -3,26 +3,26 @@ @@ -18,7 +_,7 @@ } - public HashMapPalette(int bits) { + public HashMapPalette(final int bits) { - this(bits, CrudeIncrementalIntIdentityHashBiMap.create(1 << bits)); + this(bits, CrudeIncrementalIntIdentityHashBiMap.create((1 << bits) + 1)); // Paper - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap } - private HashMapPalette(int bits, CrudeIncrementalIntIdentityHashBiMap values) { + private HashMapPalette(final int bits, final CrudeIncrementalIntIdentityHashBiMap values) { @@ -34,10 +_,16 @@ - public int idFor(T state, PaletteResize resizeHandler) { - int id = this.values.getId(state); + public int idFor(final T value, final PaletteResize resizeHandler) { + int id = this.values.getId(value); if (id == -1) { -- id = this.values.add(state); +- id = this.values.add(value); - if (id >= 1 << this.bits) { + // Paper start - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap and optimize + // We use size() instead of the result from add(K) + // This avoids adding another object unnecessarily + // Without this change, + 2 would be required in the constructor + if (this.values.size() >= 1 << this.bits) { - id = resizeHandler.onResize(this.bits + 1, state); + id = resizeHandler.onResize(this.bits + 1, value); + } else { -+ id = this.values.add(state); ++ id = this.values.add(value); } + // Paper end - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap and optimize } diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/ImposterProtoChunk.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/ImposterProtoChunk.java.patch index 332679661db4..887a899d6af9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/ImposterProtoChunk.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/ImposterProtoChunk.java.patch @@ -12,5 +12,5 @@ + // Paper end + @Override - public FluidState getFluidState(BlockPos pos) { + public FluidState getFluidState(final BlockPos pos) { return this.wrapped.getFluidState(pos); diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch index 4173f4e8e9fd..f060348c8a0e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java -@@ -87,13 +_,21 @@ +@@ -88,13 +_,21 @@ }; private final Map tickersInLevel = Maps.newHashMap(); public boolean loaded; @@ -21,29 +21,29 @@ + boolean loadedTicketLevel; + // Paper end - public LevelChunk(Level level, ChunkPos pos) { + public LevelChunk(final Level level, final ChunkPos pos) { this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null); -@@ -111,7 +_,7 @@ - @Nullable BlendingData blendingData +@@ -112,7 +_,7 @@ + final @Nullable BlendingData blendingData ) { - super(pos, data, level, level.palettedContainerFactory(), inhabitedTime, sections, blendingData); + super(pos, upgradeData, level, level.palettedContainerFactory(), inhabitedTime, sections, blendingData); - this.level = level; + this.level = (ServerLevel) level; // CraftBukkit - type this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>(); - for (Heightmap.Types types : Heightmap.Types.values()) { -@@ -163,6 +_,10 @@ - this.skyLightSources = chunk.skyLightSources; - this.setLightCorrect(chunk.isLightCorrect()); + for (Heightmap.Types type : Heightmap.Types.values()) { +@@ -164,6 +_,10 @@ + this.skyLightSources = protoChunk.skyLightSources; + this.setLightCorrect(protoChunk.isLightCorrect()); this.markUnsaved(); + this.needsDecoration = true; // CraftBukkit + // CraftBukkit start -+ this.persistentDataContainer = chunk.persistentDataContainer; // SPIGOT-6814: copy PDC to account for 1.17 to 1.18 chunk upgrading. ++ this.persistentDataContainer = protoChunk.persistentDataContainer; // SPIGOT-6814: copy PDC to account for 1.17 to 1.18 chunk upgrading. + // CraftBukkit end } - public void setUnsavedListener(LevelChunk.UnsavedListener unsavedListener) { -@@ -171,6 +_,12 @@ + public void setUnsavedListener(final LevelChunk.UnsavedListener unsavedListener) { +@@ -172,6 +_,12 @@ unsavedListener.setUnsaved(this.chunkPos); } } @@ -56,8 +56,8 @@ @Override public void markUnsaved() { -@@ -204,8 +_,28 @@ - : super.getListenerRegistry(sectionY); +@@ -205,8 +_,28 @@ + : super.getListenerRegistry(section); } + // Paper start - Perf: Reduce instructions and provide final method @@ -77,7 +77,7 @@ + } + @Override - public BlockState getBlockState(BlockPos pos) { + public BlockState getBlockState(final BlockPos pos) { + if (true) { + return this.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); + } @@ -85,7 +85,7 @@ int x = pos.getX(); int y = pos.getY(); int z = pos.getZ(); -@@ -240,28 +_,42 @@ +@@ -241,28 +_,42 @@ } } @@ -102,47 +102,47 @@ + // Paper end + @Override - public FluidState getFluidState(BlockPos pos) { + public FluidState getFluidState(final BlockPos pos) { return this.getFluidState(pos.getX(), pos.getY(), pos.getZ()); } - public FluidState getFluidState(int x, int y, int z) { + public FluidState getFluidState(final int x, final int y, final int z) { - try { + // try { // Paper start - Perf: Optimise Chunk#getFluid int sectionIndex = this.getSectionIndex(y); if (sectionIndex >= 0 && sectionIndex < this.sections.length) { - LevelChunkSection levelChunkSection = this.sections[sectionIndex]; - if (!levelChunkSection.hasOnlyAir()) { -- return levelChunkSection.getFluidState(x & 15, y & 15, z & 15); -+ return levelChunkSection.states.get((y & 15) << 8 | (z & 15) << 4 | x & 15).getFluidState(); // Paper - Perf: Optimise Chunk#getFluid + LevelChunkSection currentSection = this.sections[sectionIndex]; + if (!currentSection.hasOnlyAir()) { +- return currentSection.getFluidState(x & 15, y & 15, z & 15); ++ return currentSection.states.get((y & 15) << 8 | (z & 15) << 4 | x & 15).getFluidState(); // Paper - Perf: Optimise Chunk#getFluid } } return Fluids.EMPTY.defaultFluidState(); + /* // Paper - Perf: Optimise Chunk#getFluid } catch (Throwable var7) { - CrashReport crashReport = CrashReport.forThrowable(var7, "Getting fluid state"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Block being got"); - crashReportCategory.setDetail("Location", () -> CrashReportCategory.formatLocation(this, x, y, z)); - throw new ReportedException(crashReport); + CrashReport report = CrashReport.forThrowable(var7, "Getting fluid state"); + CrashReportCategory category = report.addCategory("Block being got"); + category.setDetail("Location", () -> CrashReportCategory.formatLocation(this, x, y, z)); + throw new ReportedException(report); } + */ // Paper - Perf: Optimise Chunk#getFluid } @Override -@@ -322,7 +_,7 @@ - if (!section.getBlockState(i, i1, i2).is(block)) { +@@ -323,7 +_,7 @@ + if (!section.getBlockState(localX, localY, localZ).is(newBlock)) { return null; } else { - if (!this.level.isClientSide() && (flags & Block.UPDATE_SKIP_ON_PLACE) == 0) { -+ if (!this.level.isClientSide() && (flags & Block.UPDATE_SKIP_ON_PLACE) == 0 && (!this.level.captureBlockStates || block instanceof net.minecraft.world.level.block.BaseEntityBlock)) { // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled. - state.onPlace(this.level, pos, blockState, flag1); ++ if (!this.level.isClientSide() && (flags & Block.UPDATE_SKIP_ON_PLACE) == 0 && (!this.level.captureBlockStates || newBlock instanceof net.minecraft.world.level.block.BaseEntityBlock)) { // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled. + state.onPlace(this.level, pos, oldState, movedByPiston); } -@@ -373,7 +_,12 @@ +@@ -369,7 +_,12 @@ } - public @Nullable BlockEntity getBlockEntity(BlockPos pos, LevelChunk.EntityCreationType creationType) { + public @Nullable BlockEntity getBlockEntity(final BlockPos pos, final LevelChunk.EntityCreationType creationType) { - BlockEntity blockEntity = this.blockEntities.get(pos); + // CraftBukkit start + BlockEntity blockEntity = this.level.capturedTileEntities.get(pos); @@ -151,36 +151,36 @@ + } + // CraftBukkit end if (blockEntity == null) { - CompoundTag compoundTag = this.pendingBlockEntities.remove(pos); - if (compoundTag != null) { -@@ -428,7 +_,13 @@ - BlockPos blockPos = blockEntity.getBlockPos(); - BlockState blockState = this.getBlockState(blockPos); + CompoundTag tag = this.pendingBlockEntities.remove(pos); + if (tag != null) { +@@ -424,7 +_,13 @@ + BlockPos pos = blockEntity.getBlockPos(); + BlockState blockState = this.getBlockState(pos); if (!blockState.hasBlockEntity()) { -- LOGGER.warn("Trying to set block entity {} at position {}, but state {} does not allow it", blockEntity, blockPos, blockState); +- LOGGER.warn("Trying to set block entity {} at position {}, but state {} does not allow it", blockEntity, pos, blockState); + // Paper start - ServerExceptionEvent + com.destroystokyo.paper.exception.ServerInternalException e = new com.destroystokyo.paper.exception.ServerInternalException( -+ "Trying to set block entity %s at position %s, but state %s does not allow it".formatted(blockEntity, blockPos, blockState) ++ "Trying to set block entity %s at position %s, but state %s does not allow it".formatted(blockEntity, pos, blockState) + ); + e.printStackTrace(); + com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(e); + // Paper end - ServerExceptionEvent } else { - BlockState blockState1 = blockEntity.getBlockState(); - if (blockState != blockState1) { -@@ -475,6 +_,11 @@ - public void removeBlockEntity(BlockPos pos) { + BlockState cachedBlockState = blockEntity.getBlockState(); + if (blockState != cachedBlockState) { +@@ -471,6 +_,11 @@ + public void removeBlockEntity(final BlockPos pos) { if (this.isInLevel()) { - BlockEntity blockEntity = this.blockEntities.remove(pos); + BlockEntity removeThis = this.blockEntities.remove(pos); + // CraftBukkit start - SPIGOT-5561: Also remove from pending map + if (!this.pendingBlockEntities.isEmpty()) { + this.pendingBlockEntities.remove(pos); + } + // CraftBukkit end - if (blockEntity != null) { + if (removeThis != null) { if (this.level instanceof ServerLevel serverLevel) { - this.removeGameEventListener(blockEntity, serverLevel); -@@ -518,6 +_,65 @@ + this.removeGameEventListener(removeThis, serverLevel); +@@ -514,6 +_,65 @@ } } @@ -206,7 +206,7 @@ + random.setSeed(this.level.getSeed()); + long xRand = random.nextLong() / 2L * 2L + 1L; + long zRand = random.nextLong() / 2L * 2L + 1L; -+ random.setSeed((long) this.chunkPos.x * xRand + (long) this.chunkPos.z * zRand ^ this.level.getSeed()); ++ random.setSeed((long)this.chunkPos.x() * xRand + (long)this.chunkPos.z() * zRand ^ this.level.getSeed()); + + org.bukkit.World world = this.level.getWorld(); + if (world != null) { @@ -246,7 +246,7 @@ public boolean isEmpty() { return false; } -@@ -756,23 +_,24 @@ +@@ -752,23 +_,24 @@ if (this.blockEntity.getType().isValid(blockState)) { this.ticker.tick(LevelChunk.this.level, this.blockEntity.getBlockPos(), blockState, this.blockEntity); this.loggedInvalidBlockState = false; @@ -269,12 +269,12 @@ + // Paper end - Remove the Block Entity if it's invalid } - profilerFiller.pop(); + profiler.pop(); } catch (Throwable var5) { -- CrashReport crashReport = CrashReport.forThrowable(var5, "Ticking block entity"); -- CrashReportCategory crashReportCategory = crashReport.addCategory("Block entity being ticked"); -- this.blockEntity.fillCrashReportCategory(crashReportCategory); -- throw new ReportedException(crashReport); +- CrashReport report = CrashReport.forThrowable(var5, "Ticking block entity"); +- CrashReportCategory category = report.addCategory("Block entity being ticked"); +- this.blockEntity.fillCrashReportCategory(category); +- throw new ReportedException(report); + // Paper start - Prevent block entity and entity crashes + final String msg = String.format("BlockEntity threw exception at %s:%s,%s,%s", LevelChunk.this.getLevel().getWorld().getName(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ()); + net.minecraft.server.MinecraftServer.LOGGER.error(msg, var5); diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunkSection.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunkSection.java.patch index 1c1f58b5ab2f..5d3e1faf88bf 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunkSection.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunkSection.java.patch @@ -1,46 +1,48 @@ --- a/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -14,11 +_,11 @@ +@@ -15,12 +_,12 @@ public static final int SECTION_HEIGHT = 16; public static final int SECTION_SIZE = 4096; public static final int BIOME_CONTAINER_BITS = 2; - private short nonEmptyBlockCount; + short nonEmptyBlockCount; // Paper - package private + private short fluidCount; private short tickingBlockCount; private short tickingFluidCount; public final PalettedContainer states; - private PalettedContainerRO> biomes; + private PalettedContainer> biomes; // CraftBukkit - read/write - private LevelChunkSection(LevelChunkSection section) { - this.nonEmptyBlockCount = section.nonEmptyBlockCount; -@@ -28,7 +_,7 @@ - this.biomes = section.biomes.copy(); + private LevelChunkSection(final LevelChunkSection source) { + this.nonEmptyBlockCount = source.nonEmptyBlockCount; +@@ -31,7 +_,7 @@ + this.biomes = source.biomes.copy(); } -- public LevelChunkSection(PalettedContainer states, PalettedContainerRO> biomes) { -+ public LevelChunkSection(PalettedContainer states, PalettedContainer> biomes) { // CraftBukkit - read/write +- public LevelChunkSection(final PalettedContainer states, final PalettedContainerRO> biomes) { ++ public LevelChunkSection(final PalettedContainer states, final PalettedContainer> biomes) { // CraftBukkit - read/write this.states = states; this.biomes = biomes; this.recalcBlockCounts(); -@@ -44,7 +_,7 @@ +@@ -47,7 +_,7 @@ } - public FluidState getFluidState(int x, int y, int z) { -- return this.states.get(x, y, z).getFluidState(); -+ return this.states.get(x, y, z).getFluidState(); // Paper - Perf: Optimise LevelChunk#getFluidState; diff on change - we expect this to be effectively just get(x, y, z).getFluidState(). If this changes we need to check other patches that use BlockBehaviour#getFluidState. + public FluidState getFluidState(final int sectionX, final int sectionY, final int sectionZ) { +- return this.states.get(sectionX, sectionY, sectionZ).getFluidState(); ++ return this.states.get(sectionX, sectionY, sectionZ).getFluidState(); // Paper - Perf: Optimise LevelChunk#getFluidState; diff on change - we expect this to be effectively just get(x, y, z).getFluidState(). If this changes we need to check other patches that use BlockBehaviour#getFluidState. } public void acquire() { -@@ -181,6 +_,11 @@ - public Holder getNoiseBiome(int x, int y, int z) { - return this.biomes.get(x, y, z); +@@ -203,6 +_,12 @@ + public Holder getNoiseBiome(final int quartX, final int quartY, final int quartZ) { + return this.biomes.get(quartX, quartY, quartZ); } ++ + // CraftBukkit start -+ public void setBiome(int x, int y, int z, Holder biome) { -+ this.biomes.set(x, y, z, biome); ++ public void setNoiseBiome(int quartX, int quartY, int quartZ, Holder biome) { ++ this.biomes.set(quartX, quartY, quartZ, biome); + } + // CraftBukkit end - public void fillBiomesFromNoise(BiomeResolver biomeResolver, Climate.Sampler climateSampler, int x, int y, int z) { - PalettedContainer> palettedContainer = this.biomes.recreate(); + public void fillBiomesFromNoise( + final BiomeResolver biomeResolver, final Climate.Sampler sampler, final int quartMinX, final int quartMinY, final int quartMinZ diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainer.java.patch index f8ed89adb41c..072c43a99a1a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainer.java.patch @@ -17,58 +17,58 @@ + // this.threadingDetector.checkAndUnlock(); // Paper - disable this - use proper synchronization } - public static Codec> codecRW(Codec valueCodec, Strategy strategy, T defaultValue) { -@@ -93,7 +_,7 @@ + public static Codec> codecRW(final Codec elementCodec, final Strategy strategy, final T defaultValue) { +@@ -94,7 +_,7 @@ } @Override -- public int onResize(int bits, T addedValue) { -+ public synchronized int onResize(int bits, T addedValue) { // Paper - synchronize - PalettedContainer.Data data = this.data; - PalettedContainer.Data data1 = this.createOrReuseData(data, bits); - data1.copyFrom(data.palette, data.storage); -@@ -101,7 +_,7 @@ - return data1.palette.idFor(addedValue, PaletteResize.noResizeExpected()); +- public int onResize(final int bits, final T lastAddedValue) { ++ public synchronized int onResize(final int bits, final T lastAddedValue) { // Paper - synchronize + PalettedContainer.Data oldData = this.data; + PalettedContainer.Data newData = this.createOrReuseData(oldData, bits); + newData.copyFrom(oldData.palette, oldData.storage); +@@ -102,7 +_,7 @@ + return newData.palette.idFor(lastAddedValue, PaletteResize.noResizeExpected()); } -- public T getAndSet(int x, int y, int z, T state) { -+ public synchronized T getAndSet(int x, int y, int z, T state) { // Paper - synchronize +- public T getAndSet(final int x, final int y, final int z, final T value) { ++ public synchronized T getAndSet(final int x, final int y, final int z, final T value) { // Paper - synchronize this.acquire(); Object var5; -@@ -124,7 +_,7 @@ - return this.data.palette.valueFor(andSet); +@@ -125,7 +_,7 @@ + return this.data.palette.valueFor(oldId); } -- public void set(int x, int y, int z, T state) { -+ public synchronized void set(int x, int y, int z, T state) { // Paper - synchronize +- public void set(final int x, final int y, final int z, final T value) { ++ public synchronized void set(final int x, final int y, final int z, final T value) { // Paper - synchronize this.acquire(); try { -@@ -157,7 +_,7 @@ - set.forEach(id -> consumer.accept(palette.valueFor(id))); +@@ -158,7 +_,7 @@ + allExistingEntries.forEach(state -> consumer.accept(palette.valueFor(state))); } -- public void read(FriendlyByteBuf buffer) { -+ public synchronized void read(FriendlyByteBuf buffer) { // Paper - synchronize +- public void read(final FriendlyByteBuf buffer) { ++ public synchronized void read(final FriendlyByteBuf buffer) { // Paper - synchronize this.acquire(); try { -@@ -172,7 +_,7 @@ +@@ -173,7 +_,7 @@ } @Override -- public void write(FriendlyByteBuf buffer) { -+ public synchronized void write(FriendlyByteBuf buffer) { // Paper - synchronize +- public void write(final FriendlyByteBuf buffer) { ++ public synchronized void write(final FriendlyByteBuf buffer) { // Paper - synchronize this.acquire(); try { -@@ -226,7 +_,7 @@ +@@ -227,7 +_,7 @@ } @Override -- public PalettedContainerRO.PackedData pack(Strategy strategy) { -+ public synchronized PalettedContainerRO.PackedData pack(Strategy strategy) { // Paper - synchronize +- public PalettedContainerRO.PackedData pack(final Strategy strategy) { ++ public synchronized PalettedContainerRO.PackedData pack(final Strategy strategy) { // Paper - synchronize this.acquire(); PalettedContainerRO.PackedData var14; diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainerFactory.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainerFactory.java.patch index e7c119b53452..7338d09abd81 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainerFactory.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/PalettedContainerFactory.java.patch @@ -6,13 +6,13 @@ Codec>> biomeContainerCodec + , Codec>> biomeContainerRWCodec // Paper ) { - public static PalettedContainerFactory create(RegistryAccess registryAccess) { - Strategy strategy = Strategy.createForBlockStates(Block.BLOCK_STATE_REGISTRY); + public static PalettedContainerFactory create(final RegistryAccess registries) { + Strategy blockStateStrategy = Strategy.createForBlockStates(Block.BLOCK_STATE_REGISTRY); @@ -32,6 +_,7 @@ - strategy1, - orThrow, - PalettedContainer.codecRO(registry.holderByNameCodec(), strategy1, orThrow) -+ , PalettedContainer.codecRW(registry.holderByNameCodec(), strategy1, orThrow) // Paper + biomeStrategy, + defaultBiome, + PalettedContainer.codecRO(biomes.holderByNameCodec(), biomeStrategy, defaultBiome) ++ , PalettedContainer.codecRW(biomes.holderByNameCodec(), biomeStrategy, defaultBiome) // Paper ); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/ProtoChunk.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/ProtoChunk.java.patch index f0f46aee2a6d..0b28e108703a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/ProtoChunk.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/ProtoChunk.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/chunk/ProtoChunk.java +++ b/net/minecraft/world/level/chunk/ProtoChunk.java @@ -90,14 +_,31 @@ - return new ChunkAccess.PackedTicks(this.blockTicks.pack(gameTime), this.fluidTicks.pack(gameTime)); + return new ChunkAccess.PackedTicks(this.blockTicks.pack(currentTick), this.fluidTicks.pack(currentTick)); } + // Paper start - If loaded util @@ -17,7 +17,7 @@ + // Paper end + @Override - public BlockState getBlockState(BlockPos pos) { + public BlockState getBlockState(final BlockPos pos) { - int y = pos.getY(); + // Paper start + return this.getBlockState(pos.getX(), pos.getY(), pos.getZ()); diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/UpgradeData.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/UpgradeData.java.patch index e11eae4bb003..08fdc2236b4c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/UpgradeData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/UpgradeData.java.patch @@ -23,7 +23,7 @@ + } + // Paper end - filter out relocated neighbour ticks + - public void upgrade(LevelChunk chunk) { + public void upgrade(final LevelChunk chunk) { this.upgradeInside(chunk); @@ -94,6 +_,10 @@ @@ -35,13 +35,13 @@ + filterTickList(chunk.locX, chunk.locZ, this.neighborFluidTicks); + // Paper end - filter out relocated neighbour ticks Level level = chunk.getLevel(); - this.neighborBlockTicks.forEach(savedTick -> { - Block block = savedTick.type() == Blocks.AIR ? level.getBlockState(savedTick.pos()).getBlock() : savedTick.type(); + this.neighborBlockTicks.forEach(tick -> { + Block type = tick.type() == Blocks.AIR ? level.getBlockState(tick.pos()).getBlock() : tick.type(); @@ -103,6 +_,7 @@ - Fluid fluid = savedTick.type() == Fluids.EMPTY ? level.getFluidState(savedTick.pos()).getType() : savedTick.type(); - level.scheduleTick(savedTick.pos(), fluid, savedTick.delay(), savedTick.priority()); + Fluid type = tick.type() == Fluids.EMPTY ? level.getFluidState(tick.pos()).getType() : tick.type(); + level.scheduleTick(tick.pos(), type, tick.delay(), tick.priority()); }); + UpgradeData.BlockFixers.values(); // Paper - force the class init so that we don't access CHUNKY_FIXERS before all BlockFixers are initialised - CHUNKY_FIXERS.forEach(blockFixer -> blockFixer.processChunk(level)); + CHUNKY_FIXERS.forEach(fixer -> fixer.processChunk(level)); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java.patch index c329cff10fc1..d359e9e44b46 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java.patch @@ -1,23 +1,23 @@ --- a/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java +++ b/net/minecraft/world/level/chunk/status/ChunkStatusTasks.java @@ -41,7 +_,7 @@ - WorldGenContext worldGenContext, ChunkStep step, StaticCache2D cache, ChunkAccess chunk + final WorldGenContext context, final ChunkStep step, final StaticCache2D chunks, final ChunkAccess chunk ) { - ServerLevel serverLevel = worldGenContext.level(); -- if (serverLevel.getServer().getWorldData().worldGenOptions().generateStructures()) { -+ if (serverLevel.serverLevelData.worldGenOptions().generateStructures()) { // CraftBukkit - worldGenContext.generator() + ServerLevel level = context.level(); +- if (level.getServer().getWorldGenSettings().options().generateStructures()) { ++ if (level.worldGenSettings.options().generateStructures()) { // CraftBukkit + context.generator() .createStructures( - serverLevel.registryAccess(), -@@ -211,7 +_,51 @@ + level.registryAccess(), +@@ -210,7 +_,51 @@ - public static void postLoadProtoChunk(ServerLevel level, ValueInput.ValueInputList input) { - if (!input.isEmpty()) { -- level.addWorldGenChunkEntities(EntityType.loadEntitiesRecursive(input, level, EntitySpawnReason.LOAD)); + public static void postLoadProtoChunk(final ServerLevel level, final ValueInput.ValueInputList entities) { + if (!entities.isEmpty()) { +- level.addWorldGenChunkEntities(EntityType.loadEntitiesRecursive(entities, level, EntitySpawnReason.LOAD)); - } - } + // Paper start - duplicate uuid resolving -+ level.addWorldGenChunkEntities(EntityType.loadEntitiesRecursive(input, level, EntitySpawnReason.LOAD).filter((entity) -> { ++ level.addWorldGenChunkEntities(EntityType.loadEntitiesRecursive(entities, level, EntitySpawnReason.LOAD).filter((entity) -> { + return !checkDupeUUID(level, entity); + })); + // Paper end - duplicate uuid resolving diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFile.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFile.java.patch index b04f2e617633..054f739c75d9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFile.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFile.java.patch @@ -1,18 +1,18 @@ --- a/net/minecraft/world/level/chunk/storage/RegionFile.java +++ b/net/minecraft/world/level/chunk/storage/RegionFile.java -@@ -46,7 +_,7 @@ +@@ -47,7 +_,7 @@ protected final RegionBitmap usedSectors = new RegionBitmap(); - public RegionFile(RegionStorageInfo info, Path path, Path externalFileDir, boolean sync) throws IOException { + public RegionFile(final RegionStorageInfo info, final Path path, final Path externalFileDir, final boolean sync) throws IOException { - this(info, path, externalFileDir, RegionFileVersion.getSelected(), sync); + this(info, path, externalFileDir, RegionFileVersion.getCompressionFormat(), sync); // Paper - Configurable region compression format } - public RegionFile(RegionStorageInfo info, Path path, Path externalFileDir, RegionFileVersion version, boolean sync) throws IOException { -@@ -82,6 +_,14 @@ - if (i2 != 0) { - int sectorNumber = getSectorNumber(i2); - int numSectors = getNumSectors(i2); + public RegionFile(final RegionStorageInfo info, final Path path, final Path externalFileDir, final RegionFileVersion version, final boolean sync) throws IOException { +@@ -83,6 +_,14 @@ + if (offset != 0) { + int sectorNumber = getSectorNumber(offset); + int numSectors = getNumSectors(offset); + // Spigot start + if (numSectors == 255) { + // We're maxed out, so we need to read the proper length from the section @@ -22,9 +22,9 @@ + } + // Spigot end if (sectorNumber < 2) { - LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", path, i1, sectorNumber); - this.offsets.put(i1, 0); -@@ -116,6 +_,13 @@ + LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", path, i, sectorNumber); + this.offsets.put(i, 0); +@@ -117,6 +_,13 @@ } else { int sectorNumber = getSectorNumber(offset); int numSectors = getNumSectors(offset); @@ -35,10 +35,10 @@ + numSectors = (realLen.getInt(0) + 4) / 4096 + 1; + } + // Spigot end - int i = numSectors * 4096; - ByteBuffer byteBuffer = ByteBuffer.allocate(i); - this.file.read(byteBuffer, sectorNumber * 4096); -@@ -257,6 +_,7 @@ + int sectorsLength = numSectors * 4096; + ByteBuffer buffer = ByteBuffer.allocate(sectorsLength); + this.file.read(buffer, sectorNumber * 4096); +@@ -258,6 +_,7 @@ return true; } } catch (IOException var9) { @@ -46,10 +46,10 @@ return false; } } -@@ -328,6 +_,11 @@ - try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) { - chunkData.position(5); - fileChannel.write(chunkData); +@@ -329,6 +_,11 @@ + try (FileChannel extFile = FileChannel.open(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) { + data.position(5); + extFile.write(data); + // Paper start - ServerExceptionEvent + } catch (Throwable throwable) { + com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(throwable); @@ -57,4 +57,4 @@ + // Paper end - ServerExceptionEvent } - return () -> Files.move(path, externalChunkFile, StandardCopyOption.REPLACE_EXISTING); + return () -> Files.move(tmpPath, path, StandardCopyOption.REPLACE_EXISTING); diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFileStorage.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFileStorage.java.patch index 3d3404cdedc8..1c6ee4a3c21b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFileStorage.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFileStorage.java.patch @@ -1,61 +1,62 @@ --- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -@@ -29,18 +_,19 @@ +@@ -29,18 +_,20 @@ this.info = info; } -- private RegionFile getRegionFile(ChunkPos chunkPos) throws IOException { -+ @org.jetbrains.annotations.Contract("_, false -> !null") @Nullable private RegionFile getRegionFile(ChunkPos chunkPos, boolean existingOnly) throws IOException { // CraftBukkit - long packedChunkPos = ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ()); - RegionFile regionFile = this.regionCache.getAndMoveToFirst(packedChunkPos); - if (regionFile != null) { - return regionFile; +- private RegionFile getRegionFile(final ChunkPos pos) throws IOException { ++ @org.jetbrains.annotations.Contract("_, false -> !null") private @Nullable RegionFile getRegionFile(final ChunkPos pos, boolean existingOnly) throws IOException { // CraftBukkit + long key = ChunkPos.pack(pos.getRegionX(), pos.getRegionZ()); + RegionFile region = this.regionCache.getAndMoveToFirst(key); + if (region != null) { + return region; } else { - if (this.regionCache.size() >= 256) { -+ if (this.regionCache.size() >= io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize) { // Paper - Sanitise RegionFileCache and make configurable ++ int cacheSize = io.papermc.paper.configuration.GlobalConfiguration.get() == null ? 256 : io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize; // Paper - Sanitise RegionFileCache and make configurable - Config not available during initial FileFixerUpper run ++ if (this.regionCache.size() >= cacheSize) { // Paper - Sanitise RegionFileCache and make configurable this.regionCache.removeLast().close(); } FileUtil.createDirectoriesSafe(this.folder); - Path path = this.folder.resolve("r." + chunkPos.getRegionX() + "." + chunkPos.getRegionZ() + ".mca"); -+ if (existingOnly && !java.nio.file.Files.exists(path)) return null; // CraftBukkit - RegionFile regionFile1 = new RegionFile(this.info, path, this.folder, this.sync); - this.regionCache.putAndMoveToFirst(packedChunkPos, regionFile1); - return regionFile1; + Path file = this.folder.resolve("r." + pos.getRegionX() + "." + pos.getRegionZ() + ".mca"); ++ if (existingOnly && !java.nio.file.Files.exists(file)) return null; // CraftBukkit + RegionFile newRegion = new RegionFile(this.info, file, this.folder, this.sync); + this.regionCache.putAndMoveToFirst(key, newRegion); + return newRegion; @@ -48,7 +_,12 @@ } - public @Nullable CompoundTag read(ChunkPos chunkPos) throws IOException { -- RegionFile regionFile = this.getRegionFile(chunkPos); + public @Nullable CompoundTag read(final ChunkPos pos) throws IOException { +- RegionFile region = this.getRegionFile(pos); + // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing -+ RegionFile regionFile = this.getRegionFile(chunkPos, true); -+ if (regionFile == null) { ++ RegionFile region = this.getRegionFile(pos, true); ++ if (region == null) { + return null; + } + // CraftBukkit end CompoundTag var4; - try (DataInputStream chunkDataInputStream = regionFile.getChunkDataInputStream(chunkPos)) { + try (DataInputStream regionChunkInputStream = region.getChunkDataInputStream(pos)) { @@ -63,7 +_,12 @@ } - public void scanChunk(ChunkPos chunkPos, StreamTagVisitor visitor) throws IOException { -- RegionFile regionFile = this.getRegionFile(chunkPos); + public void scanChunk(final ChunkPos pos, final StreamTagVisitor scanner) throws IOException { +- RegionFile region = this.getRegionFile(pos); + // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing -+ RegionFile regionFile = this.getRegionFile(chunkPos, true); -+ if (regionFile == null) { ++ RegionFile region = this.getRegionFile(pos, true); ++ if (region == null) { + return; + } + // CraftBukkit end - try (DataInputStream chunkDataInputStream = regionFile.getChunkDataInputStream(chunkPos)) { - if (chunkDataInputStream != null) { + try (DataInputStream regionChunkInputStream = region.getChunkDataInputStream(pos)) { + if (regionChunkInputStream != null) { @@ -74,7 +_,7 @@ - protected void write(ChunkPos chunkPos, @Nullable CompoundTag chunkData) throws IOException { + protected void write(final ChunkPos pos, final @Nullable CompoundTag value) throws IOException { if (!SharedConstants.DEBUG_DONT_SAVE_WORLD) { -- RegionFile regionFile = this.getRegionFile(chunkPos); -+ RegionFile regionFile = this.getRegionFile(chunkPos, false); // CraftBukkit - if (chunkData == null) { - regionFile.clear(chunkPos); +- RegionFile region = this.getRegionFile(pos); ++ RegionFile region = this.getRegionFile(pos, false); // CraftBukkit + if (value == null) { + region.clear(pos); } else { diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFileVersion.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFileVersion.java.patch index 26636eb668ec..56db01d59599 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFileVersion.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFileVersion.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java +++ b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java -@@ -60,6 +_,16 @@ +@@ -49,6 +_,16 @@ private final RegionFileVersion.StreamWrapper inputWrapper; private final RegionFileVersion.StreamWrapper outputWrapper; @@ -15,5 +15,5 @@ + } + // Paper end - Configurable region compression format private RegionFileVersion( - int id, - @Nullable String optionName, + final int id, + final @Nullable String optionName, diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch index 29cbf85f57fb..803a761358c2 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch @@ -8,7 +8,7 @@ ) { private static final Codec>> BLOCK_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.BLOCK.byNameCodec()).listOf(); private static final Codec>> FLUID_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.FLUID.byNameCodec()).listOf(); -@@ -107,11 +_,37 @@ +@@ -107,13 +_,39 @@ public static final String BLOCK_LIGHT_TAG = "BlockLight"; public static final String SKY_LIGHT_TAG = "SkyLight"; @@ -30,64 +30,66 @@ + private static final boolean JUST_CORRUPT_IT = Boolean.getBoolean("Paper.ignoreWorldDataVersion"); + // Paper end - Do not let the server load chunks from newer versions + - public static SerializableChunkData parse(LevelHeightAccessor level, PalettedContainerFactory containerFactory, CompoundTag tag) { - if (tag.getString("Status").isEmpty()) { + public static SerializableChunkData parse( + final LevelHeightAccessor levelHeight, final PalettedContainerFactory containerFactory, final CompoundTag chunkData + ) { + if (chunkData.getString("Status").isEmpty()) { return null; } else { -- ChunkPos chunkPos = new ChunkPos(tag.getIntOr("xPos", 0), tag.getIntOr("zPos", 0)); +- ChunkPos chunkPos = new ChunkPos(chunkData.getIntOr("xPos", 0), chunkData.getIntOr("zPos", 0)); + // Paper start - Do not let the server load chunks from newer versions -+ tag.getInt("DataVersion").ifPresent(dataVersion -> { ++ chunkData.getInt("DataVersion").ifPresent(dataVersion -> { + if (!JUST_CORRUPT_IT && dataVersion > CURRENT_DATA_VERSION) { + new RuntimeException("Server attempted to load chunk saved with newer version of minecraft! " + dataVersion + " > " + CURRENT_DATA_VERSION).printStackTrace(); + System.exit(1); + } + }); + // Paper end - Do not let the server load chunks from newer versions -+ ChunkPos chunkPos = new ChunkPos(tag.getIntOr("xPos", 0), tag.getIntOr("zPos", 0)); // Paper - guard against serializing mismatching coordinates; diff on change, see ChunkSerializer#getChunkCoordinate - long longOr = tag.getLongOr("LastUpdate", 0L); - long longOr1 = tag.getLongOr("InhabitedTime", 0L); - ChunkStatus chunkStatus = tag.read("Status", ChunkStatus.CODEC).orElse(ChunkStatus.EMPTY); -@@ -150,7 +_,7 @@ - CompoundTag compoundOrEmpty = tag.getCompoundOrEmpty("structures"); - ListTag listOrEmpty1 = tag.getListOrEmpty("sections"); - List list5 = new ArrayList<>(listOrEmpty1.size()); -- Codec>> codec = containerFactory.biomeContainerCodec(); -+ Codec>> codec = containerFactory.biomeContainerRWCodec(); // CraftBukkit - read/write - Codec> codec1 = containerFactory.blockStatesContainerCodec(); ++ ChunkPos chunkPos = new ChunkPos(chunkData.getIntOr("xPos", 0), chunkData.getIntOr("zPos", 0)); // Paper - guard against serializing mismatching coordinates; diff on change, see ChunkSerializer#getChunkCoordinate + long lastUpdateTime = chunkData.getLongOr("LastUpdate", 0L); + long inhabitedTime = chunkData.getLongOr("InhabitedTime", 0L); + ChunkStatus status = chunkData.read("Status", ChunkStatus.CODEC).orElse(ChunkStatus.EMPTY); +@@ -152,7 +_,7 @@ + CompoundTag structureData = chunkData.getCompoundOrEmpty("structures"); + ListTag sectionTags = chunkData.getListOrEmpty("sections"); + List sectionData = new ArrayList<>(sectionTags.size()); +- Codec>> biomesCodec = containerFactory.biomeContainerCodec(); ++ Codec>> biomesCodec = containerFactory.biomeContainerRWCodec(); // CraftBukkit - read/write + Codec> blockStatesCodec = containerFactory.blockStatesContainerCodec(); - for (int i2 = 0; i2 < listOrEmpty1.size(); i2++) { -@@ -167,7 +_,7 @@ + for (int i = 0; i < sectionTags.size(); i++) { +@@ -169,7 +_,7 @@ .getOrThrow(SerializableChunkData.ChunkReadException::new) ) .orElseGet(containerFactory::createForBlockStates); -- PalettedContainerRO> palettedContainerRo = compoundTag.getCompound("biomes") -+ PalettedContainer> palettedContainerRo = compoundTag.getCompound("biomes") // CraftBukkit - read/write +- PalettedContainerRO> biomes = sectionTag.getCompound("biomes") ++ PalettedContainer> biomes = sectionTag.getCompound("biomes") // CraftBukkit - read/write .map( - compoundTag1 -> codec.parse(NbtOps.INSTANCE, compoundTag1) - .promotePartial(string -> logErrors(chunkPos, byteOr, string)) -@@ -204,6 +_,7 @@ - list3, - list4, - compoundOrEmpty -+ , tag.get("ChunkBukkitValues") // CraftBukkit - ChunkBukkitValues + container -> biomesCodec.parse(NbtOps.INSTANCE, container) + .promotePartial(msg -> logErrors(chunkPos, y, msg)) +@@ -206,6 +_,7 @@ + entities, + blockEntities, + structureData ++ , chunkData.get("ChunkBukkitValues") // CraftBukkit - ChunkBukkitValues ); } } -@@ -288,6 +_,12 @@ +@@ -283,6 +_,12 @@ } } + // CraftBukkit start - load chunk persistent data from nbt - SPIGOT-6814: Already load PDC here to account for 1.17 to 1.18 chunk upgrading. + if (this.persistentDataContainer instanceof CompoundTag compoundTag) { -+ chunkAccess.persistentDataContainer.putAll(compoundTag); ++ chunk.persistentDataContainer.putAll(compoundTag); + } + // CraftBukkit end + - chunkAccess.setLightCorrect(this.lightCorrect); - EnumSet set = EnumSet.noneOf(Heightmap.Types.class); + chunk.setLightCorrect(this.lightCorrect); + EnumSet toPrime = EnumSet.noneOf(Heightmap.Types.class); -@@ -394,6 +_,12 @@ - CompoundTag compoundTag = packStructureData( +@@ -389,6 +_,12 @@ + CompoundTag structureData = packStructureData( StructurePieceSerializationContext.fromLevel(level), pos, chunk.getAllStarts(), chunk.getAllReferences() ); + // CraftBukkit start - store chunk persistent data in nbt @@ -99,36 +101,36 @@ return new SerializableChunkData( level.palettedContainerFactory(), pos, -@@ -413,6 +_,7 @@ - list2, - list1, - compoundTag +@@ -408,6 +_,7 @@ + entities, + blockEntities, + structureData + , persistentDataContainer // CraftBukkit - persistentDataContainer ); } } -@@ -480,6 +_,11 @@ - this.heightmaps.forEach((types, longs) -> compoundTag2.put(types.getSerializationKey(), new LongArrayTag(longs))); - compoundTag.put("Heightmaps", compoundTag2); - compoundTag.put("structures", this.structureData); +@@ -475,6 +_,11 @@ + this.heightmaps.forEach((type, data) -> heightmapsTag.put(type.getSerializationKey(), new LongArrayTag(data))); + tag.put("Heightmaps", heightmapsTag); + tag.put("structures", this.structureData); + // CraftBukkit start - store chunk persistent data in nbt + if (this.persistentDataContainer != null) { // SPIGOT-6814: Always save PDC to account for 1.17 to 1.18 chunk upgrading. -+ compoundTag.put("ChunkBukkitValues", this.persistentDataContainer); ++ tag.put("ChunkBukkitValues", this.persistentDataContainer); + } + // CraftBukkit end - return compoundTag; + return tag; } -@@ -560,6 +_,12 @@ +@@ -558,6 +_,12 @@ } else { - StructureStart structureStart = StructureStart.loadStaticStart(context, compoundOrEmpty.getCompoundOrEmpty(string), seed); - if (structureStart != null) { + StructureStart start = StructureStart.loadStaticStart(context, startsTag.getCompoundOrEmpty(key), seed); + if (start != null) { + // CraftBukkit start - load persistent data for structure start -+ net.minecraft.nbt.Tag persistentBase = compoundOrEmpty.getCompoundOrEmpty(string).get("StructureBukkitValues"); ++ net.minecraft.nbt.Tag persistentBase = startsTag.getCompoundOrEmpty(key).get("StructureBukkitValues"); + if (persistentBase instanceof CompoundTag compoundTag) { -+ structureStart.persistentDataContainer.putAll(compoundTag); ++ start.persistentDataContainer.putAll(compoundTag); + } + // CraftBukkit end - map.put(structure, structureStart); + outmap.put(startFeature, start); } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java.patch index e9b4c4a5a21e..4b3e57efc5b0 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java.patch @@ -1,66 +1,49 @@ --- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java +++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java -@@ -51,19 +_,47 @@ +@@ -44,7 +_,19 @@ } - public CompletableFuture write(ChunkPos chunkPos, Supplier data) { + public CompletableFuture write(final ChunkPos pos, final Supplier supplier) { +- return this.worker.store(pos, supplier); + // Paper start - guard against possible chunk pos desync + final Supplier guardedPosCheck = () -> { -+ CompoundTag nbt = data.get(); ++ final CompoundTag nbt = supplier.get(); + final boolean chunkStorage = this.dataFixType == net.minecraft.util.datafix.DataFixTypes.CHUNK; -+ if (chunkStorage && nbt != null && !chunkPos.equals(SerializableChunkData.getChunkCoordinate(nbt))) { -+ final String world = (SimpleRegionStorage.this instanceof net.minecraft.server.level.ChunkMap) ? ((net.minecraft.server.level.ChunkMap) SimpleRegionStorage.this).level.getWorld().getName() : null; -+ throw new IllegalArgumentException("Chunk coordinate and serialized data do not have matching coordinates, trying to serialize coordinate " + chunkPos ++ if (chunkStorage && nbt != null && !pos.equals(SerializableChunkData.getChunkCoordinate(nbt))) { ++ final String world = (this instanceof net.minecraft.server.level.ChunkMap chunkMap) ? chunkMap.level.getWorld().getName() : null; ++ throw new IllegalArgumentException("Chunk coordinate and serialized data do not have matching coordinates, trying to serialize coordinate " + pos + + " but compound says coordinate is " + SerializableChunkData.getChunkCoordinate(nbt) + (world == null ? " for an unknown world" : (" for world: " + world))); + } + return nbt; + }; ++ return this.worker.store(pos, guardedPosCheck); + // Paper end - guard against possible chunk pos desync - this.markChunkDone(chunkPos); -- return this.worker.store(chunkPos, data); -+ return this.worker.store(chunkPos, guardedPosCheck); // Paper - guard against possible chunk pos desync } -- public CompoundTag upgradeChunkTag(CompoundTag tag, int fallbackVersion, @Nullable CompoundTag contextTag) { -+ public CompoundTag upgradeChunkTag(CompoundTag tag, int fallbackVersion, @Nullable CompoundTag contextTag, net.minecraft.world.level.@Nullable LevelAccessor levelAccessor) { // CraftBukkit - int dataVersion = NbtUtils.getDataVersion(tag, fallbackVersion); - if (dataVersion == SharedConstants.getCurrentVersion().dataVersion().version()) { - return tag; + public CompoundTag upgradeChunkTag(CompoundTag chunkTag, final int defaultVersion, final @Nullable CompoundTag dataFixContextTag, final int targetVersion) { +@@ -53,8 +_,25 @@ + return chunkTag; } else { try { - tag = this.legacyFixer.get().applyFix(tag); + // Spigot start - SPIGOT-6806: Quick and dirty way to prevent below zero generation in old chunks, by setting the status to heightmap instead of empty + boolean stopBelowZero = false; + final boolean chunkStorage = this.dataFixType == net.minecraft.util.datafix.DataFixTypes.CHUNK; + if (chunkStorage) { -+ boolean belowZeroGenerationInExistingChunks = (levelAccessor != null) ? ((net.minecraft.server.level.ServerLevel) levelAccessor).spigotConfig.belowZeroGenerationInExistingChunks : org.spigotmc.SpigotConfig.belowZeroGenerationInExistingChunks; -+ -+ if (dataVersion <= 2730 && !belowZeroGenerationInExistingChunks) { -+ stopBelowZero = "full".equals(tag.getCompound("Level").flatMap(l -> l.getString("Status")).orElse(null)); ++ final boolean belowZeroGenerationInExistingChunks = (this instanceof net.minecraft.server.level.ChunkMap chunkMap) ++ ? chunkMap.level.spigotConfig.belowZeroGenerationInExistingChunks ++ : org.spigotmc.SpigotConfig.belowZeroGenerationInExistingChunks; ++ if (version <= 2730 && !belowZeroGenerationInExistingChunks) { ++ stopBelowZero = "full".equals(chunkTag.getCompound("Level").flatMap(level -> level.getString("Status")).orElse(null)); + } + } + // Spigot end - injectDatafixingContext(tag, contextTag); - tag = this.dataFixType.updateToCurrentVersion(this.fixerUpper, tag, Math.max(this.legacyFixer.get().targetDataVersion(), dataVersion)); + injectDatafixingContext(chunkTag, dataFixContextTag); + chunkTag = this.dataFixType.update(this.fixerUpper, chunkTag, version, targetVersion); + // Spigot start + if (stopBelowZero) { -+ tag.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN).toString()); ++ chunkTag.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN).toString()); + } + // Spigot end - removeDatafixingContext(tag); - NbtUtils.addCurrentDataVersion(tag); - return tag; -@@ -77,11 +_,11 @@ - } - - public CompoundTag upgradeChunkTag(CompoundTag tag, int version) { -- return this.upgradeChunkTag(tag, version, null); -+ return this.upgradeChunkTag(tag, version, null, null); // CraftBukkit - } - - public Dynamic upgradeChunkTag(Dynamic tag, int version) { -- return new Dynamic<>(tag.getOps(), this.upgradeChunkTag((CompoundTag)tag.getValue(), version, null)); -+ return new Dynamic<>(tag.getOps(), this.upgradeChunkTag((CompoundTag)tag.getValue(), version, null, null)); // CraftBukkit - } - - public static void injectDatafixingContext(CompoundTag tag, @Nullable CompoundTag contextTag) { + removeDatafixingContext(chunkTag); + NbtUtils.addDataVersion(chunkTag, targetVersion); + return chunkTag; diff --git a/paper-server/patches/sources/net/minecraft/world/level/dimension/end/DragonRespawnAnimation.java.patch b/paper-server/patches/sources/net/minecraft/world/level/dimension/end/DragonRespawnAnimation.java.patch deleted file mode 100644 index a5dfcd6e9ec4..000000000000 --- a/paper-server/patches/sources/net/minecraft/world/level/dimension/end/DragonRespawnAnimation.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/world/level/dimension/end/DragonRespawnAnimation.java -+++ b/net/minecraft/world/level/dimension/end/DragonRespawnAnimation.java -@@ -91,7 +_,7 @@ - for (EndCrystal endCrystal : crystals) { - endCrystal.setBeamTarget(null); - level.explode(endCrystal, endCrystal.getX(), endCrystal.getY(), endCrystal.getZ(), 6.0F, Level.ExplosionInteraction.NONE); -- endCrystal.discard(); -+ endCrystal.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause - } - } else if (ticks >= 80) { - level.levelEvent(LevelEvent.ANIMATION_DRAGON_SUMMON_ROAR, new BlockPos(0, 128, 0), 0); diff --git a/paper-server/patches/sources/net/minecraft/world/level/dimension/end/DragonRespawnStage.java.patch b/paper-server/patches/sources/net/minecraft/world/level/dimension/end/DragonRespawnStage.java.patch new file mode 100644 index 000000000000..c048133ab36a --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/dimension/end/DragonRespawnStage.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/dimension/end/DragonRespawnStage.java ++++ b/net/minecraft/world/level/dimension/end/DragonRespawnStage.java +@@ -91,7 +_,7 @@ + for (EndCrystal crystal : crystals) { + crystal.setBeamTarget(null); + level.explode(crystal, crystal.getX(), crystal.getY(), crystal.getZ(), 6.0F, Level.ExplosionInteraction.NONE); +- crystal.discard(); ++ crystal.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause + } + } else if (time >= 80) { + level.levelEvent(LevelEvent.ANIMATION_DRAGON_SUMMON_ROAR, new BlockPos(0, 128, 0), 0); diff --git a/paper-server/patches/sources/net/minecraft/world/level/dimension/end/EndDragonFight.java.patch b/paper-server/patches/sources/net/minecraft/world/level/dimension/end/EndDragonFight.java.patch deleted file mode 100644 index 210efbcaf8ab..000000000000 --- a/paper-server/patches/sources/net/minecraft/world/level/dimension/end/EndDragonFight.java.patch +++ /dev/null @@ -1,195 +0,0 @@ ---- a/net/minecraft/world/level/dimension/end/EndDragonFight.java -+++ b/net/minecraft/world/level/dimension/end/EndDragonFight.java -@@ -70,8 +_,9 @@ - private static final int GATEWAY_DISTANCE = 96; - public static final int DRAGON_SPAWN_Y = 128; - private final Predicate validPlayer; -+ private static final Component DEFAULT_BOSS_EVENT_NAME = Component.translatable("entity.minecraft.ender_dragon"); // Paper - reset EnderDragon boss event name - public final ServerBossEvent dragonEvent = (ServerBossEvent)new ServerBossEvent( -- Component.translatable("entity.minecraft.ender_dragon"), BossEvent.BossBarColor.PINK, BossEvent.BossBarOverlay.PROGRESS -+ DEFAULT_BOSS_EVENT_NAME, BossEvent.BossBarColor.PINK, BossEvent.BossBarOverlay.PROGRESS // Paper - reset EnderDragon boss event name - ) - .setPlayBossMusic(true) - .setCreateWorldFog(true); -@@ -108,7 +_,12 @@ - if (data.isRespawning) { - this.respawnStage = DragonRespawnAnimation.START; - } -- -+ // Paper start - Add config to disable ender dragon legacy check -+ if (data == EndDragonFight.Data.DEFAULT && !level.paperConfig().entities.spawning.scanForLegacyEnderDragon) { -+ this.needsStateScanning = false; -+ this.dragonKilled = true; -+ } -+ // Paper end - Add config to disable ender dragon legacy check - this.portalLocation = data.exitPortalLocation.orElse(null); - this.gateways.addAll(data.gateways.orElseGet(() -> { - ObjectArrayList list = new ObjectArrayList<>(ContiguousSet.create(Range.closedOpen(0, 20), DiscreteDomain.integers())); -@@ -205,9 +_,9 @@ - this.dragonUUID = enderDragon.getUUID(); - LOGGER.info("Found that there's a dragon still alive ({})", enderDragon); - this.dragonKilled = false; -- if (!hasActiveExitPortal) { -+ if (!hasActiveExitPortal && this.level.paperConfig().entities.behavior.shouldRemoveDragon) { // Paper - Toggle for removing existing dragon - LOGGER.info("But we didn't have a portal, let's remove it."); -- enderDragon.discard(); -+ enderDragon.discard(null); // CraftBukkit - add Bukkit remove cause - this.dragonUUID = null; - } - } -@@ -361,12 +_,22 @@ - this.dragonEvent.setVisible(false); - this.spawnExitPortal(true); - this.spawnNewGateway(); -- if (!this.previouslyKilled) { -- this.level -- .setBlockAndUpdate( -- this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.origin)), -- Blocks.DRAGON_EGG.defaultBlockState() -- ); -+ // Paper start - Add DragonEggFormEvent -+ BlockPos eggPosition = this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.origin)); -+ org.bukkit.craftbukkit.block.CraftBlockState eggState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(this.level, eggPosition); -+ eggState.setData(Blocks.DRAGON_EGG.defaultBlockState()); -+ io.papermc.paper.event.block.DragonEggFormEvent eggEvent = new io.papermc.paper.event.block.DragonEggFormEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this.level, eggPosition), eggState, -+ new org.bukkit.craftbukkit.boss.CraftDragonBattle(this)); -+ // Paper end - Add DragonEggFormEvent -+ if (this.level.paperConfig().entities.behavior.enderDragonsDeathAlwaysPlacesDragonEgg || !this.previouslyKilled) { // Paper - Add toggle for always placing the dragon egg -+ // Paper start - Add DragonEggFormEvent -+ // this.level.setBlockAndUpdate(this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.origin)), Blocks.DRAGON_EGG.defaultBlockState()); -+ } else { -+ eggEvent.setCancelled(true); -+ } -+ if (eggEvent.callEvent()) { -+ ((org.bukkit.craftbukkit.block.CraftBlockState) eggEvent.getNewState()).place(net.minecraft.world.level.block.Block.UPDATE_ALL); -+ // Paper end - Add DragonEggFormEvent - } - - this.previouslyKilled = true; -@@ -380,6 +_,24 @@ - this.gateways.clear(); - } - -+ // Paper start - More DragonBattle API -+ public boolean spawnNewGatewayIfPossible() { -+ if (!this.gateways.isEmpty()) { -+ this.spawnNewGateway(); -+ return true; -+ } -+ return false; -+ } -+ -+ public List getSpikeCrystals() { -+ final List endCrystals = new java.util.ArrayList<>(); -+ for (final SpikeFeature.EndSpike spike : SpikeFeature.getSpikesForLevel(this.level)) { -+ endCrystals.addAll(this.level.getEntitiesOfClass(EndCrystal.class, spike.getTopBoundingBox())); -+ } -+ return endCrystals; -+ } -+ // Paper end - More DragonBattle API -+ - private void spawnNewGateway() { - if (!this.gateways.isEmpty()) { - int i = this.gateways.remove(this.gateways.size() - 1); -@@ -410,6 +_,11 @@ - this.portalLocation = this.portalLocation.atY(Math.max(this.level.getMinY() + 1, this.portalLocation.getY())); - } - -+ // Paper start - Prevent "softlocked" exit portal generation -+ if (this.portalLocation.getY() <= this.level.getMinY()) { -+ this.portalLocation = this.portalLocation.atY(this.level.getMinY() + 1); -+ } -+ // Paper end - Prevent "softlocked" exit portal generation - if (endPodiumFeature.place( - FeatureConfiguration.NONE, this.level, this.level.getChunkSource().getGenerator(), RandomSource.create(), this.portalLocation - )) { -@@ -428,6 +_,7 @@ - enderDragon.snapTo(this.origin.getX(), 128 + this.origin.getY(), this.origin.getZ(), this.level.random.nextFloat() * 360.0F, 0.0F); - this.level.addFreshEntity(enderDragon); - this.dragonUUID = enderDragon.getUUID(); -+ this.resetSpikeCrystals(); // Paper - Reset ender crystals on dragon spawn - } - - return enderDragon; -@@ -439,6 +_,10 @@ - this.ticksSinceDragonSeen = 0; - if (dragon.hasCustomName()) { - this.dragonEvent.setName(dragon.getDisplayName()); -+ // Paper start - ensure reset EnderDragon boss event name -+ } else { -+ this.dragonEvent.setName(DEFAULT_BOSS_EVENT_NAME); -+ // Paper end - ensure reset EnderDragon boss event name - } - } - } -@@ -466,7 +_,13 @@ - return this.previouslyKilled; - } - -- public void tryRespawn() { -+ public boolean tryRespawn() { // CraftBukkit - return boolean -+ // Paper start - Perf: Do crystal-portal proximity check before entity lookup -+ return this.tryRespawn(null); -+ } -+ -+ public boolean tryRespawn(@Nullable BlockPos placedEndCrystalPos) { // placedEndCrystalPos is null if the tryRespawn() call was not caused by a placed end crystal -+ // Paper end - Perf: Do crystal-portal proximity check before entity lookup - if (this.dragonKilled && this.respawnStage == null) { - BlockPos blockPos = this.portalLocation; - if (blockPos == null) { -@@ -481,6 +_,22 @@ - - blockPos = this.portalLocation; - } -+ // Paper start - Perf: Do crystal-portal proximity check before entity lookup -+ if (placedEndCrystalPos != null && !level.paperConfig().misc.allowRemoteEnderDragonRespawning) { -+ // The end crystal must be 0 or 1 higher than the portal origin -+ int dy = placedEndCrystalPos.getY() - blockPos.getY(); -+ if (dy != 0 && dy != 1) { -+ return false; -+ } -+ // The end crystal must be within a distance of 1 in one planar direction, and 3 in the other -+ int dx = placedEndCrystalPos.getX() - blockPos.getX(); -+ int dz = placedEndCrystalPos.getZ() - blockPos.getZ(); -+ if (!((dx >= -1 && dx <= 1 && dz >= -3 && dz <= 3) || (dx >= -3 && dx <= 3 && dz >= -1 && dz <= 1))) { -+ return false; -+ } -+ } -+ // Paper end - Perf: Do crystal-portal proximity check before entity lookup -+ - - List list = Lists.newArrayList(); - BlockPos blockPos1 = blockPos.above(1); -@@ -488,18 +_,19 @@ - for (Direction direction : Direction.Plane.HORIZONTAL) { - List entitiesOfClass = this.level.getEntitiesOfClass(EndCrystal.class, new AABB(blockPos1.relative(direction, 2))); - if (entitiesOfClass.isEmpty()) { -- return; -+ return false; // CraftBukkit - return value - } - - list.addAll(entitiesOfClass); - } - - LOGGER.debug("Found all crystals, respawning dragon."); -- this.respawnDragon(list); -+ return this.respawnDragon(list); // CraftBukkit - return value - } -+ return false; // CraftBukkit - return value - } - -- public void respawnDragon(List crystals) { -+ public boolean respawnDragon(List crystals) { // CraftBukkit - return boolean - if (this.dragonKilled && this.respawnStage == null) { - for (BlockPattern.BlockPatternMatch blockPatternMatch = this.findExitPortal(); blockPatternMatch != null; blockPatternMatch = this.findExitPortal()) { - for (int i = 0; i < this.exitPortalPattern.getWidth(); i++) { -@@ -518,7 +_,9 @@ - this.respawnTime = 0; - this.spawnExitPortal(false); - this.respawnCrystals = crystals; -+ return true; // CraftBukkit - return value - } -+ return false; // CraftBukkit - return value - } - - public void resetSpikeCrystals() { diff --git a/paper-server/patches/sources/net/minecraft/world/level/dimension/end/EnderDragonFight.java.patch b/paper-server/patches/sources/net/minecraft/world/level/dimension/end/EnderDragonFight.java.patch new file mode 100644 index 000000000000..99f8747f0049 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/dimension/end/EnderDragonFight.java.patch @@ -0,0 +1,184 @@ +--- a/net/minecraft/world/level/dimension/end/EnderDragonFight.java ++++ b/net/minecraft/world/level/dimension/end/EnderDragonFight.java +@@ -155,6 +_,12 @@ + ); + this.dragonEvent.setPlayBossMusic(true).setCreateWorldFog(true); + this.validPlayer = EntitySelector.ENTITY_STILL_ALIVE.and(EntitySelector.withinDistance(origin.getX(), 128 + origin.getY(), origin.getZ(), 192.0)); ++ // Paper start - Add config to disable ender dragon legacy check ++ if (this.isDirty() && !level.paperConfig().entities.spawning.scanForLegacyEnderDragon) { ++ this.needsStateScanning = false; ++ this.dragonKilled = true; ++ } ++ // Paper end - Add config to disable ender dragon legacy check + if (this.gateways.isEmpty()) { + ObjectArrayList newGateways = new ObjectArrayList<>(ContiguousSet.create(Range.closedOpen(0, 20), DiscreteDomain.integers())); + Util.shuffle(newGateways, RandomSource.createThreadLocalInstance(seed)); +@@ -241,9 +_,9 @@ + this.dragonUUID = dragon.getUUID(); + LOGGER.info("Found that there's a dragon still alive ({})", dragon); + this.dragonKilled = false; +- if (!activePortalExists) { ++ if (!activePortalExists && this.level.paperConfig().entities.behavior.shouldRemoveDragon) { // Paper - Toggle for removing existing dragon + LOGGER.info("But we didn't have a portal, let's remove it."); +- dragon.discard(); ++ dragon.discard(null); // CraftBukkit - add Bukkit remove cause + this.dragonUUID = null; + } + } +@@ -404,13 +_,20 @@ + this.dragonEvent.setVisible(false); + this.spawnExitPortal(true); + this.spawnNewGateway(); +- if (!this.hasPreviouslyKilledDragon) { +- this.level +- .setBlockAndUpdate( +- this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.origin)), +- Blocks.DRAGON_EGG.defaultBlockState() +- ); +- } ++ // Paper start - Add DragonEggFormEvent ++ BlockPos eggPosition = this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.origin)); ++ org.bukkit.craftbukkit.block.CraftBlockState eggState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(this.level, eggPosition); ++ eggState.setBlock(Blocks.DRAGON_EGG.defaultBlockState()); ++ io.papermc.paper.event.block.DragonEggFormEvent eggEvent = new io.papermc.paper.event.block.DragonEggFormEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(this.level, eggPosition), eggState, new org.bukkit.craftbukkit.boss.CraftDragonBattle(this) ++ ); ++ if (!this.level.paperConfig().entities.behavior.enderDragonsDeathAlwaysPlacesDragonEgg && this.hasPreviouslyKilledDragon) { // Paper - Add toggle for always placing the dragon egg ++ eggEvent.setCancelled(true); ++ } ++ if (eggEvent.callEvent()) { ++ ((org.bukkit.craftbukkit.block.CraftBlockState)eggEvent.getNewState()).place(net.minecraft.world.level.block.Block.UPDATE_ALL); ++ } ++ // Paper end - Add DragonEggFormEvent + + this.hasPreviouslyKilledDragon = true; + this.dragonKilled = true; +@@ -425,14 +_,26 @@ + this.setDirty(); + } + +- private void spawnNewGateway() { ++ // Paper start - More DragonBattle API ++ public List getSpikeCrystals() { ++ final List crystals = new ArrayList<>(); ++ for (final EndSpikeFeature.EndSpike spike : EndSpikeFeature.getSpikesForLevel(this.level)) { ++ crystals.addAll(this.level.getEntitiesOfClass(EndCrystal.class, spike.getTopBoundingBox())); ++ } ++ return crystals; ++ } ++ // Paper end - More DragonBattle API ++ ++ public boolean spawnNewGateway() { // Paper - More DragonBattle API + if (!this.gateways.isEmpty()) { + int gateway = this.gateways.remove(this.gateways.size() - 1); + int x = Mth.floor(96.0 * Math.cos(2.0 * (-Math.PI + (Math.PI / 20) * gateway))); + int z = Mth.floor(96.0 * Math.sin(2.0 * (-Math.PI + (Math.PI / 20) * gateway))); + this.spawnNewGateway(new BlockPos(x, 75, z)); + this.setDirty(); ++ return true; // Paper - More DragonBattle API + } ++ return false; // Paper - More DragonBattle API + } + + public void spawnNewGateway(final BlockPos pos) { +@@ -456,6 +_,12 @@ + this.exitPortalLocation = this.exitPortalLocation.atY(Math.max(this.level.getMinY() + 1, this.exitPortalLocation.getY())); + this.setDirty(); + } ++ // Paper start - Prevent "softlocked" exit portal generation ++ else if (this.exitPortalLocation.getY() <= this.level.getMinY()) { ++ this.exitPortalLocation = this.exitPortalLocation.atY(this.level.getMinY() + 1); ++ this.setDirty(); ++ } ++ // Paper end - Prevent "softlocked" exit portal generation + + if (feature.place(FeatureConfiguration.NONE, this.level, this.level.getChunkSource().getGenerator(), RandomSource.create(), this.exitPortalLocation)) { + int chunkRadius = Mth.positiveCeilDiv(4, 16); +@@ -473,6 +_,7 @@ + dragon.snapTo(this.origin.getX(), 128 + this.origin.getY(), this.origin.getZ(), this.level.getRandom().nextFloat() * 360.0F, 0.0F); + this.level.addFreshEntity(dragon); + this.dragonUUID = dragon.getUUID(); ++ this.resetSpikeCrystals(); // Paper - Reset ender crystals on dragon spawn + this.setDirty(); + } + +@@ -485,6 +_,10 @@ + this.ticksSinceDragonSeen = 0; + if (dragon.hasCustomName()) { + this.dragonEvent.setName(dragon.getDisplayName()); ++ // Paper start - ensure reset EnderDragon boss event name ++ } else { ++ this.dragonEvent.setName(EVENT_DISPLAY_NAME); ++ // Paper end - ensure reset EnderDragon boss event name + } + } + } +@@ -517,7 +_,13 @@ + return this.hasPreviouslyKilledDragon; + } + +- public void tryRespawn() { ++ public boolean tryRespawn() { // CraftBukkit - return boolean ++ // Paper start - Perf: Do crystal-portal proximity check before entity lookup ++ return this.tryRespawn(null); ++ } ++ ++ public boolean tryRespawn(@Nullable final BlockPos placedEndCrystalPos) { // placedEndCrystalPos is null if the tryRespawn() call was not caused by a placed end crystal ++ // Paper end - Perf: Do crystal-portal proximity check before entity lookup + if (this.dragonKilled && this.respawnStage == null) { + BlockPos location = this.exitPortalLocation; + if (location == null) { +@@ -532,6 +_,20 @@ + + location = this.exitPortalLocation; + } ++ // Paper start - Perf: Do crystal-portal proximity check before entity lookup ++ if (placedEndCrystalPos != null && !this.level.paperConfig().misc.allowRemoteEnderDragonRespawning) { ++ int dy = placedEndCrystalPos.getY() - location.getY(); ++ if (dy != 0 && dy != 1) { ++ return false; ++ } ++ ++ int dx = placedEndCrystalPos.getX() - location.getX(); ++ int dz = placedEndCrystalPos.getZ() - location.getZ(); ++ if (!((dx >= -1 && dx <= 1 && dz >= -4 && dz <= 4) || (dx >= -4 && dx <= 4 && dz >= -1 && dz <= 1))) { ++ return false; ++ } ++ } ++ // Paper end - Perf: Do crystal-portal proximity check before entity lookup + + List crystals = Lists.newArrayList(); + BlockPos center = location.above(1); +@@ -539,18 +_,19 @@ + for (Direction direction : Direction.Plane.HORIZONTAL) { + List found = this.level.getEntitiesOfClass(EndCrystal.class, new AABB(center.relative(direction, 3))); + if (found.isEmpty()) { +- return; ++ return false; // CraftBukkit - return value + } + + crystals.addAll(found); + } + + LOGGER.debug("Found all crystals, respawning dragon."); +- this.respawnDragon(crystals); ++ return this.respawnDragon(crystals); // CraftBukkit - return value + } ++ return false; // CraftBukkit - return value + } + +- public void respawnDragon(final List crystals) { ++ public boolean respawnDragon(final List crystals) { // CraftBukkit - return boolean + if (this.dragonKilled && this.respawnStage == null) { + for (BlockPattern.BlockPatternMatch portal = this.findExitPortal(); portal != null; portal = this.findExitPortal()) { + for (int x = 0; x < this.exitPortalPattern.getWidth(); x++) { +@@ -570,7 +_,9 @@ + this.spawnExitPortal(false); + this.respawnCrystals = crystals.stream().map(EntityReference::of).toList(); + this.setDirty(); ++ return true; // CraftBukkit - return value + } ++ return false; // CraftBukkit - return value + } + + public void resetSpikeCrystals() { diff --git a/paper-server/patches/sources/net/minecraft/world/level/entity/EntityAccess.java.patch b/paper-server/patches/sources/net/minecraft/world/level/entity/EntityAccess.java.patch index f5c795978fc0..d662e184a0ca 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/entity/EntityAccess.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/entity/EntityAccess.java.patch @@ -10,7 +10,7 @@ + this.setRemoved(removalReason, null); + } + -+ void setRemoved(Entity.RemovalReason removalReason, @javax.annotation.Nullable org.bukkit.event.entity.EntityRemoveEvent.Cause eventCause); ++ void setRemoved(Entity.RemovalReason removalReason, org.bukkit.event.entity.EntityRemoveEvent.@org.jspecify.annotations.Nullable Cause eventCause); + // CraftBukkit end - add Bukkit remove cause boolean shouldBeSaved(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/entity/EntitySection.java.patch b/paper-server/patches/sources/net/minecraft/world/level/entity/EntitySection.java.patch index ba9179927ba5..aed26f44b809 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/entity/EntitySection.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/entity/EntitySection.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/entity/EntitySection.java +++ b/net/minecraft/world/level/entity/EntitySection.java @@ -19,6 +_,12 @@ - this.storage = new ClassInstanceMultiMap<>(entityClazz); + this.storage = new ClassInstanceMultiMap<>(entityClass); } + // Paper start - support retrieving all entities, regardless of whether they are accessible @@ -10,6 +10,6 @@ + } + // Paper end - support retrieving all entities, regardless of whether they are accessible + - public void add(T entity) { + public void add(final T entity) { this.storage.add(entity); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/entity/EntitySectionStorage.java.patch b/paper-server/patches/sources/net/minecraft/world/level/entity/EntitySectionStorage.java.patch index a6c808035bd3..724276223efc 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/entity/EntitySectionStorage.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/entity/EntitySectionStorage.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/entity/EntitySectionStorage.java +++ b/net/minecraft/world/level/entity/EntitySectionStorage.java -@@ -34,6 +_,16 @@ - this.intialSectionVisibility = initialSectionVisibility; +@@ -35,6 +_,16 @@ + this.intialSectionVisibility = intialSectionVisibility; } + // Paper start - support retrieving all entities, regardless of whether they are accessible @@ -14,6 +14,6 @@ + } + // Paper end - support retrieving all entities, regardless of whether they are accessible + - public void forEachAccessibleNonEmptySection(AABB boundingBox, AbortableIterationConsumer> consumer) { - int sectionPosCoord = SectionPos.posToSectionCoord(boundingBox.minX - 2.0); - int sectionPosCoord1 = SectionPos.posToSectionCoord(boundingBox.minY - 4.0); + public void forEachAccessibleNonEmptySection(final AABB bb, final AbortableIterationConsumer> output) { + int xMin = SectionPos.posToSectionCoord(bb.minX - 2.0); + int yMin = SectionPos.posToSectionCoord(bb.minY - 4.0); diff --git a/paper-server/patches/sources/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java.patch b/paper-server/patches/sources/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java.patch index a9e2d66b722a..11f5a46a69da 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java.patch @@ -7,5 +7,5 @@ - private final EntitySectionStorage sectionStorage; + public final EntitySectionStorage sectionStorage; // Paper - public - public LevelEntityGetterAdapter(EntityLookup visibleEntities, EntitySectionStorage sectionStorage) { + public LevelEntityGetterAdapter(final EntityLookup visibleEntities, final EntitySectionStorage sectionStorage) { this.visibleEntities = visibleEntities; diff --git a/paper-server/patches/sources/net/minecraft/world/level/entity/PersistentEntitySectionManager.java.patch b/paper-server/patches/sources/net/minecraft/world/level/entity/PersistentEntitySectionManager.java.patch index 62e3e6784e6c..1008a9bc4535 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/entity/PersistentEntitySectionManager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/entity/PersistentEntitySectionManager.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/level/entity/PersistentEntitySectionManager.java +++ b/net/minecraft/world/level/entity/PersistentEntitySectionManager.java -@@ -52,6 +_,16 @@ +@@ -53,6 +_,16 @@ this.entityGetter = new LevelEntityGetterAdapter<>(this.visibleEntityStorage, this.sectionStorage); } + // CraftBukkit start - add method to get all entities in chunk + public List getEntities(ChunkPos chunkPos) { -+ return this.sectionStorage.getExistingSectionsInChunk(chunkPos.toLong()).flatMap(EntitySection::getEntities).map(entity -> (Entity) entity).collect(Collectors.toList()); ++ return this.sectionStorage.getExistingSectionsInChunk(chunkPos.pack()).flatMap(EntitySection::getEntities).map(entity -> (Entity) entity).collect(Collectors.toList()); + } + + public boolean isPending(long pair) { @@ -14,27 +14,27 @@ + } + // CraftBukkit end + - void removeSectionIfEmpty(long sectionKey, EntitySection section) { + private void removeSectionIfEmpty(final long sectionPos, final EntitySection section) { if (section.isEmpty()) { - this.sectionStorage.remove(sectionKey); -@@ -59,6 +_,7 @@ + this.sectionStorage.remove(sectionPos); +@@ -60,6 +_,7 @@ } - private boolean addEntityUuid(T entity) { + private boolean addEntityUuid(final T entity) { + org.spigotmc.AsyncCatcher.catchOp("Entity add by UUID"); // Paper if (!this.knownUuids.add(entity.getUUID())) { LOGGER.warn("UUID of added entity already exists: {}", entity); return false; -@@ -72,6 +_,17 @@ +@@ -73,6 +_,17 @@ } - private boolean addEntity(T entity, boolean worldGenSpawned) { + private boolean addEntity(final T entity, final boolean loaded) { + org.spigotmc.AsyncCatcher.catchOp("Entity add"); // Paper + // Paper start - chunk system hooks + // I don't want to know why this is a generic type. + Entity entityCasted = (Entity)entity; + boolean wasRemoved = entityCasted.isRemoved(); -+ boolean screened = ca.spottedleaf.moonrise.common.PlatformHooks.get().screenEntity((net.minecraft.server.level.ServerLevel)entityCasted.level(), entityCasted, worldGenSpawned, true); ++ boolean screened = ca.spottedleaf.moonrise.common.PlatformHooks.get().screenEntity((net.minecraft.server.level.ServerLevel)entityCasted.level(), entityCasted, loaded, true); + if ((!wasRemoved && entityCasted.isRemoved()) || !screened) { + // removed by callback + return false; @@ -43,99 +43,99 @@ if (!this.addEntityUuid(entity)) { return false; } else { -@@ -113,19 +_,23 @@ +@@ -114,19 +_,23 @@ } - void startTicking(T entity) { + private void startTicking(final T entity) { + org.spigotmc.AsyncCatcher.catchOp("Entity start ticking"); // Paper this.callbacks.onTickingStart(entity); } - void stopTicking(T entity) { + private void stopTicking(final T entity) { + org.spigotmc.AsyncCatcher.catchOp("Entity stop ticking"); // Paper this.callbacks.onTickingEnd(entity); } - void startTracking(T entity) { + private void startTracking(final T entity) { + org.spigotmc.AsyncCatcher.catchOp("Entity start tracking"); // Paper this.visibleEntityStorage.add(entity); this.callbacks.onTrackingStart(entity); } - void stopTracking(T entity) { + private void stopTracking(final T entity) { + org.spigotmc.AsyncCatcher.catchOp("Entity stop tracking"); // Paper this.callbacks.onTrackingEnd(entity); this.visibleEntityStorage.remove(entity); } -@@ -136,6 +_,7 @@ +@@ -137,6 +_,7 @@ } - public void updateChunkStatus(ChunkPos pos, Visibility visibility) { + public void updateChunkStatus(final ChunkPos pos, final Visibility chunkStatus) { + org.spigotmc.AsyncCatcher.catchOp("Update chunk status"); // Paper - long packedChunkPos = pos.toLong(); - if (visibility == Visibility.HIDDEN) { - this.chunkVisibility.remove(packedChunkPos); -@@ -169,6 +_,7 @@ + long chunkPosKey = pos.pack(); + if (chunkStatus == Visibility.HIDDEN) { + this.chunkVisibility.remove(chunkPosKey); +@@ -170,6 +_,7 @@ } - public void ensureChunkQueuedForLoad(long chunkPosValue) { + public void ensureChunkQueuedForLoad(final long chunkPos) { + org.spigotmc.AsyncCatcher.catchOp("Entity chunk save"); // Paper - PersistentEntitySectionManager.ChunkLoadStatus chunkLoadStatus = this.chunkLoadStatuses.get(chunkPosValue); + PersistentEntitySectionManager.ChunkLoadStatus chunkLoadStatus = this.chunkLoadStatuses.get(chunkPos); if (chunkLoadStatus == PersistentEntitySectionManager.ChunkLoadStatus.FRESH) { - this.requestChunkLoad(chunkPosValue); -@@ -176,6 +_,11 @@ + this.requestChunkLoad(chunkPos); +@@ -177,6 +_,11 @@ } - private boolean storeChunkSections(long chunkPosValue, Consumer entityAction) { + private boolean storeChunkSections(final long chunkPos, final Consumer savedEntityVisitor) { + // CraftBukkit start -+ return storeChunkSections(chunkPosValue, entityAction, false); ++ return storeChunkSections(chunkPos, savedEntityVisitor, false); + } -+ private boolean storeChunkSections(long chunkPosValue, Consumer entityAction, boolean callEvent) { ++ private boolean storeChunkSections(final long chunkPos, final Consumer savedEntityVisitor, final boolean callEvent) { + // CraftBukkit end - PersistentEntitySectionManager.ChunkLoadStatus chunkLoadStatus = this.chunkLoadStatuses.get(chunkPosValue); + PersistentEntitySectionManager.ChunkLoadStatus chunkLoadStatus = this.chunkLoadStatuses.get(chunkPos); if (chunkLoadStatus == PersistentEntitySectionManager.ChunkLoadStatus.PENDING) { return false; -@@ -186,6 +_,7 @@ +@@ -187,6 +_,7 @@ .collect(Collectors.toList()); - if (list.isEmpty()) { + if (rootEntitiesToSave.isEmpty()) { if (chunkLoadStatus == PersistentEntitySectionManager.ChunkLoadStatus.LOADED) { -+ if (callEvent) org.bukkit.craftbukkit.event.CraftEventFactory.callEntitiesUnloadEvent(((net.minecraft.world.level.chunk.storage.EntityStorage) this.permanentStorage).level, new ChunkPos(chunkPosValue), ImmutableList.of()); // CraftBukkit - this.permanentStorage.storeEntities(new ChunkEntities<>(new ChunkPos(chunkPosValue), ImmutableList.of())); ++ if (callEvent) org.bukkit.craftbukkit.event.CraftEventFactory.callEntitiesUnloadEvent(((net.minecraft.world.level.chunk.storage.EntityStorage) this.permanentStorage).level, ChunkPos.unpack(chunkPos), ImmutableList.of()); // CraftBukkit + this.permanentStorage.storeEntities(new ChunkEntities<>(ChunkPos.unpack(chunkPos), ImmutableList.of())); } -@@ -194,6 +_,7 @@ - this.requestChunkLoad(chunkPosValue); +@@ -195,6 +_,7 @@ + this.requestChunkLoad(chunkPos); return false; } else { -+ if (callEvent) org.bukkit.craftbukkit.event.CraftEventFactory.callEntitiesUnloadEvent(((net.minecraft.world.level.chunk.storage.EntityStorage) this.permanentStorage).level, new ChunkPos(chunkPosValue), list.stream().map(entity -> (Entity) entity).collect(Collectors.toList())); // CraftBukkit - this.permanentStorage.storeEntities(new ChunkEntities<>(new ChunkPos(chunkPosValue), list)); - list.forEach(entityAction); ++ if (callEvent) org.bukkit.craftbukkit.event.CraftEventFactory.callEntitiesUnloadEvent(((net.minecraft.world.level.chunk.storage.EntityStorage) this.permanentStorage).level, ChunkPos.unpack(chunkPos), rootEntitiesToSave.stream().map(entity -> (Entity) entity).collect(Collectors.toList())); // CraftBukkit + this.permanentStorage.storeEntities(new ChunkEntities<>(ChunkPos.unpack(chunkPos), rootEntitiesToSave)); + rootEntitiesToSave.forEach(savedEntityVisitor); return true; -@@ -202,6 +_,7 @@ +@@ -203,6 +_,7 @@ } - private void requestChunkLoad(long chunkPosValue) { + private void requestChunkLoad(final long chunkKey) { + org.spigotmc.AsyncCatcher.catchOp("Entity chunk load request"); // Paper - this.chunkLoadStatuses.put(chunkPosValue, PersistentEntitySectionManager.ChunkLoadStatus.PENDING); - ChunkPos chunkPos = new ChunkPos(chunkPosValue); - this.permanentStorage.loadEntities(chunkPos).thenAccept(this.loadingInbox::add).exceptionally(throwable -> { -@@ -211,7 +_,8 @@ + this.chunkLoadStatuses.put(chunkKey, PersistentEntitySectionManager.ChunkLoadStatus.PENDING); + ChunkPos pos = ChunkPos.unpack(chunkKey); + this.permanentStorage.loadEntities(pos).thenAccept(this.loadingInbox::add).exceptionally(t -> { +@@ -212,7 +_,8 @@ } - private boolean processChunkUnload(long chunkPosValue) { -- boolean flag = this.storeChunkSections(chunkPosValue, entity -> entity.getPassengersAndSelf().forEach(this::unloadEntity)); + private boolean processChunkUnload(final long chunkKey) { +- boolean storeSuccessful = this.storeChunkSections(chunkKey, entity -> entity.getPassengersAndSelf().forEach(this::unloadEntity)); + org.spigotmc.AsyncCatcher.catchOp("Entity chunk unload process"); // Paper -+ boolean flag = this.storeChunkSections(chunkPosValue, entity -> entity.getPassengersAndSelf().forEach(this::unloadEntity), true); // CraftBukkit - add boolean for event call - if (!flag) { ++ boolean storeSuccessful = this.storeChunkSections(chunkKey, entity -> entity.getPassengersAndSelf().forEach(this::unloadEntity), true); // CraftBukkit - add boolean for event call + if (!storeSuccessful) { return false; } else { -@@ -221,7 +_,7 @@ +@@ -222,7 +_,7 @@ } - private void unloadEntity(EntityAccess entity) { -- entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK); -+ entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK, org.bukkit.event.entity.EntityRemoveEvent.Cause.UNLOAD); // CraftBukkit - add Bukkit remove cause - entity.setLevelCallback(EntityInLevelCallback.NULL); + private void unloadEntity(final EntityAccess e) { +- e.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK); ++ e.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK, org.bukkit.event.entity.EntityRemoveEvent.Cause.UNLOAD); // CraftBukkit - add Bukkit remove cause + e.setLevelCallback(EntityInLevelCallback.NULL); } @@ -231,14 +_,20 @@ @@ -143,13 +143,13 @@ public void processPendingLoads() { + org.spigotmc.AsyncCatcher.catchOp("Entity chunk process pending loads"); // Paper - ChunkEntities chunkEntities; - while ((chunkEntities = this.loadingInbox.poll()) != null) { - chunkEntities.getEntities().forEach(entity -> this.addEntity((T)entity, true)); - this.chunkLoadStatuses.put(chunkEntities.getPos().toLong(), PersistentEntitySectionManager.ChunkLoadStatus.LOADED); + ChunkEntities loadedChunk; + while ((loadedChunk = this.loadingInbox.poll()) != null) { + loadedChunk.getEntities().forEach(e -> this.addEntity((T)e, true)); + this.chunkLoadStatuses.put(loadedChunk.getPos().pack(), PersistentEntitySectionManager.ChunkLoadStatus.LOADED); + // CraftBukkit start - call entity load event -+ List entities = this.getEntities(chunkEntities.getPos()); -+ org.bukkit.craftbukkit.event.CraftEventFactory.callEntitiesLoadEvent(((net.minecraft.world.level.chunk.storage.EntityStorage) this.permanentStorage).level, chunkEntities.getPos(), entities); ++ List entities = this.getEntities(loadedChunk.getPos()); ++ org.bukkit.craftbukkit.event.CraftEventFactory.callEntitiesLoadEvent(((net.minecraft.world.level.chunk.storage.EntityStorage) this.permanentStorage).level, loadedChunk.getPos(), entities); + // CraftBukkit end } } @@ -164,17 +164,17 @@ public void autoSave() { + org.spigotmc.AsyncCatcher.catchOp("Entity manager autosave"); // Paper - this.getAllChunksToSave().forEach(packedChunkPos -> { - boolean flag = this.chunkVisibility.get(packedChunkPos) == Visibility.HIDDEN; - if (flag) { + this.getAllChunksToSave().forEach(chunkKey -> { + boolean shouldUnload = this.chunkVisibility.get(chunkKey) == Visibility.HIDDEN; + if (shouldUnload) { @@ -267,6 +_,7 @@ } public void saveAll() { + org.spigotmc.AsyncCatcher.catchOp("Entity manager save"); // Paper - LongSet allChunksToSave = this.getAllChunksToSave(); + LongSet chunksToSave = this.getAllChunksToSave(); - while (!allChunksToSave.isEmpty()) { + while (!chunksToSave.isEmpty()) { @@ -283,7 +_,13 @@ @Override @@ -190,18 +190,18 @@ this.permanentStorage.close(); } -@@ -384,6 +_,7 @@ - BlockPos blockPos = this.entity.blockPosition(); - long packedSectionPos = SectionPos.asLong(blockPos); - if (packedSectionPos != this.currentSectionKey) { +@@ -386,6 +_,7 @@ + BlockPos pos = this.entity.blockPosition(); + long newSectionPos = SectionPos.asLong(pos); + if (newSectionPos != this.currentSectionKey) { + org.spigotmc.AsyncCatcher.catchOp("Entity move"); // Paper - Visibility status = this.currentSection.getStatus(); + Visibility previousStatus = this.currentSection.getStatus(); if (!this.currentSection.remove(this.entity)) { PersistentEntitySectionManager.LOGGER -@@ -431,6 +_,7 @@ +@@ -433,6 +_,7 @@ @Override - public void onRemove(Entity.RemovalReason reason) { + public void onRemove(final Entity.RemovalReason reason) { + org.spigotmc.AsyncCatcher.catchOp("Entity remove"); // Paper if (!this.currentSection.remove(this.entity)) { PersistentEntitySectionManager.LOGGER diff --git a/paper-server/patches/sources/net/minecraft/world/level/gameevent/DynamicGameEventListener.java.patch b/paper-server/patches/sources/net/minecraft/world/level/gameevent/DynamicGameEventListener.java.patch index a920d35331d0..5cec11d673fa 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/gameevent/DynamicGameEventListener.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/gameevent/DynamicGameEventListener.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/level/gameevent/DynamicGameEventListener.java @@ -40,7 +_,7 @@ - private static void ifChunkExists(LevelReader level, @Nullable SectionPos sectionPos, Consumer dispatcherConsumer) { + private static void ifChunkExists(final LevelReader level, final @Nullable SectionPos sectionPos, final Consumer action) { if (sectionPos != null) { - ChunkAccess chunk = level.getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.FULL, false); + ChunkAccess chunk = level.getChunkIfLoadedImmediately(sectionPos.getX(), sectionPos.getZ()); // Paper - Perf: can cause sync loads while completing a chunk, resulting in deadlock if (chunk != null) { - dispatcherConsumer.accept(chunk.getListenerRegistry(sectionPos.y())); + action.accept(chunk.getListenerRegistry(sectionPos.y())); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/gameevent/GameEvent.java.patch b/paper-server/patches/sources/net/minecraft/world/level/gameevent/GameEvent.java.patch index efcac86d58ce..95139c54f334 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/gameevent/GameEvent.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/gameevent/GameEvent.java.patch @@ -3,7 +3,7 @@ @@ -85,7 +_,7 @@ } - private static Holder.Reference register(String name, int notificationRadius) { + private static Holder.Reference register(final String name, final int notificationRadius) { - return Registry.registerForHolder(BuiltInRegistries.GAME_EVENT, Identifier.withDefaultNamespace(name), new GameEvent(notificationRadius)); + return io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerForHolderWithListeners(BuiltInRegistries.GAME_EVENT, Identifier.withDefaultNamespace(name), new GameEvent(notificationRadius)); // Paper - run with listeners } diff --git a/paper-server/patches/sources/net/minecraft/world/level/gameevent/GameEventDispatcher.java.patch b/paper-server/patches/sources/net/minecraft/world/level/gameevent/GameEventDispatcher.java.patch index 043949fc6716..56b704270597 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/gameevent/GameEventDispatcher.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/gameevent/GameEventDispatcher.java.patch @@ -1,27 +1,27 @@ --- a/net/minecraft/world/level/gameevent/GameEventDispatcher.java +++ b/net/minecraft/world/level/gameevent/GameEventDispatcher.java @@ -22,6 +_,15 @@ - public void post(Holder gameEvent, Vec3 pos, GameEvent.Context context) { - int notificationRadius = gameEvent.value().notificationRadius(); - BlockPos blockPos = BlockPos.containing(pos); + public void post(final Holder gameEvent, final Vec3 position, final GameEvent.Context context) { + int radius = gameEvent.value().notificationRadius(); + BlockPos center = BlockPos.containing(position); + // CraftBukkit start + org.bukkit.event.world.GenericGameEvent apiEvent = new org.bukkit.event.world.GenericGameEvent( -+ org.bukkit.craftbukkit.CraftGameEvent.minecraftHolderToBukkit(gameEvent), org.bukkit.craftbukkit.util.CraftLocation.toBukkit(blockPos, this.level), -+ (context.sourceEntity() == null) ? null : context.sourceEntity().getBukkitEntity(), notificationRadius, !org.bukkit.Bukkit.isPrimaryThread()); ++ org.bukkit.craftbukkit.CraftGameEvent.minecraftHolderToBukkit(gameEvent), org.bukkit.craftbukkit.util.CraftLocation.toBukkit(center, this.level), ++ (context.sourceEntity() == null) ? null : context.sourceEntity().getBukkitEntity(), radius, !org.bukkit.Bukkit.isPrimaryThread()); + if (!apiEvent.callEvent()) { + return; + } -+ notificationRadius = apiEvent.getRadius(); ++ radius = apiEvent.getRadius(); + // CraftBukkit end - int sectionPosCoord = SectionPos.blockToSectionCoord(blockPos.getX() - notificationRadius); - int sectionPosCoord1 = SectionPos.blockToSectionCoord(blockPos.getY() - notificationRadius); - int sectionPosCoord2 = SectionPos.blockToSectionCoord(blockPos.getZ() - notificationRadius); + int sectionMinX = SectionPos.blockToSectionCoord(center.getX() - radius); + int sectionMinY = SectionPos.blockToSectionCoord(center.getY() - radius); + int sectionMinZ = SectionPos.blockToSectionCoord(center.getZ() - radius); @@ -40,7 +_,7 @@ - for (int i = sectionPosCoord; i <= sectionPosCoord3; i++) { - for (int i1 = sectionPosCoord2; i1 <= sectionPosCoord5; i1++) { -- ChunkAccess chunkNow = this.level.getChunkSource().getChunkNow(i, i1); -+ ChunkAccess chunkNow = this.level.getChunkIfLoadedImmediately(i, i1); // Paper - Use getChunkIfLoadedImmediately - if (chunkNow != null) { - for (int i2 = sectionPosCoord1; i2 <= sectionPosCoord4; i2++) { - flag |= chunkNow.getListenerRegistry(i2).visitInRangeListeners(gameEvent, pos, context, listenerVisitor); + for (int chunkX = sectionMinX; chunkX <= sectionMaxX; chunkX++) { + for (int chunkZ = sectionMinZ; chunkZ <= sectionMaxZ; chunkZ++) { +- ChunkAccess chunk = this.level.getChunkSource().getChunkNow(chunkX, chunkZ); ++ ChunkAccess chunk = this.level.getChunkIfLoadedImmediately(chunkX, chunkZ); // Paper - Use getChunkIfLoadedImmediately + if (chunk != null) { + for (int section = sectionMinY; section <= sectionMaxY; section++) { + applicable |= chunk.getListenerRegistry(section).visitInRangeListeners(gameEvent, position, context, visitListeners); diff --git a/paper-server/patches/sources/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java.patch b/paper-server/patches/sources/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java.patch index c56f5cbcf66d..e102937119a9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java.patch @@ -1,27 +1,27 @@ --- a/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java +++ b/net/minecraft/world/level/gameevent/vibrations/VibrationSystem.java @@ -123,7 +_,7 @@ - public static Codec CODEC = RecordCodecBuilder.create( - instance -> instance.group( - VibrationInfo.CODEC.lenientOptionalFieldOf("event").forGetter(data -> Optional.ofNullable(data.currentVibration)), + public static final Codec CODEC = RecordCodecBuilder.create( + i -> i.group( + VibrationInfo.CODEC.lenientOptionalFieldOf("event").forGetter(o -> Optional.ofNullable(o.currentVibration)), - VibrationSelector.CODEC.fieldOf("selector").forGetter(VibrationSystem.Data::getSelectionStrategy), + VibrationSelector.CODEC.optionalFieldOf("selector").xmap(o -> o.orElseGet(VibrationSelector::new), Optional::of).forGetter(VibrationSystem.Data::getSelectionStrategy), // Paper - fix MapLike spam for missing "selector" in 1.19.2 ExtraCodecs.NON_NEGATIVE_INT.fieldOf("event_delay").orElse(0).forGetter(VibrationSystem.Data::getTravelTimeInTicks) ) .apply( -@@ -212,7 +_,14 @@ +@@ -219,7 +_,14 @@ return false; } else { - Vec3 vec3 = position.get(); -- if (!vibrationUser.canReceiveVibration(level, BlockPos.containing(pos), gameEvent, context)) { + Vec3 destination = listenerSourcePos.get(); +- if (!user.canReceiveVibration(level, BlockPos.containing(sourcePosition), event, context)) { + // CraftBukkit start -+ boolean defaultCancel = !vibrationUser.canReceiveVibration(level, BlockPos.containing(pos), gameEvent, context); ++ boolean defaultCancel = !user.canReceiveVibration(level, BlockPos.containing(sourcePosition), event, context); + Entity entity = context.sourceEntity(); -+ org.bukkit.event.block.BlockReceiveGameEvent event1 = new org.bukkit.event.block.BlockReceiveGameEvent(org.bukkit.craftbukkit.CraftGameEvent.minecraftHolderToBukkit(gameEvent), org.bukkit.craftbukkit.block.CraftBlock.at(level, BlockPos.containing(vec3)), (entity == null) ? null : entity.getBukkitEntity()); ++ org.bukkit.event.block.BlockReceiveGameEvent event1 = new org.bukkit.event.block.BlockReceiveGameEvent(org.bukkit.craftbukkit.CraftGameEvent.minecraftHolderToBukkit(event), org.bukkit.craftbukkit.block.CraftBlock.at(level, BlockPos.containing(sourcePosition)), (entity == null) ? null : entity.getBukkitEntity()); + event1.setCancelled(defaultCancel); + level.getCraftServer().getPluginManager().callEvent(event1); + if (event1.isCancelled()) { + // CraftBukkit end return false; - } else if (isOccluded(level, pos, vec3)) { + } else if (isOccluded(level, sourcePosition, destination)) { return false; diff --git a/paper-server/patches/sources/net/minecraft/world/level/gamerules/GameRuleMap.java.patch b/paper-server/patches/sources/net/minecraft/world/level/gamerules/GameRuleMap.java.patch index cee18bdd0ce5..e33be7abc8ba 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/gamerules/GameRuleMap.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/gamerules/GameRuleMap.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/level/gamerules/GameRuleMap.java +++ b/net/minecraft/world/level/gamerules/GameRuleMap.java -@@ -15,9 +_,16 @@ - public static final Codec CODEC = Codec., Object>dispatchedMap(BuiltInRegistries.GAME_RULE.byNameCodec(), GameRule::valueCodec) - .xmap(GameRuleMap::ofTrusted, GameRuleMap::map); +@@ -22,9 +_,16 @@ + Identifier.withDefaultNamespace("game_rules"), GameRuleMap::of, CODEC, DataFixTypes.SAVED_DATA_GAME_RULES + ); private final Reference2ObjectMap, Object> map; + private final @Nullable Object[] idAccess; // Paper - array backed gamerule access - array storage - GameRuleMap(Reference2ObjectMap, Object> map) { + private GameRuleMap(final Reference2ObjectMap, Object> map) { this.map = map; + // Paper start - array backed gamerule access - array storage + idAccess = new Object[GameRule.LAST_GAMERULE_INDEX]; @@ -16,27 +16,32 @@ + // Paper end - array backed gamerule access - array storage } - private static GameRuleMap ofTrusted(Map, Object> rules) { -@@ -39,18 +_,20 @@ + private static GameRuleMap ofTrusted(final Map, Object> map) { +@@ -46,16 +_,17 @@ } - public boolean has(GameRule rule) { -- return this.map.containsKey(rule); -+ return this.idAccess[rule.gameRuleIndex] != null; // Paper - array backed gamerule access - the gamerule map does not allow null values, so this suffices for a contains check (see net.minecraft.world.level.gamerules.GameRuleMap.setGameRule's non-null checks) + public boolean has(final GameRule gameRule) { +- return this.map.containsKey(gameRule); ++ return this.idAccess[gameRule.gameRuleIndex] != null; // Paper - array backed gamerule access - the gamerule map does not allow null values, so this suffices for a contains check (see net.minecraft.world.level.gamerules.GameRuleMap.setGameRule's non-null checks) } - public @Nullable T get(GameRule rule) { -- return (T)this.map.get(rule); -+ return (T) this.idAccess[rule.gameRuleIndex]; // Paper - array backed gamerule access + public @Nullable T get(final GameRule gameRule) { +- return (T)this.map.get(gameRule); ++ return (T)this.idAccess[gameRule.gameRuleIndex]; // Paper - array backed gamerule access } - public void set(GameRule rule, T value) { - this.map.put(rule, value); -+ this.idAccess[rule.gameRuleIndex] = value; // Paper - array backed gamerule access - above map is kept in sync instead of fully removing the map, which needs more diff. + public void set(final GameRule gameRule, final T value) { + this.setDirty(); + this.map.put(gameRule, value); ++ this.idAccess[gameRule.gameRuleIndex] = value; // Paper - array backed gamerule access - above map is kept in sync instead of fully removing the map, which needs more diff. } - public @Nullable T remove(GameRule rule) { -+ this.idAccess[rule.gameRuleIndex] = null; // Paper - array backed gamerule access - return (T)this.map.remove(rule); + public void reset(final GameRule gameRule) { +@@ -64,6 +_,7 @@ + + public @Nullable T remove(final GameRule gameRule) { + this.setDirty(); ++ this.idAccess[gameRule.gameRuleIndex] = null; // Paper - array backed gamerule access + return (T)this.map.remove(gameRule); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/gamerules/GameRules.java.patch b/paper-server/patches/sources/net/minecraft/world/level/gamerules/GameRules.java.patch index afb8178f0114..5c080676454e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/gamerules/GameRules.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/gamerules/GameRules.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/gamerules/GameRules.java +++ b/net/minecraft/world/level/gamerules/GameRules.java -@@ -17,6 +_,14 @@ - import org.jspecify.annotations.Nullable; +@@ -21,6 +_,13 @@ public class GameRules { + private static final Logger LOGGER = LogUtils.getLogger(); + // Paper start - allow disabling gamerule limits + private static final boolean DISABLE_LIMITS = Boolean.getBoolean("paper.disableGameRuleLimits"); + @@ -11,59 +11,58 @@ + return DISABLE_LIMITS ? unlimited : limit; + } + // Paper end - allow disabling gamerule limits -+ public static final GameRule ADVANCE_TIME = registerBoolean("advance_time", GameRuleCategory.UPDATES, !SharedConstants.DEBUG_WORLD_RECREATE); public static final GameRule ADVANCE_WEATHER = registerBoolean("advance_weather", GameRuleCategory.UPDATES, !SharedConstants.DEBUG_WORLD_RECREATE); public static final GameRule ALLOW_ENTERING_NETHER_USING_PORTALS = registerBoolean( -@@ -114,13 +_,15 @@ +@@ -130,13 +_,15 @@ } } -- public void set(GameRule rule, T value, @Nullable MinecraftServer server) { -+ public void set(GameRule rule, T value, net.minecraft.server.level.@Nullable ServerLevel level) { // Paper - per-world game rules - if (!this.rules.has(rule)) { - throw new IllegalArgumentException("Tried to set invalid game rule"); +- public void set(final GameRule gameRule, final T value, final @Nullable MinecraftServer server) { ++ public void set(final GameRule gameRule, final T value, final net.minecraft.server.level.@Nullable ServerLevel level) { // Paper - per-world game rules + if (!this.rules.has(gameRule)) { + LOGGER.warn("Tried to set invalid game rule '{}' to value '{}'", gameRule.getIdentifierWithFallback(), value); } else { - this.rules.set(rule, value); + this.rules.set(gameRule, value); - if (server != null) { -- server.onGameRuleChanged(rule, value); +- server.onGameRuleChanged(gameRule, value); + // Paper start - per-world game rules + if (level != null) { -+ level.getServer().onGameRuleChanged(level, rule, value); ++ level.getServer().onGameRuleChanged(level, gameRule, value); + // Paper end - per-world game rules } } } -@@ -129,16 +_,22 @@ - return new GameRules(enabledFeatures, this.rules); +@@ -145,16 +_,22 @@ + return new GameRules(enabledFeatures, GameRuleMap.copyOf(this.rules)); } -- public void setAll(GameRules gameRules, @Nullable MinecraftServer server) { -- this.setAll(gameRules.rules, server); +- public void setAll(final GameRules other, final @Nullable MinecraftServer server) { +- this.setAll(other.rules, server); - } - -- public void setAll(GameRuleMap rules, @Nullable MinecraftServer server) { -- rules.keySet().forEach(rule -> this.setFromOther(rules, (GameRule)rule, server)); +- public void setAll(final GameRuleMap gameRulesMap, final @Nullable MinecraftServer server) { +- gameRulesMap.keySet().forEach(gameRule -> this.setFromOther(gameRulesMap, (GameRule)gameRule, server)); - } - -- private void setFromOther(GameRuleMap rules, GameRule rule, @Nullable MinecraftServer server) { -- this.set(rule, Objects.requireNonNull(rules.get(rule)), server); +- private void setFromOther(final GameRuleMap gameRulesMap, final GameRule gameRule, final @Nullable MinecraftServer server) { +- this.set(gameRule, Objects.requireNonNull(gameRulesMap.get(gameRule)), server); + // Paper start - per-world game rules -+ public void setAll(GameRules gameRules, net.minecraft.server.level.@Nullable ServerLevel level) { -+ this.setAll(gameRules.rules, level); ++ public void setAll(final GameRules other, final net.minecraft.server.level.@Nullable ServerLevel level) { ++ this.setAll(other.rules, level); + // Paper end - per-world game rules + } + + // Paper start - per-world game rules -+ public void setAll(GameRuleMap rules, net.minecraft.server.level.@Nullable ServerLevel level) { -+ rules.keySet().forEach(rule -> this.setFromOther(rules, (GameRule)rule, level)); ++ public void setAll(final GameRuleMap gameRulesMap, final net.minecraft.server.level.@Nullable ServerLevel level) { ++ gameRulesMap.keySet().forEach(gameRule -> this.setFromOther(gameRulesMap, (GameRule)gameRule, level)); + // Paper end - per-world game rules + } + + // Paper start - per-world game rules -+ private void setFromOther(GameRuleMap rules, GameRule rule, net.minecraft.server.level.@Nullable ServerLevel level) { -+ this.set(rule, Objects.requireNonNull(rules.get(rule)), level); ++ private void setFromOther(final GameRuleMap gameRulesMap, final GameRule gameRule, final net.minecraft.server.level.@Nullable ServerLevel level) { ++ this.set(gameRule, Objects.requireNonNull(gameRulesMap.get(gameRule)), level); + // Paper end - per-world game rules } - public void visitGameRuleTypes(GameRuleTypeVisitor visitor) { + public void visitGameRuleTypes(final GameRuleTypeVisitor visitor) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/DensityFunctions.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/DensityFunctions.java.patch index 95a6587fba98..03ee533dca7a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/DensityFunctions.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/DensityFunctions.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/levelgen/DensityFunctions.java +++ b/net/minecraft/world/level/levelgen/DensityFunctions.java -@@ -512,6 +_,16 @@ +@@ -532,6 +_,16 @@ ); private static final float ISLAND_THRESHOLD = -0.9F; private final SimplexNoise islandNoise; @@ -15,41 +15,39 @@ + private static final ThreadLocal> noiseCache = ThreadLocal.withInitial(java.util.WeakHashMap::new); + // Paper end - Perf: Optimize end generation - public EndIslandDensityFunction(long seed) { - RandomSource randomSource = new LegacyRandomSource(seed); -@@ -524,15 +_,31 @@ - int i1 = z / 2; - int i2 = x % 2; - int i3 = z % 2; -- float f = 100.0F - Mth.sqrt(x * x + z * z) * 8.0F; -+ float f = 100.0F - (io.papermc.paper.configuration.GlobalConfiguration.get().misc.fixFarEndTerrainGeneration ? Mth.sqrt((long)x * (long)x + (long)z * (long)z) : Mth.sqrt(x * x + z * z)) * 8.0F; // Paper - cast ints to long when MC-159283 fix enabled - f = Mth.clamp(f, -100.0F, 80.0F); + public EndIslandDensityFunction(final long seed) { + RandomSource islandRandom = new LegacyRandomSource(seed); +@@ -544,15 +_,31 @@ + int chunkZ = sectionZ / 2; + int subSectionX = sectionX % 2; + int subSectionZ = sectionZ % 2; +- float doffs = 100.0F - Mth.sqrt(sectionX * sectionX + sectionZ * sectionZ) * 8.0F; ++ float doffs = 100.0F - (ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC159283() ? Mth.sqrt((long) sectionX * (long) sectionX + (long) sectionZ * (long) sectionZ) : Mth.sqrt(sectionX * sectionX + sectionZ * sectionZ)) * 8.0F; // Paper - cast ints to long when MC-159283 fix enabled + doffs = Mth.clamp(doffs, -100.0F, 80.0F); -+ NoiseCache cache = noiseCache.get().computeIfAbsent(noise, noiseKey -> new NoiseCache()); // Paper - Perf: Optimize end generation - for (int i4 = -12; i4 <= 12; i4++) { - for (int i5 = -12; i5 <= 12; i5++) { -- long l = i + i4; -- long l1 = i1 + i5; -+ long l = i + i4; final int chunkX = (int) l; // Paper - OBFHELPER -+ long l1 = i1 + i5; final int chunkZ = (int) l1; // Paper - OBFHELPER ++ NoiseCache cache = noiseCache.get().computeIfAbsent(islandNoise, noiseKey -> new NoiseCache()); // Paper - Perf: Optimize end generation + for (int xo = -12; xo <= 12; xo++) { + for (int zo = -12; zo <= 12; zo++) { + long totalChunkX = chunkX + xo; + long totalChunkZ = chunkZ + zo; + // Paper start - Perf: Optimize end generation by using a noise cache -+ final long chunkKey = net.minecraft.world.level.ChunkPos.asLong(chunkX, chunkZ); ++ final long chunkKey = net.minecraft.world.level.ChunkPos.pack((int) totalChunkX, (int) totalChunkZ); + final int cacheIndex = (int) it.unimi.dsi.fastutil.HashCommon.mix(chunkKey) & 8191; -+ float f1 = Float.MIN_VALUE; // noise value ++ float islandSize = Float.MIN_VALUE; // noise value + if (cache.keys[cacheIndex] == chunkKey) { + // Use cache -+ f1 = cache.values[cacheIndex]; ++ islandSize = cache.values[cacheIndex]; + } else { -+ // Vanilla function - if (l * l + l1 * l1 > 4096L && noise.getValue(l, l1) < -0.9F) { -- float f1 = (Mth.abs((float)l) * 3439.0F + Mth.abs((float)l1) * 147.0F) % 13.0F + 9.0F; -+ f1 = (Mth.abs((float)l) * 3439.0F + Mth.abs((float)l1) * 147.0F) % 13.0F + 9.0F; -+ } ++ // Vanilla function + if (totalChunkX * totalChunkX + totalChunkZ * totalChunkZ > 4096L && islandNoise.getValue(totalChunkX, totalChunkZ) < -0.9F) { +- float islandSize = (Mth.abs((float)totalChunkX) * 3439.0F + Mth.abs((float)totalChunkZ) * 147.0F) % 13.0F + 9.0F; ++ islandSize = (Mth.abs((float)totalChunkX) * 3439.0F + Mth.abs((float) totalChunkZ) * 147.0F) % 13.0F + 9.0F; ++ } + cache.keys[cacheIndex] = chunkKey; -+ cache.values[cacheIndex] = f1; ++ cache.values[cacheIndex] = islandSize; + } -+ if (f1 != Float.MIN_VALUE) { ++ if (islandSize != Float.MIN_VALUE) { + // Paper end - Perf: Optimize end generation - float f2 = i2 - i4 * 2; - float f3 = i3 - i5 * 2; - float f4 = 100.0F - Mth.sqrt(f2 * f2 + f3 * f3) * f1; + float xd = subSectionX - xo * 2; + float zd = subSectionZ - zo * 2; + float newDoffs = 100.0F - Mth.sqrt(xd * xd + zd * zd) * islandSize; diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/FlatLevelSource.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/FlatLevelSource.java.patch index f1cd4a895b1e..53fb63d80f04 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/FlatLevelSource.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/FlatLevelSource.java.patch @@ -1,28 +1,28 @@ --- a/net/minecraft/world/level/levelgen/FlatLevelSource.java +++ b/net/minecraft/world/level/levelgen/FlatLevelSource.java -@@ -33,17 +_,22 @@ +@@ -32,17 +_,22 @@ private final FlatLevelGeneratorSettings settings; - public FlatLevelSource(FlatLevelGeneratorSettings settings) { -- super(new FixedBiomeSource(settings.getBiome()), Util.memoize(settings::adjustGenerationSettings)); + public FlatLevelSource(final FlatLevelGeneratorSettings generatorSettings) { +- super(new FixedBiomeSource(generatorSettings.getBiome()), Util.memoize(generatorSettings::adjustGenerationSettings)); + // CraftBukkit start -+ this(settings, new FixedBiomeSource(settings.getBiome())); ++ this(generatorSettings, new FixedBiomeSource(generatorSettings.getBiome())); + } -+ public FlatLevelSource(FlatLevelGeneratorSettings settings, net.minecraft.world.level.biome.BiomeSource biomeSource) { -+ super(biomeSource, Util.memoize(settings::adjustGenerationSettings)); ++ public FlatLevelSource(final FlatLevelGeneratorSettings generatorSettings, final net.minecraft.world.level.biome.BiomeSource biomeSource) { ++ super(biomeSource, Util.memoize(generatorSettings::adjustGenerationSettings)); + // CraftBukkit end - this.settings = settings; + this.settings = generatorSettings; } @Override -- public ChunkGeneratorStructureState createState(HolderLookup structureSetLookup, RandomState randomState, long seed) { -+ public ChunkGeneratorStructureState createState(HolderLookup structureSetLookup, RandomState randomState, long seed, org.spigotmc.SpigotWorldConfig conf) { // Spigot - Stream> stream = this.settings +- public ChunkGeneratorStructureState createState(final HolderLookup structureSets, final RandomState randomState, final long levelSeed) { ++ public ChunkGeneratorStructureState createState(final HolderLookup structureSets, final RandomState randomState, final long levelSeed, final org.spigotmc.SpigotWorldConfig conf) { // Spigot + Stream> structures = this.settings .structureOverrides() .map(HolderSet::stream) - .orElseGet(() -> structureSetLookup.listElements().map(reference -> (Holder)reference)); -- return ChunkGeneratorStructureState.createForFlat(randomState, seed, this.biomeSource, stream); -+ return ChunkGeneratorStructureState.createForFlat(randomState, seed, this.biomeSource, stream, conf); // Spigot + .orElseGet(() -> structureSets.listElements().map(e -> (Holder)e)); +- return ChunkGeneratorStructureState.createForFlat(randomState, levelSeed, this.biomeSource, structures); ++ return ChunkGeneratorStructureState.createForFlat(randomState, levelSeed, this.biomeSource, structures, conf); // Spigot } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java.patch index 0b411864f6da..dfd1c2e4d7be 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java +++ b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -@@ -226,7 +_,7 @@ +@@ -266,7 +_,7 @@ @Override - public void buildSurface(WorldGenRegion region, StructureManager structureManager, RandomState random, ChunkAccess chunk) { - if (!SharedConstants.debugVoidTerrain(chunk.getPos()) && !SharedConstants.DEBUG_DISABLE_SURFACE) { -- WorldGenerationContext worldGenerationContext = new WorldGenerationContext(this, region); -+ WorldGenerationContext worldGenerationContext = new WorldGenerationContext(this, region, region.getMinecraftWorld()); // Paper - Flat bedrock generator settings + public void buildSurface(final WorldGenRegion region, final StructureManager structureManager, final RandomState randomState, final ChunkAccess protoChunk) { + if (!SharedConstants.debugVoidTerrain(protoChunk.getPos()) && !SharedConstants.DEBUG_DISABLE_SURFACE) { +- WorldGenerationContext context = new WorldGenerationContext(this, region); ++ WorldGenerationContext context = new WorldGenerationContext(this, region, region.getMinecraftWorld()); // Paper - Flat bedrock generator settings this.buildSurface( - chunk, - worldGenerationContext, -@@ -269,7 +_,7 @@ - NoiseChunk noiseChunk = chunk.getOrCreateNoiseChunk(chunkAccess -> this.createNoiseChunk(chunkAccess, structureManager, Blender.of(region), random)); + protoChunk, + context, +@@ -314,7 +_,7 @@ + NoiseChunk noiseChunk = chunk.getOrCreateNoiseChunk(c -> this.createNoiseChunk(c, structureManager, Blender.of(region), randomState)); Aquifer aquifer = noiseChunk.aquifer(); - CarvingContext carvingContext = new CarvingContext( -- this, region.registryAccess(), chunk.getHeightAccessorForGeneration(), noiseChunk, random, this.settings.value().surfaceRule() -+ this, region.registryAccess(), chunk.getHeightAccessorForGeneration(), noiseChunk, random, this.settings.value().surfaceRule(), region.getMinecraftWorld() // Paper - Flat bedrock generator settings + CarvingContext context = new CarvingContext( +- this, region.registryAccess(), chunk.getHeightAccessorForGeneration(), noiseChunk, randomState, this.settings.value().surfaceRule() ++ this, region.registryAccess(), chunk.getHeightAccessorForGeneration(), noiseChunk, randomState, this.settings.value().surfaceRule(), region.getMinecraftWorld() // Paper - Flat bedrock generator settings ); - CarvingMask carvingMask = ((ProtoChunk)chunk).getOrCreateCarvingMask(); + CarvingMask mask = ((ProtoChunk)chunk).getOrCreateCarvingMask(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/PatrolSpawner.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/PatrolSpawner.java.patch index 76603adde963..5bbe28c783cf 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/PatrolSpawner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/PatrolSpawner.java.patch @@ -1,27 +1,27 @@ --- a/net/minecraft/world/level/levelgen/PatrolSpawner.java +++ b/net/minecraft/world/level/levelgen/PatrolSpawner.java -@@ -18,17 +_,47 @@ +@@ -18,17 +_,51 @@ @Override - public void tick(ServerLevel level, boolean spawnEnemies) { + public void tick(final ServerLevel level, final boolean spawnEnemies) { + if (level.paperConfig().entities.behavior.pillagerPatrols.disable || level.paperConfig().entities.behavior.pillagerPatrols.spawnChance == 0) return; // Paper - Add option to disable pillager patrols & Pillager patrol spawn settings and per player options if (spawnEnemies) { if (level.getGameRules().get(GameRules.SPAWN_PATROLS)) { - RandomSource randomSource = level.random; + RandomSource random = level.getRandom(); - this.nextTick--; - if (this.nextTick <= 0) { -- this.nextTick = this.nextTick + 12000 + randomSource.nextInt(1200); +- this.nextTick = this.nextTick + 12000 + random.nextInt(1200); - if (level.isBrightOutside()) { -- if (randomSource.nextInt(5) == 0) { -- int size = level.players().size(); +- if (random.nextInt(5) == 0) { +- int playerCount = level.players().size(); + // this.nextTick--; + // Paper start - Pillager patrol spawn settings and per player options -+ int size = level.players().size(); -+ if (size < 1) { ++ int playerCount = level.players().size(); ++ if (playerCount < 1) { + return; + } + -+ net.minecraft.server.level.ServerPlayer player = level.players().get(randomSource.nextInt(size)); ++ net.minecraft.server.level.ServerPlayer player = level.players().get(random.nextInt(playerCount)); + if (player.isSpectator()) { + return; + } @@ -37,30 +37,34 @@ + if (patrolSpawnDelay <= 0) { + long dayCount; + if (level.paperConfig().entities.behavior.pillagerPatrols.start.perPlayer) { -+ dayCount = player.getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.PLAY_TIME)) / 24000L; // PLAY_TIME is counting in ticks ++ dayCount = player.getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.PLAY_TIME)) / net.minecraft.SharedConstants.TICKS_PER_GAME_DAY; // PLAY_TIME is counting in ticks + } else { -+ dayCount = level.getDayCount(); ++ dayCount = level ++ .registryAccess() ++ .get(net.minecraft.world.timeline.Timelines.OVERWORLD_DAY) ++ .map(timeline -> timeline.value().getPeriodCount(level.clockManager())) ++ .orElse(0); + } + if (level.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.perPlayer) { -+ player.patrolSpawnDelay += level.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.ticks + randomSource.nextInt(1200); ++ player.patrolSpawnDelay += level.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.ticks + random.nextInt(1200); + } else { -+ this.nextTick += level.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.ticks + randomSource.nextInt(1200); ++ this.nextTick += level.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.ticks + random.nextInt(1200); + } + + if (dayCount >= level.paperConfig().entities.behavior.pillagerPatrols.start.day && level.isBrightOutside()) { -+ if (randomSource.nextDouble() < level.paperConfig().entities.behavior.pillagerPatrols.spawnChance) { ++ if (random.nextDouble() < level.paperConfig().entities.behavior.pillagerPatrols.spawnChance) { + // Paper end - Pillager patrol spawn settings and per player options - if (size >= 1) { -- Player player = level.players().get(randomSource.nextInt(size)); + if (playerCount >= 1) { +- Player player = level.players().get(random.nextInt(playerCount)); if (!player.isSpectator()) { if (!level.isCloseToVillage(player.blockPosition(), 2)) { - int i = (24 + randomSource.nextInt(24)) * (randomSource.nextBoolean() ? -1 : 1); -@@ -84,7 +_,7 @@ + int x = (24 + random.nextInt(24)) * (random.nextBoolean() ? -1 : 1); +@@ -80,7 +_,7 @@ - patrollingMonster.setPos(pos.getX(), pos.getY(), pos.getZ()); - patrollingMonster.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), EntitySpawnReason.PATROL, null); -- level.addFreshEntityWithPassengers(patrollingMonster); -+ level.addFreshEntityWithPassengers(patrollingMonster, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.PATROL); // CraftBukkit + mob.setPos(pos.getX(), pos.getY(), pos.getZ()); + mob.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), EntitySpawnReason.PATROL, null); +- level.addFreshEntityWithPassengers(mob); ++ level.addFreshEntityWithPassengers(mob, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.PATROL); // CraftBukkit return true; } else { return false; diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch index 602489891fc3..86cac164e604 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/level/levelgen/PhantomSpawner.java +++ b/net/minecraft/world/level/levelgen/PhantomSpawner.java @@ -25,13 +_,22 @@ - public void tick(ServerLevel level, boolean spawnEnemies) { + public void tick(final ServerLevel level, final boolean spawnEnemies) { if (spawnEnemies) { if (level.getGameRules().get(GameRules.SPAWN_PHANTOMS)) { + // Paper start - Ability to control player's insomnia and phantoms @@ -9,28 +9,28 @@ + return; + } + // Paper end - Ability to control player's insomnia and phantoms - RandomSource randomSource = level.random; + RandomSource random = level.getRandom(); this.nextTick--; if (this.nextTick <= 0) { -- this.nextTick = this.nextTick + (60 + randomSource.nextInt(60)) * 20; +- this.nextTick = this.nextTick + (60 + random.nextInt(60)) * 20; + // Paper start - Ability to control player's insomnia and phantoms + int spawnAttemptMinSeconds = level.paperConfig().entities.behavior.phantomsSpawnAttemptMinSeconds; + int spawnAttemptMaxSeconds = level.paperConfig().entities.behavior.phantomsSpawnAttemptMaxSeconds; -+ this.nextTick += (spawnAttemptMinSeconds + randomSource.nextInt(spawnAttemptMaxSeconds - spawnAttemptMinSeconds + 1)) * 20; ++ this.nextTick += (spawnAttemptMinSeconds + random.nextInt(spawnAttemptMaxSeconds - spawnAttemptMinSeconds + 1)) * 20; + // Paper end - Ability to control player's insomnia and phantoms if (level.getSkyDarken() >= 5 || !level.dimensionType().hasSkyLight()) { - for (ServerPlayer serverPlayer : level.players()) { -- if (!serverPlayer.isSpectator()) { -+ if (!serverPlayer.isSpectator() && (!level.paperConfig().entities.behavior.phantomsDoNotSpawnOnCreativePlayers || !serverPlayer.isCreative())) { // Paper - Add phantom creative and insomniac controls - BlockPos blockPos = serverPlayer.blockPosition(); - if (!level.dimensionType().hasSkyLight() || blockPos.getY() >= level.getSeaLevel() && level.canSeeSky(blockPos)) { - DifficultyInstance currentDifficultyAt = level.getCurrentDifficultyAt(blockPos); -@@ -50,13 +_,23 @@ - int i2 = 1 + randomSource.nextInt(currentDifficultyAt.getDifficulty().getId() + 1); + for (ServerPlayer player : level.players()) { +- if (!player.isSpectator()) { ++ if (!player.isSpectator() && (!level.paperConfig().entities.behavior.phantomsDoNotSpawnOnCreativePlayers || !player.isCreative())) { // Paper - Add phantom creative and insomniac controls + BlockPos playerPos = player.blockPosition(); + if (!level.dimensionType().hasSkyLight() || playerPos.getY() >= level.getSeaLevel() && level.canSeeSky(playerPos)) { + DifficultyInstance difficulty = level.getCurrentDifficultyAt(playerPos); +@@ -50,11 +_,21 @@ + int groupSize = 1 + random.nextInt(difficulty.getDifficulty().getId() + 1); - for (int i3 = 0; i3 < i2; i3++) { + for (int i = 0; i < groupSize; i++) { + // Paper start - PhantomPreSpawnEvent -+ com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent event = new com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent(org.bukkit.craftbukkit.util.CraftLocation.toBukkit(blockPos1, level), serverPlayer.getBukkitEntity(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); ++ com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent event = new com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent(org.bukkit.craftbukkit.util.CraftLocation.toBukkit(spawnPos, level), player.getBukkitEntity(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); + if (!event.callEvent()) { + if (event.shouldAbortSpawn()) { + break; @@ -40,11 +40,9 @@ + // Paper end - PhantomPreSpawnEvent Phantom phantom = EntityType.PHANTOM.create(level, EntitySpawnReason.NATURAL); if (phantom != null) { -+ phantom.spawningEntity = serverPlayer.getUUID(); // Paper - PhantomPreSpawnEvent - phantom.snapTo(blockPos1, 0.0F, 0.0F); - spawnGroupData = phantom.finalizeSpawn( - level, currentDifficultyAt, EntitySpawnReason.NATURAL, spawnGroupData - ); ++ phantom.spawningEntity = player.getUUID(); // Paper - PhantomPreSpawnEvent + phantom.snapTo(spawnPos, 0.0F, 0.0F); + groupData = phantom.finalizeSpawn(level, difficulty, EntitySpawnReason.NATURAL, groupData); - level.addFreshEntityWithPassengers(phantom); + level.addFreshEntityWithPassengers(phantom, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit } diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/WorldGenerationContext.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/WorldGenerationContext.java.patch index 4bbbbabfb314..392f7a377cc7 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/WorldGenerationContext.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/WorldGenerationContext.java.patch @@ -5,16 +5,16 @@ private final int minY; private final int height; + // Paper start - Flat bedrock generator settings -+ private final @javax.annotation.Nullable net.minecraft.world.level.Level serverLevel; ++ private final net.minecraft.world.level.@org.jspecify.annotations.Nullable Level level; - public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor level) { -+ this(generator, level, null); + public WorldGenerationContext(final ChunkGenerator generator, final LevelHeightAccessor heightAccessor) { ++ this(generator, heightAccessor, null); + } -+ public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor level, @javax.annotation.Nullable net.minecraft.world.level.Level serverLevel) { -+ this.serverLevel = serverLevel; ++ public WorldGenerationContext(final ChunkGenerator generator, final LevelHeightAccessor heightAccessor, final net.minecraft.world.level.@org.jspecify.annotations.Nullable Level level) { ++ this.level = level; + // Paper end - Flat bedrock generator settings - this.minY = Math.max(level.getMinY(), generator.getMinY()); - this.height = Math.min(level.getHeight(), generator.getGenDepth()); + this.minY = Math.max(heightAccessor.getMinY(), generator.getMinY()); + this.height = Math.min(heightAccessor.getHeight(), generator.getGenDepth()); } @@ -19,4 +_,13 @@ public int getGenDepth() { @@ -23,10 +23,10 @@ + + // Paper start - Flat bedrock generator settings + public net.minecraft.world.level.Level level() { -+ if (this.serverLevel == null) { ++ if (this.level == null) { + throw new NullPointerException("WorldGenerationContext was initialized without a Level, but WorldGenerationContext#level was called"); + } -+ return this.serverLevel; ++ return this.level; + } + // Paper end - Flat bedrock generator settings } diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/WorldgenRandom.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/WorldgenRandom.java.patch new file mode 100644 index 000000000000..b40f2970ed1c --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/WorldgenRandom.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/levelgen/WorldgenRandom.java ++++ b/net/minecraft/world/level/levelgen/WorldgenRandom.java +@@ -64,7 +_,7 @@ + } + + public void setLargeFeatureWithSalt(final long seed, final int x, final int z, final int blend) { +- long result = x * 341873128712L + z * 132897987541L + seed + blend; ++ long result = x * 341873128712L + z * 132897987541L + seed + blend; // Paper - diff on change for CustomChunkGenerator + this.setSeed(result); + } + diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/carver/CarvingContext.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/carver/CarvingContext.java.patch index 63d7237796a3..03efad035a1d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/carver/CarvingContext.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/carver/CarvingContext.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/level/levelgen/carver/CarvingContext.java +++ b/net/minecraft/world/level/levelgen/carver/CarvingContext.java @@ -27,9 +_,9 @@ - LevelHeightAccessor level, - NoiseChunk noiseChunk, - RandomState randomState, -- SurfaceRules.RuleSource surfaceRule -+ SurfaceRules.RuleSource surfaceRule, @javax.annotation.Nullable net.minecraft.world.level.Level serverLevel // Paper - Flat bedrock generator settings + final LevelHeightAccessor heightAccessor, + final NoiseChunk noiseChunk, + final RandomState randomState, +- final SurfaceRules.RuleSource surfaceRule ++ final SurfaceRules.RuleSource surfaceRule, net.minecraft.world.level.@org.jspecify.annotations.Nullable Level serverLevel // Paper - Flat bedrock generator settings ) { -- super(generator, level); -+ super(generator, level, serverLevel); // Paper - Flat bedrock generator settings +- super(generator, heightAccessor); ++ super(generator, heightAccessor, serverLevel); // Paper - Flat bedrock generator settings this.registryAccess = registryAccess; this.noiseChunk = noiseChunk; this.randomState = randomState; diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/EndPlatformFeature.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/EndPlatformFeature.java.patch index 0d1ce408f040..af4afb034803 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/EndPlatformFeature.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/EndPlatformFeature.java.patch @@ -3,29 +3,29 @@ @@ -19,6 +_,13 @@ } - public static void createEndPlatform(ServerLevelAccessor level, BlockPos pos, boolean dropBlocks) { + public static void createEndPlatform(final ServerLevelAccessor newLevel, final BlockPos origin, final boolean dropResources) { + // CraftBukkit start -+ createEndPlatform(level, pos, dropBlocks, null); ++ createEndPlatform(newLevel, origin, dropResources, null); + } + -+ public static void createEndPlatform(ServerLevelAccessor level, BlockPos pos, boolean dropBlocks, @javax.annotation.Nullable net.minecraft.world.entity.Entity entity) { -+ org.bukkit.craftbukkit.util.BlockStateListPopulator blockList = new org.bukkit.craftbukkit.util.BlockStateListPopulator(level); ++ public static void createEndPlatform(final ServerLevelAccessor newLevel, final BlockPos origin, final boolean dropResources, final net.minecraft.world.entity.@org.jspecify.annotations.Nullable Entity entity) { ++ org.bukkit.craftbukkit.util.BlockStateListPopulator blockList = new org.bukkit.craftbukkit.util.BlockStateListPopulator(newLevel); + // CraftBukkit end - BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); + BlockPos.MutableBlockPos pos = origin.mutable(); - for (int i = -2; i <= 2; i++) { + for (int dz = -2; dz <= 2; dz++) { @@ -26,15 +_,31 @@ - for (int i2 = -1; i2 < 3; i2++) { - BlockPos blockPos = mutableBlockPos.set(pos).move(i1, i2, i); - Block block = i2 == -1 ? Blocks.OBSIDIAN : Blocks.AIR; -- if (!level.getBlockState(blockPos).is(block)) { + for (int dy = -1; dy < 3; dy++) { + BlockPos blockPos = pos.set(origin).move(dx, dy, dz); + Block block = dy == -1 ? Blocks.OBSIDIAN : Blocks.AIR; +- if (!newLevel.getBlockState(blockPos).is(block)) { + if (!blockList.getBlockState(blockPos).is(block)) { // CraftBukkit - if (dropBlocks) { -- level.destroyBlock(blockPos, true, null); + if (dropResources) { +- newLevel.destroyBlock(blockPos, true, null); + blockList.destroyBlock(blockPos, true, null); // CraftBukkit } -- level.setBlock(blockPos, block.defaultBlockState(), Block.UPDATE_ALL); +- newLevel.setBlock(blockPos, block.defaultBlockState(), Block.UPDATE_ALL); + blockList.setBlock(blockPos, block.defaultBlockState(), Block.UPDATE_ALL); // CraftBukkit } } @@ -35,14 +35,14 @@ + // CraftBukkit start + // SPIGOT-7746: Entity will only be null during world generation, which is async, so just generate without event + if (entity != null) { -+ org.bukkit.World bworld = level.getLevel().getWorld(); ++ org.bukkit.World bworld = newLevel.getLevel().getWorld(); + org.bukkit.event.world.PortalCreateEvent portalEvent = new org.bukkit.event.world.PortalCreateEvent((java.util.List) (java.util.List) blockList.getSnapshotBlocks(), bworld, entity.getBukkitEntity(), org.bukkit.event.world.PortalCreateEvent.CreateReason.END_PLATFORM); -+ level.getLevel().getCraftServer().getPluginManager().callEvent(portalEvent); ++ newLevel.getLevel().getCraftServer().getPluginManager().callEvent(portalEvent); + if (portalEvent.isCancelled()) return; + } + -+ if (dropBlocks) { -+ blockList.placeBlocks(state -> level.destroyBlock(state.getPosition(), true, null)); ++ if (dropResources) { ++ blockList.placeBlocks(state -> newLevel.destroyBlock(state.getPosition(), true, null)); + } else { + blockList.placeBlocks(); + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/SpikeFeature.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/EndSpikeFeature.java.patch similarity index 57% rename from paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/SpikeFeature.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/EndSpikeFeature.java.patch index c36bbede368b..8d9e18233d6a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/SpikeFeature.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/EndSpikeFeature.java.patch @@ -1,10 +1,10 @@ ---- a/net/minecraft/world/level/levelgen/feature/SpikeFeature.java -+++ b/net/minecraft/world/level/levelgen/feature/SpikeFeature.java -@@ -113,6 +_,7 @@ +--- a/net/minecraft/world/level/levelgen/feature/EndSpikeFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/EndSpikeFeature.java +@@ -114,6 +_,7 @@ endCrystal.setBeamTarget(config.getCrystalBeamTarget()); endCrystal.setInvulnerable(config.isCrystalInvulnerable()); endCrystal.snapTo(spike.getCenterX() + 0.5, spike.getHeight() + 1, spike.getCenterZ() + 0.5, random.nextFloat() * 360.0F, 0.0F); + endCrystal.generatedByDragonFight = true; // Paper - Fix invulnerable end crystals level.addFreshEntity(endCrystal); - BlockPos blockPosx = endCrystal.blockPosition(); - this.setBlock(level, blockPosx.below(), Blocks.BEDROCK.defaultBlockState()); + BlockPos crystalPos = endCrystal.blockPosition(); + this.setBlock(level, crystalPos.below(), Blocks.BEDROCK.defaultBlockState()); diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java.patch index f034ad2abba2..45cf44178437 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java.patch @@ -1,10 +1,10 @@ --- a/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java +++ b/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java -@@ -26,6 +_,7 @@ +@@ -24,6 +_,7 @@ @Override - public void place(TreeDecorator.Context context) { + public void place(final TreeDecorator.Context context) { + if (context.logs().isEmpty()) return; // Paper - Fix crash when trying to generate without logs - RandomSource randomSource = context.random(); - if (!(randomSource.nextFloat() >= this.probability)) { - List list = context.logs(); + RandomSource random = context.random(); + if (!(random.nextFloat() >= this.probability)) { + List logs = context.logs(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/placement/PlacementContext.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/placement/PlacementContext.java.patch index 1d38b1ee7cc6..82a214358fc5 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/placement/PlacementContext.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/placement/PlacementContext.java.patch @@ -3,7 +3,7 @@ @@ -17,7 +_,7 @@ private final Optional topFeature; - public PlacementContext(WorldGenLevel level, ChunkGenerator generator, Optional topFeature) { + public PlacementContext(final WorldGenLevel level, final ChunkGenerator generator, final Optional topFeature) { - super(generator, level); + super(generator, level, level.getLevel()); // Paper - Flat bedrock generator settings this.level = level; diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java.patch deleted file mode 100644 index a160c1120baf..000000000000 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java.patch +++ /dev/null @@ -1,24 +0,0 @@ ---- a/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java -+++ b/net/minecraft/world/level/levelgen/structure/LegacyStructureDataHandler.java -@@ -260,17 +_,18 @@ - } - - public static Supplier getLegacyTagFixer(ResourceKey level, Supplier<@Nullable DimensionDataStorage> storage, DataFixer dataFixer) { -- if (level == Level.OVERWORLD) { -+ ResourceKey stemKey = net.minecraft.core.registries.Registries.levelToLevelStem(level); // CraftBukkit -+ if (stemKey == net.minecraft.world.level.dimension.LevelStem.OVERWORLD) { // CraftBukkit - return () -> new LegacyStructureDataHandler( - storage.get(), - ImmutableList.of("Monument", "Stronghold", "Village", "Mineshaft", "Temple", "Mansion"), - ImmutableList.of("Village", "Mineshaft", "Mansion", "Igloo", "Desert_Pyramid", "Jungle_Pyramid", "Swamp_Hut", "Stronghold", "Monument"), - dataFixer - ); -- } else if (level == Level.NETHER) { -+ } else if (stemKey == net.minecraft.world.level.dimension.LevelStem.NETHER) { // CraftBukkit - List list = ImmutableList.of("Fortress"); - return () -> new LegacyStructureDataHandler(storage.get(), list, list, dataFixer); -- } else if (level == Level.END) { -+ } else if (stemKey == net.minecraft.world.level.dimension.LevelStem.END) { // CraftBukkit - List list = ImmutableList.of("EndCity"); - return () -> new LegacyStructureDataHandler(storage.get(), list, list, dataFixer); - } else { diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructureCheck.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructureCheck.java.patch index 89126f2147fe..f3f18c1d4f9d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructureCheck.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructureCheck.java.patch @@ -10,14 +10,14 @@ private final RandomState randomState; private final LevelHeightAccessor heightAccessor; @@ -57,7 +_,7 @@ - ChunkScanAccess storageAccess, - RegistryAccess registryAccess, - StructureTemplateManager structureTemplateManager, -- ResourceKey dimension, -+ ResourceKey dimension, // Paper - fix missing CB diff - ChunkGenerator chunkGenerator, - RandomState randomState, - LevelHeightAccessor heightAccessor, + final ChunkScanAccess storageAccess, + final RegistryAccess registryAccess, + final StructureTemplateManager structureTemplateManager, +- final ResourceKey dimension, ++ final ResourceKey dimension, // Paper - fix missing CB diff + final ChunkGenerator chunkGenerator, + final RandomState randomState, + final LevelHeightAccessor heightAccessor, @@ -77,6 +_,20 @@ this.fixerUpper = fixerUpper; } @@ -36,15 +36,15 @@ + } + // Paper end - add missing structure seed configs + - public StructureCheckResult checkStart(ChunkPos chunkPos, Structure structure, StructurePlacement placement, boolean skipKnownStructures) { - long packedChunkPos = chunkPos.toLong(); - Object2IntMap map = this.loadedChunks.get(packedChunkPos); + public StructureCheckResult checkStart(final ChunkPos pos, final Structure structure, final StructurePlacement placement, final boolean requireUnreferenced) { + long posKey = pos.pack(); + Object2IntMap cachedResult = this.loadedChunks.get(posKey); @@ -86,7 +_,7 @@ - StructureCheckResult structureCheckResult = this.tryLoadFromStorage(chunkPos, structure, skipKnownStructures, packedChunkPos); - if (structureCheckResult != null) { - return structureCheckResult; -- } else if (!placement.applyAdditionalChunkRestrictions(chunkPos.x, chunkPos.z, this.seed)) { -+ } else if (!placement.applyAdditionalChunkRestrictions(chunkPos.x, chunkPos.z, this.seed, this.getSaltOverride(structure))) { // Paper - add missing structure seed configs + StructureCheckResult storageCheckResult = this.tryLoadFromStorage(pos, structure, requireUnreferenced, posKey); + if (storageCheckResult != null) { + return storageCheckResult; +- } else if (!placement.applyAdditionalChunkRestrictions(pos.x(), pos.z(), this.seed)) { ++ } else if (!placement.applyAdditionalChunkRestrictions(pos.x(), pos.z(), this.seed, this.getSaltOverride(structure))) { // Paper - add missing structure seed configs return StructureCheckResult.START_NOT_PRESENT; } else { - boolean flag = this.featureChecks + boolean isFeatureChunk = this.featureChecks diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructurePiece.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructurePiece.java.patch index 7921171a285f..e71d18ec3648 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructurePiece.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructurePiece.java.patch @@ -1,37 +1,37 @@ --- a/net/minecraft/world/level/levelgen/structure/StructurePiece.java +++ b/net/minecraft/world/level/levelgen/structure/StructurePiece.java -@@ -180,6 +_,11 @@ +@@ -188,6 +_,11 @@ } - level.setBlock(worldPos, state, Block.UPDATE_CLIENTS); -+ // CraftBukkit start - fluid handling is already done if we have a transformer generator access -+ if (level instanceof org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess && transformerAccess.canTransformBlocks()) { + level.setBlock(pos, blockState, Block.UPDATE_CLIENTS); ++ // CraftBukkit start - fluid handling is already done if we have a transformer level accessor ++ if (level instanceof org.bukkit.craftbukkit.util.TransformerLevelAccessor transformerAccessor && transformerAccessor.canTransformBlocks()) { + return; + } + // CraftBukkit end - FluidState fluidState = level.getFluidState(worldPos); + FluidState fluidState = level.getFluidState(pos); if (!fluidState.isEmpty()) { - level.scheduleTick(worldPos, fluidState.getType(), 0); -@@ -192,6 +_,48 @@ + level.scheduleTick(pos, fluidState.getType(), 0); +@@ -200,6 +_,48 @@ } } + // CraftBukkit start + private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); -+ protected boolean placeCraftBlockEntity(ServerLevelAccessor levelAccessor, BlockPos pos, org.bukkit.craftbukkit.block.CraftBlockEntityState craftBlockEntityState, @Block.UpdateFlags int flags) { -+ if (levelAccessor instanceof org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess && transformerAccess.canTransformBlocks()) { -+ return transformerAccess.setCraftBlock(pos, craftBlockEntityState, flags); ++ protected boolean placeCraftBlockEntity(ServerLevelAccessor level, BlockPos pos, org.bukkit.craftbukkit.block.CraftBlockEntityState craftBlockEntityState, @Block.UpdateFlags int updateFlags) { ++ if (level instanceof org.bukkit.craftbukkit.util.TransformerLevelAccessor transformerAccessor && transformerAccessor.canTransformBlocks()) { ++ return transformerAccessor.setCraftBlock(pos, craftBlockEntityState, updateFlags); + } + -+ boolean result = levelAccessor.setBlock(pos, craftBlockEntityState.getHandle(), flags); -+ BlockEntity blockEntity = levelAccessor.getBlockEntity(pos); ++ boolean result = level.setBlock(pos, craftBlockEntityState.getHandle(), updateFlags); ++ BlockEntity blockEntity = level.getBlockEntity(pos); + if (blockEntity != null) { + try (final net.minecraft.util.ProblemReporter.ScopedCollector problemReporter = new net.minecraft.util.ProblemReporter.ScopedCollector( + () -> "StructurePieceTranformers@" + pos.toShortString(), LOGGER + )) { + blockEntity.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + problemReporter, -+ levelAccessor.registryAccess(), ++ level.registryAccess(), + craftBlockEntityState.getSnapshotNBT() + )); + } @@ -39,44 +39,44 @@ + return result; + } + -+ protected void placeCraftSpawner(ServerLevelAccessor levelAccessor, BlockPos pos, org.bukkit.entity.EntityType entityType, @Block.UpdateFlags int flags) { ++ protected void placeCraftSpawner(ServerLevelAccessor level, BlockPos pos, org.bukkit.entity.EntityType entityType, @Block.UpdateFlags int updateFlags) { + // This method is used in structures that are generated by code and place spawners as they set the entity after the block was placed making it impossible for plugins to access that information -+ org.bukkit.craftbukkit.block.CraftCreatureSpawner spawner = (org.bukkit.craftbukkit.block.CraftCreatureSpawner) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(levelAccessor, pos, Blocks.SPAWNER.defaultBlockState(), null); ++ org.bukkit.craftbukkit.block.CraftCreatureSpawner spawner = (org.bukkit.craftbukkit.block.CraftCreatureSpawner) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, pos, Blocks.SPAWNER.defaultBlockState(), null); + spawner.setSpawnedType(entityType); -+ this.placeCraftBlockEntity(levelAccessor, pos, spawner, flags); ++ this.placeCraftBlockEntity(level, pos, spawner, updateFlags); + } + -+ protected void setCraftLootTable(ServerLevelAccessor levelAccessor, BlockPos pos, RandomSource randomSource, ResourceKey lootTable) { ++ protected void setCraftLootTable(ServerLevelAccessor level, BlockPos pos, RandomSource randomSource, ResourceKey lootTable) { + // This method is used in structures that use data markers to a loot table to loot containers as otherwise plugins won't have access to that information. -+ net.minecraft.world.level.block.entity.BlockEntity blockEntity = levelAccessor.getBlockEntity(pos); ++ net.minecraft.world.level.block.entity.BlockEntity blockEntity = level.getBlockEntity(pos); + if (blockEntity instanceof net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity lootContainerBlockEntity) { + lootContainerBlockEntity.setLootTable(lootTable, randomSource.nextLong()); -+ if (levelAccessor instanceof org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess && transformerAccess.canTransformBlocks()) { -+ transformerAccess.setCraftBlock(pos, (org.bukkit.craftbukkit.block.CraftBlockState) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(levelAccessor, pos, blockEntity.getBlockState(), lootContainerBlockEntity.saveWithFullMetadata(levelAccessor.registryAccess())), Block.UPDATE_ALL); ++ if (level instanceof org.bukkit.craftbukkit.util.TransformerLevelAccessor transformerAccessor && transformerAccessor.canTransformBlocks()) { ++ transformerAccessor.setCraftBlock(pos, (org.bukkit.craftbukkit.block.CraftBlockState) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, pos, blockEntity.getBlockState(), lootContainerBlockEntity.saveWithFullMetadata(level.registryAccess())), Block.UPDATE_ALL); + } + } + } + // CraftBukkit end + - protected boolean canBeReplaced(LevelReader level, int x, int y, int z, BoundingBox box) { + protected boolean canBeReplaced(final LevelReader level, final int x, final int y, final int z, final BoundingBox chunkBB) { return true; } -@@ -420,11 +_,17 @@ - state = reorient(level, pos, Blocks.CHEST.defaultBlockState()); +@@ -471,11 +_,17 @@ + blockState = reorient(level, pos, Blocks.CHEST.defaultBlockState()); } -- level.setBlock(pos, state, Block.UPDATE_CLIENTS); +- level.setBlock(pos, blockState, Block.UPDATE_CLIENTS); - BlockEntity blockEntity = level.getBlockEntity(pos); - if (blockEntity instanceof ChestBlockEntity) { - ((ChestBlockEntity)blockEntity).setLootTable(lootTable, random.nextLong()); - } + // CraftBukkit start -+ // level.setBlock(pos, state, Block.UPDATE_CLIENTS); ++ // level.setBlock(pos, blockState, Block.UPDATE_CLIENTS); + // BlockEntity blockEntity = level.getBlockEntity(pos); + // if (blockEntity instanceof ChestBlockEntity) { + // ((ChestBlockEntity)blockEntity).setLootTable(lootTable, random.nextLong()); + // } -+ org.bukkit.craftbukkit.block.CraftChest chestState = (org.bukkit.craftbukkit.block.CraftChest) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, pos, state, null); ++ org.bukkit.craftbukkit.block.CraftChest chestState = (org.bukkit.craftbukkit.block.CraftChest) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, pos, blockState, null); + chestState.setLootTable(org.bukkit.craftbukkit.CraftLootTable.minecraftToBukkit(lootTable)); + chestState.setSeed(random.nextLong()); + this.placeCraftBlockEntity(level, pos, chestState, Block.UPDATE_CLIENTS); @@ -84,18 +84,18 @@ return true; } else { -@@ -437,11 +_,28 @@ +@@ -495,11 +_,28 @@ ) { - BlockPos worldPos = this.getWorldPos(x, y, z); - if (box.isInside(worldPos) && !level.getBlockState(worldPos).is(Blocks.DISPENSER)) { -- this.placeBlock(level, Blocks.DISPENSER.defaultBlockState().setValue(DispenserBlock.FACING, facing), x, y, z, box); -- BlockEntity blockEntity = level.getBlockEntity(worldPos); + BlockPos pos = this.getWorldPos(x, y, z); + if (chunkBB.isInside(pos) && !level.getBlockState(pos).is(Blocks.DISPENSER)) { +- this.placeBlock(level, Blocks.DISPENSER.defaultBlockState().setValue(DispenserBlock.FACING, facing), x, y, z, chunkBB); +- BlockEntity blockEntity = level.getBlockEntity(pos); - if (blockEntity instanceof DispenserBlockEntity) { - ((DispenserBlockEntity)blockEntity).setLootTable(lootTable, random.nextLong()); - } + // CraftBukkit start -+ // this.placeBlock(level, Blocks.DISPENSER.defaultBlockState().setValue(DispenserBlock.FACING, facing), x, y, z, box); -+ // BlockEntity blockEntity = level.getBlockEntity(worldPos); ++ // this.placeBlock(level, Blocks.DISPENSER.defaultBlockState().setValue(DispenserBlock.FACING, facing), x, y, z, chunkBB); ++ // BlockEntity blockEntity = level.getBlockEntity(pos); + // if (blockEntity instanceof DispenserBlockEntity) { + // ((DispenserBlockEntity)blockEntity).setLootTable(lootTable, random.nextLong()); + // } @@ -110,10 +110,10 @@ + dispenserBlockState = dispenserBlockState.rotate(this.rotation); + } + -+ org.bukkit.craftbukkit.block.CraftDispenser dispenserState = (org.bukkit.craftbukkit.block.CraftDispenser) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, worldPos, dispenserBlockState, null); ++ org.bukkit.craftbukkit.block.CraftDispenser dispenserState = (org.bukkit.craftbukkit.block.CraftDispenser) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, pos, dispenserBlockState, null); + dispenserState.setLootTable(org.bukkit.craftbukkit.CraftLootTable.minecraftToBukkit(lootTable)); + dispenserState.setSeed(random.nextLong()); -+ this.placeCraftBlockEntity(level, worldPos, dispenserState, Block.UPDATE_CLIENTS); ++ this.placeCraftBlockEntity(level, pos, dispenserState, Block.UPDATE_CLIENTS); + // CraftBukkit end return true; diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructureStart.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructureStart.java.patch index c42ee6b4f724..a73e4a06613e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructureStart.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructureStart.java.patch @@ -10,45 +10,45 @@ + public org.bukkit.event.world.AsyncStructureGenerateEvent.Cause generationEventCause = org.bukkit.event.world.AsyncStructureGenerateEvent.Cause.WORLD_GENERATION; + // CraftBukkit end + - public StructureStart(Structure structure, ChunkPos chunkPos, int references, PiecesContainer pieceContainer) { + public StructureStart(final Structure structure, final ChunkPos chunkPos, final int references, final PiecesContainer pieceContainer) { this.structure = structure; this.chunkPos = chunkPos; -@@ -85,11 +_,23 @@ - BlockPos center = boundingBox.getCenter(); - BlockPos blockPos = new BlockPos(center.getX(), boundingBox.minY(), center.getZ()); +@@ -90,11 +_,23 @@ + BlockPos centerPos = centerBB.getCenter(); + BlockPos referencePos = new BlockPos(centerPos.getX(), centerBB.minY(), centerPos.getZ()); -- for (StructurePiece structurePiece : list) { -- if (structurePiece.getBoundingBox().intersects(box)) { -- structurePiece.postProcess(level, structureManager, generator, random, box, chunkPos, blockPos); +- for (StructurePiece next : pieces) { +- if (next.getBoundingBox().intersects(chunkBB)) { +- next.postProcess(level, structureManager, generator, random, chunkBB, chunkPos, referencePos); + // CraftBukkit start -+ // for (StructurePiece structurePiece : list) { -+ // if (structurePiece.getBoundingBox().intersects(box)) { -+ // structurePiece.postProcess(level, structureManager, generator, random, box, chunkPos, blockPos); ++ // for (StructurePiece next : pieces) { ++ // if (next.getBoundingBox().intersects(chunkBB)) { ++ // next.postProcess(level, structureManager, generator, random, chunkBB, chunkPos, referencePos); + // } + // } -+ List pieces = list.stream().filter(piece -> piece.getBoundingBox().intersects(box)).toList(); -+ if (!pieces.isEmpty()) { -+ org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess = new org.bukkit.craftbukkit.util.TransformerGeneratorAccess(); -+ transformerAccess.setDelegate(level); -+ transformerAccess.setStructureTransformer(new org.bukkit.craftbukkit.util.CraftStructureTransformer(this.generationEventCause, level, structureManager, this.structure, box, chunkPos)); -+ for (StructurePiece piece : pieces) { -+ piece.postProcess(transformerAccess, structureManager, generator, random, box, chunkPos, blockPos); ++ List intersectingPieces = pieces.stream().filter(piece -> piece.getBoundingBox().intersects(chunkBB)).toList(); ++ if (!intersectingPieces.isEmpty()) { ++ org.bukkit.craftbukkit.util.TransformerLevelAccessor transformerAccessor = new org.bukkit.craftbukkit.util.TransformerLevelAccessor(); ++ transformerAccessor.setDelegate(level); ++ transformerAccessor.setStructureTransformer(new org.bukkit.craftbukkit.util.CraftStructureTransformer(this.generationEventCause, level, structureManager, this.structure, chunkBB, chunkPos)); ++ for (StructurePiece piece : intersectingPieces) { ++ piece.postProcess(transformerAccessor, structureManager, generator, random, chunkBB, chunkPos, referencePos); } -+ transformerAccess.getStructureTransformer().discard(); ++ transformerAccessor.getStructureTransformer().discard(); } + // CraftBukkit end - this.structure.afterPlace(level, structureManager, generator, random, box, chunkPos, this.pieceContainer); + this.structure.afterPlace(level, structureManager, generator, random, chunkBB, chunkPos, this.pieceContainer); } -@@ -97,6 +_,11 @@ +@@ -102,6 +_,11 @@ - public CompoundTag createTag(StructurePieceSerializationContext context, ChunkPos chunkPos) { - CompoundTag compoundTag = new CompoundTag(); + public CompoundTag createTag(final StructurePieceSerializationContext context, final ChunkPos chunkPos) { + CompoundTag tag = new CompoundTag(); + // CraftBukkit start - store persistent data in nbt + if (!this.persistentDataContainer.isEmpty()) { -+ compoundTag.put("StructureBukkitValues", this.persistentDataContainer.toTagCompound()); ++ tag.put("StructureBukkitValues", this.persistentDataContainer.toTagCompound()); + } + // CraftBukkit end if (this.isValid()) { - compoundTag.putString("id", context.registryAccess().lookupOrThrow(Registries.STRUCTURE).getKey(this.structure).toString()); - compoundTag.putInt("ChunkX", chunkPos.x); + tag.putString("id", context.registryAccess().lookupOrThrow(Registries.STRUCTURE).getKey(this.structure).toString()); + tag.putInt("ChunkX", chunkPos.x()); diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java.patch index 7ddef4667fd1..fa848bf4d8da 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java.patch @@ -5,79 +5,79 @@ } + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Add missing structure set seed configs - public boolean isStructureChunk(ChunkGeneratorStructureState structureState, int x, int z) { + public boolean isStructureChunk(final ChunkGeneratorStructureState state, final int sourceX, final int sourceZ) { + // Paper start - Add missing structure set seed configs -+ return this.isStructureChunk(structureState, x, z, null); ++ return this.isStructureChunk(state, sourceX, sourceZ, null); + } + -+ public boolean isStructureChunk(ChunkGeneratorStructureState structureState, int x, int z, @javax.annotation.Nullable net.minecraft.resources.ResourceKey structureSetKey) { ++ public boolean isStructureChunk(final ChunkGeneratorStructureState state, final int sourceX, final int sourceZ, final net.minecraft.resources.@org.jspecify.annotations.Nullable ResourceKey structureSetKey) { + Integer saltOverride = null; + if (structureSetKey != null) { + if (structureSetKey == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.MINESHAFTS) { -+ saltOverride = structureState.conf.mineshaftSeed; ++ saltOverride = state.conf.mineshaftSeed; + } else if (structureSetKey == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.BURIED_TREASURES) { -+ saltOverride = structureState.conf.buriedTreasureSeed; ++ saltOverride = state.conf.buriedTreasureSeed; + } + } + // Paper end - Add missing structure set seed configs - return this.isPlacementChunk(structureState, x, z) -- && this.applyAdditionalChunkRestrictions(x, z, structureState.getLevelSeed()) -+ && this.applyAdditionalChunkRestrictions(x, z, structureState.getLevelSeed(), saltOverride) // Paper - Add missing structure set seed configs - && this.applyInteractionsWithOtherStructures(structureState, x, z); + return this.isPlacementChunk(state, sourceX, sourceZ) +- && this.applyAdditionalChunkRestrictions(sourceX, sourceZ, state.getLevelSeed()) ++ && this.applyAdditionalChunkRestrictions(sourceX, sourceZ, state.getLevelSeed(), saltOverride) // Paper - Add missing structure set seed configs + && this.applyInteractionsWithOtherStructures(state, sourceX, sourceZ); } -- public boolean applyAdditionalChunkRestrictions(int regionX, int regionZ, long levelSeed) { -- return !(this.frequency < 1.0F) || this.frequencyReductionMethod.shouldGenerate(levelSeed, this.salt, regionX, regionZ, this.frequency); +- public boolean applyAdditionalChunkRestrictions(final int sourceX, final int sourceZ, final long levelSeed) { +- return !(this.frequency < 1.0F) || this.frequencyReductionMethod.shouldGenerate(levelSeed, this.salt, sourceX, sourceZ, this.frequency); + // Paper start - Add missing structure set seed configs -+ public boolean applyAdditionalChunkRestrictions(int regionX, int regionZ, long levelSeed, @javax.annotation.Nullable Integer saltOverride) { -+ return !(this.frequency < 1.0F) || this.frequencyReductionMethod.shouldGenerate(levelSeed, this.salt, regionX, regionZ, this.frequency, saltOverride); ++ public boolean applyAdditionalChunkRestrictions(final int sourceX, final int sourceZ, final long levelSeed, final @org.jspecify.annotations.Nullable Integer saltOverride) { ++ return !(this.frequency < 1.0F) || this.frequencyReductionMethod.shouldGenerate(levelSeed, this.salt, sourceX, sourceZ, this.frequency, saltOverride); + // Paper end - Add missing structure set seed configs } - public boolean applyInteractionsWithOtherStructures(ChunkGeneratorStructureState structureState, int x, int z) { + public boolean applyInteractionsWithOtherStructures(final ChunkGeneratorStructureState state, final int sourceX, final int sourceZ) { @@ -101,25 +_,31 @@ public abstract StructurePlacementType type(); -- private static boolean probabilityReducer(long levelSeed, int regionX, int regionZ, int salt, float frequency) { -+ private static boolean probabilityReducer(long levelSeed, int regionX, int regionZ, int salt, float frequency, @javax.annotation.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here - WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); - worldgenRandom.setLargeFeatureWithSalt(levelSeed, regionX, regionZ, salt); - return worldgenRandom.nextFloat() < frequency; +- private static boolean probabilityReducer(final long seed, final int salt, final int sourceX, final int sourceZ, final float probability) { ++ private static boolean probabilityReducer(final long seed, final int salt, final int sourceX, final int sourceZ, final float probability, final @org.jspecify.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here + WorldgenRandom random = new WorldgenRandom(new LegacyRandomSource(0L)); + random.setLargeFeatureWithSalt(seed, salt, sourceX, sourceZ); + return random.nextFloat() < probability; } -- private static boolean legacyProbabilityReducerWithDouble(long baseSeed, int salt, int chunkX, int chunkZ, float frequency) { -+ private static boolean legacyProbabilityReducerWithDouble(long baseSeed, int salt, int chunkX, int chunkZ, float frequency, @javax.annotation.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs - WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- private static boolean legacyProbabilityReducerWithDouble(final long seed, final int salt, final int sourceX, final int sourceZ, final float probability) { ++ private static boolean legacyProbabilityReducerWithDouble(final long seed, final int salt, final int sourceX, final int sourceZ, final float probability, final @org.jspecify.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs + WorldgenRandom random = new WorldgenRandom(new LegacyRandomSource(0L)); + if (saltOverride == null) { // Paper - Add missing structure set seed configs - worldgenRandom.setLargeFeatureSeed(baseSeed, chunkX, chunkZ); + random.setLargeFeatureSeed(seed, sourceX, sourceZ); + // Paper start - Add missing structure set seed configs + } else { -+ worldgenRandom.setLargeFeatureWithSalt(baseSeed, chunkX, chunkZ, saltOverride); ++ random.setLargeFeatureWithSalt(seed, sourceX, sourceZ, saltOverride); + } + // Paper end - Add missing structure set seed configs - return worldgenRandom.nextDouble() < frequency; + return random.nextDouble() < probability; } -- private static boolean legacyArbitrarySaltProbabilityReducer(long levelSeed, int salt, int regionX, int regionZ, float frequency) { -+ private static boolean legacyArbitrarySaltProbabilityReducer(long levelSeed, int salt, int regionX, int regionZ, float frequency, @javax.annotation.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs - WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); -- worldgenRandom.setLargeFeatureWithSalt(levelSeed, regionX, regionZ, 10387320); -+ worldgenRandom.setLargeFeatureWithSalt(levelSeed, regionX, regionZ, saltOverride != null ? saltOverride : HIGHLY_ARBITRARY_RANDOM_SALT); // Paper - Add missing structure set seed configs - return worldgenRandom.nextFloat() < frequency; +- private static boolean legacyArbitrarySaltProbabilityReducer(final long seed, final int salt, final int sourceX, final int sourceZ, final float probability) { ++ private static boolean legacyArbitrarySaltProbabilityReducer(final long seed, final int salt, final int sourceX, final int sourceZ, final float probability, final @org.jspecify.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs + WorldgenRandom random = new WorldgenRandom(new LegacyRandomSource(0L)); +- random.setLargeFeatureWithSalt(seed, sourceX, sourceZ, 10387320); ++ random.setLargeFeatureWithSalt(seed, sourceX, sourceZ, saltOverride != null ? saltOverride : HIGHLY_ARBITRARY_RANDOM_SALT); // Paper - Add missing structure set seed configs + return random.nextFloat() < probability; } -- private static boolean legacyPillagerOutpostReducer(long levelSeed, int salt, int regionX, int regionZ, float frequency) { -+ private static boolean legacyPillagerOutpostReducer(long levelSeed, int salt, int regionX, int regionZ, float frequency, @javax.annotation.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here - int i = regionX >> 4; - int i1 = regionZ >> 4; - WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- private static boolean legacyPillagerOutpostReducer(final long seed, final int salt, final int sourceX, final int sourceZ, final float probability) { ++ private static boolean legacyPillagerOutpostReducer(final long seed, final int salt, final int sourceX, final int sourceZ, final float probability, final @org.jspecify.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here + int cx = sourceX >> 4; + int cz = sourceZ >> 4; + WorldgenRandom random = new WorldgenRandom(new LegacyRandomSource(0L)); @@ -147,7 +_,7 @@ @FunctionalInterface public interface FrequencyReducer { -- boolean shouldGenerate(long levelSeed, int salt, int regionX, int regionZ, float frequency); -+ boolean shouldGenerate(long levelSeed, int salt, int regionX, int regionZ, float frequency, @javax.annotation.Nullable Integer saltOverride); // Paper - Add missing structure set seed configs +- boolean shouldGenerate(long seed, final int salt, final int sourceX, final int sourceZ, float probability); ++ boolean shouldGenerate(long seed, final int salt, final int sourceX, final int sourceZ, float probability, final @org.jspecify.annotations.Nullable Integer saltOverride); // Paper - Add missing structure set seed configs } public static enum FrequencyReductionMethod implements StringRepresentable { @@ -85,10 +85,10 @@ this.reducer = reducer; } -- public boolean shouldGenerate(long levelSeed, int salt, int regionX, int regionZ, float frequency) { -- return this.reducer.shouldGenerate(levelSeed, salt, regionX, regionZ, frequency); -+ public boolean shouldGenerate(long levelSeed, int salt, int regionX, int regionZ, float frequency, @javax.annotation.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs -+ return this.reducer.shouldGenerate(levelSeed, salt, regionX, regionZ, frequency, saltOverride); // Paper - Add missing structure set seed configs +- public boolean shouldGenerate(final long seed, final int salt, final int sourceX, final int sourceZ, final float probability) { +- return this.reducer.shouldGenerate(seed, salt, sourceX, sourceZ, probability); ++ public boolean shouldGenerate(final long seed, final int salt, final int sourceX, final int sourceZ, final float probability, final @org.jspecify.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs ++ return this.reducer.shouldGenerate(seed, salt, sourceX, sourceZ, probability, saltOverride); // Paper - Add missing structure set seed configs } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java.patch index 0d479c6f5e11..c0e388defb0a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java.patch @@ -2,18 +2,18 @@ +++ b/net/minecraft/world/level/levelgen/structure/structures/DesertPyramidStructure.java @@ -66,6 +_,16 @@ - private static void placeSuspiciousSand(BoundingBox boundingBox, WorldGenLevel worldGenLevel, BlockPos pos) { - if (boundingBox.isInside(pos)) { + private static void placeSuspiciousSand(final BoundingBox chunkBB, final WorldGenLevel level, final BlockPos blockPos) { + if (chunkBB.isInside(blockPos)) { + // CraftBukkit start -+ if (worldGenLevel instanceof org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess && transformerAccess.canTransformBlocks()) { ++ if (level instanceof org.bukkit.craftbukkit.util.TransformerLevelAccessor transformerAccessor && transformerAccessor.canTransformBlocks()) { + // todo never called cause it's called in afterPlace after the whole capture logic -+ org.bukkit.craftbukkit.block.CraftBrushableBlock brushableState = (org.bukkit.craftbukkit.block.CraftBrushableBlock) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(worldGenLevel, pos, Blocks.SUSPICIOUS_SAND.defaultBlockState(), null); ++ org.bukkit.craftbukkit.block.CraftBrushableBlock brushableState = (org.bukkit.craftbukkit.block.CraftBrushableBlock) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, blockPos, Blocks.SUSPICIOUS_SAND.defaultBlockState(), null); + brushableState.setLootTable(org.bukkit.craftbukkit.CraftLootTable.minecraftToBukkit(BuiltInLootTables.DESERT_PYRAMID_ARCHAEOLOGY)); -+ brushableState.setSeed(pos.asLong()); -+ transformerAccess.setCraftBlock(pos, brushableState, Block.UPDATE_CLIENTS); ++ brushableState.setSeed(blockPos.asLong()); ++ transformerAccessor.setCraftBlock(blockPos, brushableState, Block.UPDATE_CLIENTS); + return; + } + // CraftBukkit end - worldGenLevel.setBlock(pos, Blocks.SUSPICIOUS_SAND.defaultBlockState(), Block.UPDATE_CLIENTS); - worldGenLevel.getBlockEntity(pos, BlockEntityType.BRUSHABLE_BLOCK) - .ifPresent(brushableBlockEntity -> brushableBlockEntity.setLootTable(BuiltInLootTables.DESERT_PYRAMID_ARCHAEOLOGY, pos.asLong())); + level.setBlock(blockPos, Blocks.SUSPICIOUS_SAND.defaultBlockState(), Block.UPDATE_CLIENTS); + level.getBlockEntity(blockPos, BlockEntityType.BRUSHABLE_BLOCK) + .ifPresent(entity -> entity.setLootTable(BuiltInLootTables.DESERT_PYRAMID_ARCHAEOLOGY, blockPos.asLong())); diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/EndCityPieces.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/EndCityPieces.java.patch index a024b0bfebef..c687c69a4d8c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/EndCityPieces.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/EndCityPieces.java.patch @@ -1,14 +1,14 @@ --- a/net/minecraft/world/level/levelgen/structure/structures/EndCityPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/EndCityPieces.java -@@ -390,7 +_,10 @@ - if (name.startsWith("Chest")) { - BlockPos blockPos = pos.below(); - if (box.isInside(blockPos)) { -- RandomizableContainer.setBlockEntityLootTable(level, random, blockPos, BuiltInLootTables.END_CITY_TREASURE); +@@ -410,7 +_,10 @@ + if (markerId.startsWith("Chest")) { + BlockPos chestPosition = position.below(); + if (chunkBB.isInside(chestPosition)) { +- RandomizableContainer.setBlockEntityLootTable(level, random, chestPosition, BuiltInLootTables.END_CITY_TREASURE); + // CraftBukkit start - ensure block transformation -+ // RandomizableContainer.setBlockEntityLootTable(level, random, blockPos, BuiltInLootTables.END_CITY_TREASURE); -+ this.setCraftLootTable(level, blockPos, random, BuiltInLootTables.END_CITY_TREASURE); ++ // RandomizableContainer.setBlockEntityLootTable(level, random, chestPosition, BuiltInLootTables.END_CITY_TREASURE); ++ this.setCraftLootTable(level, chestPosition, random, BuiltInLootTables.END_CITY_TREASURE); + // CraftBukkit end } - } else if (box.isInside(pos) && Level.isInSpawnableBounds(pos)) { - if (name.startsWith("Sentry")) { + } else if (chunkBB.isInside(position) && Level.isInSpawnableBounds(position)) { + if (markerId.startsWith("Sentry")) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/IglooPieces.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/IglooPieces.java.patch index 70a17df4e57a..3054f978e67f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/IglooPieces.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/IglooPieces.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/level/levelgen/structure/structures/IglooPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/IglooPieces.java -@@ -103,10 +_,13 @@ - protected void handleDataMarker(String name, BlockPos pos, ServerLevelAccessor level, RandomSource random, BoundingBox box) { - if ("chest".equals(name)) { - level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); -- BlockEntity blockEntity = level.getBlockEntity(pos.below()); -- if (blockEntity instanceof ChestBlockEntity) { -- ((ChestBlockEntity)blockEntity).setLootTable(BuiltInLootTables.IGLOO_CHEST, random.nextLong()); +@@ -115,10 +_,13 @@ + ) { + if ("chest".equals(markerId)) { + level.setBlock(position, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); +- BlockEntity chest = level.getBlockEntity(position.below()); +- if (chest instanceof ChestBlockEntity) { +- ((ChestBlockEntity)chest).setLootTable(BuiltInLootTables.IGLOO_CHEST, random.nextLong()); - } + // CraftBukkit start - ensure block transformation -+ // BlockEntity blockEntity = level.getBlockEntity(pos.below()); ++ // BlockEntity blockEntity = level.getBlockEntity(position.below()); + // if (blockEntity instanceof ChestBlockEntity) { + // ((ChestBlockEntity)blockEntity).setLootTable(BuiltInLootTables.IGLOO_CHEST, random.nextLong()); + // } -+ this.setCraftLootTable(level, pos.below(), random, BuiltInLootTables.IGLOO_CHEST); ++ this.setCraftLootTable(level, position.below(), random, BuiltInLootTables.IGLOO_CHEST); + // CraftBukkit end } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java.patch index 210aa0320b0e..07f0395eff24 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/MineshaftPieces.java -@@ -396,10 +_,13 @@ - BlockPos worldPos = this.getWorldPos(1, 0, i8); - if (box.isInside(worldPos) && this.isInterior(level, 1, 0, i8, box)) { +@@ -427,10 +_,13 @@ + BlockPos pos = this.getWorldPos(1, 0, newZ); + if (chunkBB.isInside(pos) && this.isInterior(level, 1, 0, newZ, chunkBB)) { this.hasPlacedSpider = true; -- level.setBlock(worldPos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); -- if (level.getBlockEntity(worldPos) instanceof SpawnerBlockEntity spawnerBlockEntity) { -- spawnerBlockEntity.setEntityId(EntityType.CAVE_SPIDER, random); +- level.setBlock(pos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); +- if (level.getBlockEntity(pos) instanceof SpawnerBlockEntity spawner) { +- spawner.setEntityId(EntityType.CAVE_SPIDER, random); - } + // CraftBukkit start -+ // level.setBlock(worldPos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); -+ // if (level.getBlockEntity(worldPos) instanceof SpawnerBlockEntity spawnerBlockEntity) { -+ // spawnerBlockEntity.setEntityId(EntityType.CAVE_SPIDER, random); ++ // level.setBlock(pos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); ++ // if (level.getBlockEntity(pos) instanceof SpawnerBlockEntity spawner) { ++ // spawner.setEntityId(EntityType.CAVE_SPIDER, random); + // } -+ this.placeCraftSpawner(level, worldPos, org.bukkit.entity.EntityType.CAVE_SPIDER, Block.UPDATE_CLIENTS); ++ this.placeCraftSpawner(level, pos, org.bukkit.entity.EntityType.CAVE_SPIDER, Block.UPDATE_CLIENTS); + // CraftBukkit end } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java.patch index f167d3222b1b..0b5accdd428d 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces.java -@@ -1084,10 +_,13 @@ - BlockPos worldPos = this.getWorldPos(3, 5, 5); - if (box.isInside(worldPos)) { +@@ -1194,10 +_,13 @@ + BlockPos pos = this.getWorldPos(3, 5, 5); + if (chunkBB.isInside(pos)) { this.hasPlacedSpawner = true; -- level.setBlock(worldPos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); -- if (level.getBlockEntity(worldPos) instanceof SpawnerBlockEntity spawnerBlockEntity) { -- spawnerBlockEntity.setEntityId(EntityType.BLAZE, random); +- level.setBlock(pos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); +- if (level.getBlockEntity(pos) instanceof SpawnerBlockEntity spawner) { +- spawner.setEntityId(EntityType.BLAZE, random); - } + // CraftBukkit start -+ // level.setBlock(worldPos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); -+ // if (level.getBlockEntity(worldPos) instanceof SpawnerBlockEntity spawnerBlockEntity) { -+ // spawnerBlockEntity.setEntityId(EntityType.BLAZE, random); ++ // level.setBlock(pos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); ++ // if (level.getBlockEntity(pos) instanceof SpawnerBlockEntity spawner) { ++ // spawner.setEntityId(EntityType.BLAZE, random); + // } -+ this.placeCraftSpawner(level, worldPos, org.bukkit.entity.EntityType.BLAZE, Block.UPDATE_CLIENTS); ++ this.placeCraftSpawner(level, pos, org.bukkit.entity.EntityType.BLAZE, Block.UPDATE_CLIENTS); + // CraftBukkit end } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces.java.patch index 75dc3003e1a4..56eb8a9035a3 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces.java.patch @@ -1,31 +1,35 @@ --- a/net/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces.java -@@ -308,14 +_,20 @@ - @Override - protected void handleDataMarker(String name, BlockPos pos, ServerLevelAccessor level, RandomSource random, BoundingBox box) { - if ("chest".equals(name)) { +@@ -322,16 +_,22 @@ + final String markerId, final BlockPos position, final ServerLevelAccessor level, final RandomSource random, final BoundingBox chunkBB + ) { + if ("chest".equals(markerId)) { - level.setBlock( -- pos, Blocks.CHEST.defaultBlockState().setValue(ChestBlock.WATERLOGGED, level.getFluidState(pos).is(FluidTags.WATER)), Block.UPDATE_CLIENTS +- position, +- Blocks.CHEST.defaultBlockState().setValue(ChestBlock.WATERLOGGED, level.getFluidState(position).is(FluidTags.WATER)), +- Block.UPDATE_CLIENTS - ); -- BlockEntity blockEntity = level.getBlockEntity(pos); -- if (blockEntity instanceof ChestBlockEntity) { -- ((ChestBlockEntity)blockEntity) +- BlockEntity chest = level.getBlockEntity(position); +- if (chest instanceof ChestBlockEntity) { +- ((ChestBlockEntity)chest) - .setLootTable(this.isLarge ? BuiltInLootTables.UNDERWATER_RUIN_BIG : BuiltInLootTables.UNDERWATER_RUIN_SMALL, random.nextLong()); - } + // CraftBukkit start - transform block to ensure loot table is accessible + // level.setBlock( -+ // pos, Blocks.CHEST.defaultBlockState().setValue(ChestBlock.WATERLOGGED, level.getFluidState(pos).is(FluidTags.WATER)), Block.UPDATE_CLIENTS ++ // position, ++ // Blocks.CHEST.defaultBlockState().setValue(ChestBlock.WATERLOGGED, level.getFluidState(position).is(FluidTags.WATER)), ++ // Block.UPDATE_CLIENTS + // ); -+ // BlockEntity blockEntity = level.getBlockEntity(pos); -+ // if (blockEntity instanceof ChestBlockEntity) { -+ // ((ChestBlockEntity)blockEntity) ++ // BlockEntity chest = level.getBlockEntity(position); ++ // if (chest instanceof ChestBlockEntity) { ++ // ((ChestBlockEntity)chest) + // .setLootTable(this.isLarge ? BuiltInLootTables.UNDERWATER_RUIN_BIG : BuiltInLootTables.UNDERWATER_RUIN_SMALL, random.nextLong()); + // } -+ org.bukkit.craftbukkit.block.CraftChest craftChest = (org.bukkit.craftbukkit.block.CraftChest) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, pos, Blocks.CHEST.defaultBlockState().setValue(ChestBlock.WATERLOGGED, level.getFluidState(pos).is(FluidTags.WATER)), null); ++ org.bukkit.craftbukkit.block.CraftChest craftChest = (org.bukkit.craftbukkit.block.CraftChest) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, position, Blocks.CHEST.defaultBlockState().setValue(ChestBlock.WATERLOGGED, level.getFluidState(position).is(FluidTags.WATER)), null); + craftChest.setSeed(random.nextLong()); + craftChest.setLootTable(org.bukkit.craftbukkit.CraftLootTable.minecraftToBukkit(this.isLarge ? BuiltInLootTables.UNDERWATER_RUIN_BIG : BuiltInLootTables.UNDERWATER_RUIN_SMALL)); -+ this.placeCraftBlockEntity(level, pos, craftChest, Block.UPDATE_CLIENTS); ++ this.placeCraftBlockEntity(level, position, craftChest, Block.UPDATE_CLIENTS); + // CraftBukkit end - } else if ("drowned".equals(name)) { + } else if ("drowned".equals(markerId)) { Drowned drowned = EntityType.DROWNED.create(level.getLevel(), EntitySpawnReason.STRUCTURE); if (drowned != null) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/ShipwreckPieces.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/ShipwreckPieces.java.patch index c6cdfce89d83..9e6fd16dfdb1 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/ShipwreckPieces.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/ShipwreckPieces.java.patch @@ -1,13 +1,13 @@ --- a/net/minecraft/world/level/levelgen/structure/structures/ShipwreckPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/ShipwreckPieces.java -@@ -126,7 +_,10 @@ - protected void handleDataMarker(String name, BlockPos pos, ServerLevelAccessor level, RandomSource random, BoundingBox box) { - ResourceKey resourceKey = ShipwreckPieces.MARKERS_TO_LOOT.get(name); - if (resourceKey != null) { -- RandomizableContainer.setBlockEntityLootTable(level, random, pos.below(), resourceKey); +@@ -145,7 +_,10 @@ + ) { + ResourceKey lootTable = ShipwreckPieces.MARKERS_TO_LOOT.get(markerId); + if (lootTable != null) { +- RandomizableContainer.setBlockEntityLootTable(level, random, position.below(), lootTable); + // CraftBukkit start + // RandomizableContainer.setBlockEntityLootTable(level, random, pos.below(), resourceKey); -+ this.setCraftLootTable(level, pos.below(), random, resourceKey); ++ this.setCraftLootTable(level, position.below(), random, lootTable); + // CraftBukkit end } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java.patch index 6463110c2229..0c701f0b2de2 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java.patch @@ -1,19 +1,19 @@ --- a/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java +++ b/net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces.java -@@ -832,10 +_,13 @@ - BlockPos worldPos = this.getWorldPos(5, 3, 6); - if (box.isInside(worldPos)) { +@@ -898,10 +_,13 @@ + BlockPos pos = this.getWorldPos(5, 3, 6); + if (chunkBB.isInside(pos)) { this.hasPlacedSpawner = true; -- level.setBlock(worldPos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); -- if (level.getBlockEntity(worldPos) instanceof SpawnerBlockEntity spawnerBlockEntity) { -- spawnerBlockEntity.setEntityId(EntityType.SILVERFISH, random); +- level.setBlock(pos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); +- if (level.getBlockEntity(pos) instanceof SpawnerBlockEntity spawner) { +- spawner.setEntityId(EntityType.SILVERFISH, random); - } + // CraftBukkit start -+ // level.setBlock(worldPos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); -+ // if (level.getBlockEntity(worldPos) instanceof SpawnerBlockEntity spawnerBlockEntity) { -+ // spawnerBlockEntity.setEntityId(EntityType.SILVERFISH, random); ++ // level.setBlock(pos, Blocks.SPAWNER.defaultBlockState(), Block.UPDATE_CLIENTS); ++ // if (level.getBlockEntity(pos) instanceof SpawnerBlockEntity spawner) { ++ // spawner.setEntityId(EntityType.SILVERFISH, random); + // } -+ this.placeCraftSpawner(level, worldPos, org.bukkit.entity.EntityType.SILVERFISH, Block.UPDATE_CLIENTS); ++ this.placeCraftSpawner(level, pos, org.bukkit.entity.EntityType.SILVERFISH, Block.UPDATE_CLIENTS); + // CraftBukkit end } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/SwampHutPiece.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/SwampHutPiece.java.patch index c2966ef14660..b0dc717a7d77 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/SwampHutPiece.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/structures/SwampHutPiece.java.patch @@ -1,18 +1,18 @@ --- a/net/minecraft/world/level/levelgen/structure/structures/SwampHutPiece.java +++ b/net/minecraft/world/level/levelgen/structure/structures/SwampHutPiece.java -@@ -97,7 +_,7 @@ +@@ -103,7 +_,7 @@ witch.setPersistenceRequired(); - witch.snapTo(worldPos.getX() + 0.5, worldPos.getY(), worldPos.getZ() + 0.5, 0.0F, 0.0F); - witch.finalizeSpawn(level, level.getCurrentDifficultyAt(worldPos), EntitySpawnReason.STRUCTURE, null); + witch.snapTo(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0.0F, 0.0F); + witch.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), EntitySpawnReason.STRUCTURE, null); - level.addFreshEntityWithPassengers(witch); + level.addFreshEntityWithPassengers(witch, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason } } } -@@ -116,7 +_,7 @@ +@@ -122,7 +_,7 @@ cat.setPersistenceRequired(); - cat.snapTo(worldPos.getX() + 0.5, worldPos.getY(), worldPos.getZ() + 0.5, 0.0F, 0.0F); - cat.finalizeSpawn(level, level.getCurrentDifficultyAt(worldPos), EntitySpawnReason.STRUCTURE, null); + cat.snapTo(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0.0F, 0.0F); + cat.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), EntitySpawnReason.STRUCTURE, null); - level.addFreshEntityWithPassengers(cat); + level.addFreshEntityWithPassengers(cat, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason } diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructurePlaceSettings.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructurePlaceSettings.java.patch index 9ce178ba9c73..8804683011f0 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructurePlaceSettings.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructurePlaceSettings.java.patch @@ -10,16 +10,16 @@ private boolean knownShape; private boolean finalizeEntities; @@ -139,6 +_,13 @@ - int size = palettes.size(); - if (size == 0) { + int paletteSize = palettes.size(); + if (paletteSize == 0) { throw new IllegalStateException("No palettes"); + // CraftBukkit start + } else if (this.palette >= 0) { -+ if (this.palette >= size) { -+ throw new IllegalArgumentException("Palette index out of bounds. Got " + this.palette + " where there are only " + size + " palettes available."); ++ if (this.palette >= paletteSize) { ++ throw new IllegalArgumentException("Palette index out of bounds. Got " + this.palette + " where there are only " + paletteSize + " palettes available."); + } + return palettes.get(this.palette); + // CraftBukkit end } else { - return palettes.get(this.getRandom(pos).nextInt(size)); + return palettes.get(this.getRandom(pos).nextInt(paletteSize)); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch index 0bffaf4763df..7dda6e8c0a52 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch @@ -11,60 +11,68 @@ public Vec3i getSize() { return this.size; -@@ -258,6 +_,19 @@ +@@ -251,7 +_,7 @@ + } + + public boolean placeInWorld( +- final ServerLevelAccessor level, ++ ServerLevelAccessor level, // Paper - make non-final + final BlockPos position, + final BlockPos referencePos, + final StructurePlaceSettings settings, +@@ -261,6 +_,19 @@ if (this.palettes.isEmpty()) { return false; } else { + // CraftBukkit start -+ // We only want the TransformerGeneratorAccess at certain locations because in here are many "block update" calls that shouldn't be transformed -+ ServerLevelAccessor wrappedAccess = level; ++ // We only want the TransformerLevelAccessor at certain locations because in here are many "block update" calls that shouldn't be transformed ++ ServerLevelAccessor wrappedAccessor = level; + org.bukkit.craftbukkit.util.CraftStructureTransformer structureTransformer = null; -+ if (wrappedAccess instanceof org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess) { -+ level = transformerAccess.getDelegate(); -+ structureTransformer = transformerAccess.getStructureTransformer(); ++ if (wrappedAccessor instanceof org.bukkit.craftbukkit.util.TransformerLevelAccessor transformerAccessor) { ++ level = transformerAccessor.getDelegate(); ++ structureTransformer = transformerAccessor.getStructureTransformer(); + // The structureTransformer is not needed if we can not transform blocks therefore we can save a little bit of performance doing this + if (structureTransformer != null && !structureTransformer.canTransformBlocks()) { + structureTransformer = null; + } + } + // CraftBukkit end - List list = settings.getRandomPalette(this.palettes, offset).blocks(); - if ((!list.isEmpty() || !settings.isIgnoreEntities() && !this.entityInfoList.isEmpty()) + List blockInfoList = settings.getRandomPalette(this.palettes, position).blocks(); + if ((!blockInfoList.isEmpty() || !settings.isIgnoreEntities() && !this.entityInfoList.isEmpty()) && this.size.getX() >= 1 -@@ -285,6 +_,21 @@ +@@ -288,6 +_,20 @@ level.setBlock(blockPos, Blocks.BARRIER.defaultBlockState(), Block.UPDATE_SKIP_ALL_SIDEEFFECTS | Block.UPDATE_NONE); } + // CraftBukkit start + if (structureTransformer != null) { -+ org.bukkit.craftbukkit.block.CraftBlockState craftBlockState = (org.bukkit.craftbukkit.block.CraftBlockState) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, blockPos, blockState, null); -+ if (structureBlockInfo.nbt != null && craftBlockState instanceof org.bukkit.craftbukkit.block.CraftBlockEntityState entityState) { -+ entityState.loadData(structureBlockInfo.nbt); ++ org.bukkit.craftbukkit.block.CraftBlockState craftBlockState = (org.bukkit.craftbukkit.block.CraftBlockState) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, blockPos, state, null); ++ if (blockInfo.nbt != null && craftBlockState instanceof org.bukkit.craftbukkit.block.CraftBlockEntityState entityState) { ++ entityState.loadData(blockInfo.nbt); + if (craftBlockState instanceof org.bukkit.craftbukkit.block.CraftLootable craftLootable) { + craftLootable.setSeed(random.nextLong()); + } + } + craftBlockState = structureTransformer.transformCraftState(craftBlockState); -+ blockState = craftBlockState.getHandle(); -+ structureBlockInfo = new StructureTemplate.StructureBlockInfo(blockPos, blockState, (craftBlockState instanceof org.bukkit.craftbukkit.block.CraftBlockEntityState craftBlockEntityState ? craftBlockEntityState.getSnapshotNBT() : null)); ++ state = craftBlockState.getHandle(); ++ blockInfo = new StructureTemplate.StructureBlockInfo(blockPos, state, (craftBlockState instanceof org.bukkit.craftbukkit.block.CraftBlockEntityState craftBlockEntityState ? craftBlockEntityState.getSnapshotNBT() : null)); + } + // CraftBukkit end -+ - if (level.setBlock(blockPos, blockState, flags)) { - i = Math.min(i, blockPos.getX()); - i1 = Math.min(i1, blockPos.getY()); -@@ -296,7 +_,7 @@ - if (structureBlockInfo.nbt != null) { + if (level.setBlock(blockPos, state, updateMode)) { + minX = Math.min(minX, blockPos.getX()); + minY = Math.min(minY, blockPos.getY()); +@@ -299,7 +_,7 @@ + if (blockInfo.nbt != null) { BlockEntity blockEntity = level.getBlockEntity(blockPos); if (blockEntity != null) { - if (!SharedConstants.DEBUG_STRUCTURE_EDIT_MODE && blockEntity instanceof RandomizableContainer) { + if (structureTransformer == null && !SharedConstants.DEBUG_STRUCTURE_EDIT_MODE && blockEntity instanceof RandomizableContainer) { // CraftBukkit - only process if don't have a transformer access (Was already set above) - SPIGOT-7520: Use structureTransformer as check, so that it is the same as above - structureBlockInfo.nbt.putLong("LootTableSeed", random.nextLong()); + blockInfo.nbt.putLong("LootTableSeed", random.nextLong()); } -@@ -383,7 +_,11 @@ - if (pair1.getSecond() != null) { - BlockEntity blockEntity = level.getBlockEntity(blockPos4); +@@ -384,7 +_,11 @@ + if (blockInfox.getSecond() != null) { + BlockEntity blockEntity = level.getBlockEntity(blockPos); if (blockEntity != null) { - blockEntity.setChanged(); + // Paper start - Fix NBT pieces overriding a block entity during worldgen deadlock @@ -75,41 +83,41 @@ } } } -@@ -391,7 +_,7 @@ +@@ -392,7 +_,7 @@ if (!settings.isIgnoreEntities()) { this.placeEntities( - level, -+ wrappedAccess, // CraftBukkit - offset, ++ wrappedAccessor, // CraftBukkit + position, settings.getMirror(), settings.getRotation(), -@@ -504,14 +_,17 @@ +@@ -512,14 +_,17 @@ }); } } + } - private static Optional createEntityIgnoreException(ProblemReporter problemReporter, ServerLevelAccessor level, CompoundTag tag) { + private static Optional createEntityIgnoreException(final ProblemReporter reporter, final ServerLevelAccessor level, final CompoundTag tag) { - try { -- return EntityType.create(TagValueInput.create(problemReporter, level.registryAccess(), tag), level.getLevel(), EntitySpawnReason.STRUCTURE); +- return EntityType.create(TagValueInput.create(reporter, level.registryAccess(), tag), level.getLevel(), EntitySpawnReason.STRUCTURE); - } catch (Exception var4) { - return Optional.empty(); - } + // CraftBukkit start + // try { -+ return EntityType.create(TagValueInput.create(problemReporter, level.registryAccess(), tag), level.getLevel(), EntitySpawnReason.STRUCTURE, true); // Paper - Don't fire sync event during generation ++ return EntityType.create(TagValueInput.create(reporter, level.registryAccess(), tag), level.getLevel(), EntitySpawnReason.STRUCTURE, true); // Paper - Don't fire sync event during generation + // } catch (Exception var4) { + // return Optional.empty(); + // } + // CraftBukkit end } - public Vec3i getSize(Rotation rotation) { -@@ -704,6 +_,11 @@ + public Vec3i getSize(final Rotation rotation) { +@@ -710,6 +_,11 @@ - tag.put("entities", listTag3); + tag.put("entities", entityList); tag.put("size", this.newIntegerList(this.size.getX(), this.size.getY(), this.size.getZ())); + // CraftBukkit start - PDC + if (!this.persistentDataContainer.isEmpty()) { @@ -119,10 +127,10 @@ return NbtUtils.addCurrentDataVersion(tag); } -@@ -734,6 +_,11 @@ - .ifPresent(compoundTag1 -> this.entityInfoList.add(new StructureTemplate.StructureEntityInfo(vec3, blockPos, compoundTag1))); - } - ); +@@ -735,6 +_,11 @@ + BlockPos blockPos = new BlockPos(blockPosTag.getIntOr(0, 0), blockPosTag.getIntOr(1, 0), blockPosTag.getIntOr(2, 0)); + entityTag.getCompound("nbt").ifPresent(nbt -> this.entityInfoList.add(new StructureTemplate.StructureEntityInfo(pos, blockPos, nbt))); + }); + // CraftBukkit start - PDC + if (tag.get("BukkitValues") instanceof CompoundTag compoundTag) { + this.persistentDataContainer.putAll(compoundTag); @@ -130,8 +138,8 @@ + // CraftBukkit end } - private void loadPalette(HolderGetter blockGetter, ListTag paletteTag, ListTag blocksTag) { -@@ -833,7 +_,7 @@ + private void loadPalette(final HolderGetter blockLookup, final ListTag paletteList, final ListTag blockList) { +@@ -834,7 +_,7 @@ public static final class Palette { private final List blocks; @@ -139,4 +147,4 @@ + private final Map> cache = Maps.newConcurrentMap(); // Paper - Fix CME due to this collection being shared across threads private @Nullable List cachedJigsaws; - Palette(List blocks) { + private Palette(final List blocks) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/material/FlowingFluid.java.patch b/paper-server/patches/sources/net/minecraft/world/level/material/FlowingFluid.java.patch index f0d1dd9dafcb..adbd1fb3edbd 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/material/FlowingFluid.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/material/FlowingFluid.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/material/FlowingFluid.java +++ b/net/minecraft/world/level/material/FlowingFluid.java -@@ -119,6 +_,15 @@ - FluidState newLiquid = this.getNewLiquid(level, blockPos, blockState); - Fluid type = newLiquid.getType(); - if (fluidState1.canBeReplacedWith(level, blockPos, type, Direction.DOWN) && canHoldSpecificFluid(level, blockPos, blockState, type)) { +@@ -121,6 +_,15 @@ + Fluid newBelowFluidType = newBelowFluid.getType(); + if (belowFluid.canBeReplacedWith(level, belowPos, newBelowFluidType, Direction.DOWN) + && canHoldSpecificFluid(level, belowPos, belowState, newBelowFluidType)) { + // CraftBukkit start + org.bukkit.block.Block source = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); + org.bukkit.event.block.BlockFromToEvent event = new org.bukkit.event.block.BlockFromToEvent(source, org.bukkit.block.BlockFace.DOWN); @@ -13,48 +13,48 @@ + return; + } + // CraftBukkit end - this.spreadTo(level, blockPos, blockState, Direction.DOWN, newLiquid); + this.spreadTo(level, belowPos, belowState, Direction.DOWN, newBelowFluid); if (this.sourceNeighborCount(level, pos) >= 3) { this.spreadToSides(level, pos, fluidState, state); -@@ -147,7 +_,18 @@ - Direction direction = entry.getKey(); - FluidState fluidState1 = entry.getValue(); - BlockPos blockPos = pos.relative(direction); -- this.spreadTo(level, blockPos, level.getBlockState(blockPos), direction, fluidState1); -+ final BlockState blockStateIfLoaded = level.getBlockStateIfLoaded(blockPos); // Paper - Prevent chunk loading from fluid flowing -+ if (blockStateIfLoaded == null) continue; // Paper - Prevent chunk loading from fluid flowing +@@ -149,7 +_,18 @@ + Direction spread = entry.getKey(); + FluidState newNeighborFluid = entry.getValue(); + BlockPos neighborPos = pos.relative(spread); +- this.spreadTo(level, neighborPos, level.getBlockState(neighborPos), spread, newNeighborFluid); ++ final BlockState neighborState = level.getBlockStateIfLoaded(neighborPos); // Paper - Prevent chunk loading from fluid flowing ++ if (neighborState == null) continue; // Paper - Prevent chunk loading from fluid flowing + // CraftBukkit start + org.bukkit.block.Block source = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); -+ org.bukkit.event.block.BlockFromToEvent event = new org.bukkit.event.block.BlockFromToEvent(source, org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(direction)); ++ org.bukkit.event.block.BlockFromToEvent event = new org.bukkit.event.block.BlockFromToEvent(source, org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(spread)); + level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + continue; + } + // CraftBukkit end -+ this.spreadTo(level, blockPos, blockStateIfLoaded, direction, fluidState1); // Paper - Prevent chunk loading from fluid flowing ++ this.spreadTo(level, neighborPos, neighborState, spread, newNeighborFluid); // Paper - Prevent chunk loading from fluid flowing } } } -@@ -159,7 +_,8 @@ +@@ -161,7 +_,8 @@ for (Direction direction : Direction.Plane.HORIZONTAL) { - BlockPos blockPos = mutableBlockPos.setWithOffset(pos, direction); -- BlockState blockState = level.getBlockState(blockPos); -+ BlockState blockState = level.getBlockStateIfLoaded(blockPos); // Paper - Prevent chunk loading from fluid flowing + BlockPos relativePos = mutablePos.setWithOffset(pos, direction); +- BlockState blockState = level.getBlockState(relativePos); ++ BlockState blockState = level.getBlockStateIfLoaded(relativePos); // Paper - Prevent chunk loading from fluid flowing + if (blockState == null) continue; // Paper - Prevent chunk loading from fluid flowing FluidState fluidState = blockState.getFluidState(); - if (fluidState.getType().isSame(this) && canPassThroughWall(direction, level, pos, state, blockPos, blockState)) { + if (fluidState.getType().isSame(this) && canPassThroughWall(direction, level, pos, state, relativePos, blockState)) { if (fluidState.isSource()) { -@@ -257,13 +_,15 @@ - liquidBlockContainer.placeLiquid(level, pos, state, fluidState); +@@ -264,13 +_,15 @@ + container.placeLiquid(level, pos, state, target); } else { if (!state.isAir()) { - this.beforeDestroyingBlock(level, pos, state); + this.beforeDestroyingBlock(level, pos, state, pos.relative(direction.getOpposite())); // Paper - Add BlockBreakBlockEvent } - level.setBlock(pos, fluidState.createLegacyBlock(), Block.UPDATE_ALL); + level.setBlock(pos, target.createLegacyBlock(), Block.UPDATE_ALL); } } @@ -62,78 +62,78 @@ + protected abstract void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state); - protected int getSlopeDistance(LevelReader level, BlockPos pos, int depth, Direction direction, BlockState state, FlowingFluid.SpreadContext spreadContext) { -@@ -272,7 +_,8 @@ - for (Direction direction1 : Direction.Plane.HORIZONTAL) { - if (direction1 != direction) { - BlockPos blockPos = pos.relative(direction1); -- BlockState blockState = spreadContext.getBlockState(blockPos); -+ BlockState blockState = spreadContext.getBlockStateIfLoaded(blockPos); // Paper - Prevent chunk loading from fluid flowing -+ if (blockState == null) continue; // Paper - Prevent chunk loading from fluid flowing - FluidState fluidState = blockState.getFluidState(); - if (this.canPassThrough(level, this.getFlowing(), pos, state, direction1, blockPos, blockState, fluidState)) { - if (spreadContext.isHole(blockPos)) { -@@ -339,7 +_,8 @@ + protected int getSlopeDistance( +@@ -281,7 +_,8 @@ + for (Direction direction : Direction.Plane.HORIZONTAL) { + if (direction != from) { + BlockPos testPos = pos.relative(direction); +- BlockState testState = context.getBlockState(testPos); ++ BlockState testState = context.getBlockStateIfLoaded(testPos); // Paper - Prevent chunk loading from fluid flowing ++ if (testState == null) continue; // Paper - Prevent chunk loading from fluid flowing + FluidState testFluidState = testState.getFluidState(); + if (this.canPassThrough(level, this.getFlowing(), pos, state, direction, testPos, testState, testFluidState)) { + if (context.isHole(testPos)) { +@@ -363,7 +_,8 @@ for (Direction direction : Direction.Plane.HORIZONTAL) { - BlockPos blockPos = pos.relative(direction); -- BlockState blockState = level.getBlockState(blockPos); -+ BlockState blockState = level.getBlockStateIfLoaded(blockPos); // Paper - Prevent chunk loading from fluid flowing -+ if (blockState == null) continue; // Paper - Prevent chunk loading from fluid flowing - FluidState fluidState = blockState.getFluidState(); - if (this.canMaybePassThrough(level, pos, state, direction, blockPos, blockState, fluidState)) { - FluidState newLiquid = this.getNewLiquid(level, blockPos, blockState); -@@ -410,10 +_,24 @@ - if (newLiquid.isEmpty()) { - fluidState = newLiquid; - state = Blocks.AIR.defaultBlockState(); + BlockPos testPos = pos.relative(direction); +- BlockState testState = level.getBlockState(testPos); ++ BlockState testState = level.getBlockStateIfLoaded(testPos); // Paper - Prevent chunk loading from fluid flowing ++ if (testState == null) continue; // Paper - Prevent chunk loading from fluid flowing + FluidState testFluidState = testState.getFluidState(); + if (this.canMaybePassThrough(level, pos, state, direction, testPos, testState, testFluidState)) { + FluidState newFluid = this.getNewLiquid(level, testPos, testState); +@@ -434,10 +_,24 @@ + if (newFluidState.isEmpty()) { + fluidState = newFluidState; + blockState = Blocks.AIR.defaultBlockState(); + // CraftBukkit start -+ org.bukkit.event.block.FluidLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFluidLevelChangeEvent(level, pos, state); ++ org.bukkit.event.block.FluidLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFluidLevelChangeEvent(level, pos, blockState); + if (event.isCancelled()) { + return; + } -+ state = ((org.bukkit.craftbukkit.block.data.CraftBlockData) event.getNewData()).getState(); ++ blockState = ((org.bukkit.craftbukkit.block.data.CraftBlockData) event.getNewData()).getState(); + // CraftBukkit end - level.setBlock(pos, state, Block.UPDATE_ALL); - } else if (newLiquid != fluidState) { - fluidState = newLiquid; - state = newLiquid.createLegacyBlock(); + level.setBlock(pos, blockState, Block.UPDATE_ALL); + } else if (newFluidState != fluidState) { + fluidState = newFluidState; + blockState = newFluidState.createLegacyBlock(); + // CraftBukkit start -+ org.bukkit.event.block.FluidLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFluidLevelChangeEvent(level, pos, state); ++ org.bukkit.event.block.FluidLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFluidLevelChangeEvent(level, pos, blockState); + if (event.isCancelled()) { + return; + } -+ state = ((org.bukkit.craftbukkit.block.data.CraftBlockData) event.getNewData()).getState(); ++ blockState = ((org.bukkit.craftbukkit.block.data.CraftBlockData) event.getNewData()).getState(); + // CraftBukkit end - level.setBlock(pos, state, Block.UPDATE_ALL); - level.scheduleTick(pos, newLiquid.getType(), spreadDelay); + level.setBlock(pos, blockState, Block.UPDATE_ALL); + level.scheduleTick(pos, newFluidState.getType(), tickDelay); } -@@ -481,9 +_,27 @@ - public BlockState getBlockState(BlockPos pos) { +@@ -510,8 +_,27 @@ return this.getBlockState(pos, this.getCacheKey(pos)); } + + // Paper start - Prevent chunk loading from fluid flowing -+ public @javax.annotation.Nullable BlockState getBlockStateIfLoaded(BlockPos pos) { ++ public @org.jspecify.annotations.Nullable BlockState getBlockStateIfLoaded(BlockPos pos) { + return this.getBlockState(pos, this.getCacheKey(pos), false); + } + // Paper end - Prevent chunk loading from fluid flowing - - private BlockState getBlockState(BlockPos pos, short cacheKey) { -- return this.stateCache.computeIfAbsent(cacheKey, s -> this.level.getBlockState(pos)); -+ // Paper start - Prevent chunk loading from fluid flowing -+ return getBlockState(pos, cacheKey, true); ++ + private BlockState getBlockState(final BlockPos pos, final short key) { +- return this.stateCache.computeIfAbsent(key, k -> this.level.getBlockState(pos)); ++ // Paper start - Prevent chunk loading from fluid flowing ++ return this.getBlockState(pos, key, true); + } + -+ private @javax.annotation.Nullable BlockState getBlockState(BlockPos pos, short packed, boolean load) { -+ BlockState blockState = this.stateCache.get(packed); ++ private @org.jspecify.annotations.Nullable BlockState getBlockState(final BlockPos pos, final short key, final boolean load) { ++ BlockState blockState = this.stateCache.get(key); + if (blockState == null) { -+ blockState = load ? level.getBlockState(pos) : level.getBlockStateIfLoaded(pos); ++ blockState = load ? this.level.getBlockState(pos) : this.level.getBlockStateIfLoaded(pos); + if (blockState != null) { -+ this.stateCache.put(packed, blockState); ++ this.stateCache.put(key, blockState); + } + } + return blockState; -+ // Paper end - Prevent chunk loading from fluid flowing ++ // Paper end - Prevent chunk loading from fluid flowing } - public boolean isHole(BlockPos pos) { + public boolean isHole(final BlockPos pos) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/material/FluidState.java.patch b/paper-server/patches/sources/net/minecraft/world/level/material/FluidState.java.patch index 1153a05f33ed..16a6ce9bdd03 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/material/FluidState.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/material/FluidState.java.patch @@ -1,18 +1,18 @@ --- a/net/minecraft/world/level/material/FluidState.java +++ b/net/minecraft/world/level/material/FluidState.java -@@ -29,9 +_,11 @@ - public static final Codec CODEC = codec(BuiltInRegistries.FLUID.byNameCodec(), Fluid::defaultFluidState).stable(); +@@ -25,9 +_,11 @@ + public static final Codec CODEC = codec(BuiltInRegistries.FLUID.byNameCodec(), Fluid::defaultFluidState, Fluid::getStateDefinition).stable(); public static final int AMOUNT_MAX = 9; public static final int AMOUNT_FULL = 8; + protected final boolean isEmpty; // Paper - Perf: moved from isEmpty() - public FluidState(Fluid owner, Reference2ObjectArrayMap, Comparable> values, MapCodec propertiesCodec) { - super(owner, values, propertiesCodec); + public FluidState(final Fluid owner, final Property[] propertyKeys, final Comparable[] propertyValues) { + super(owner, propertyKeys, propertyValues); + this.isEmpty = owner.isEmpty(); // Paper - Perf: moved from isEmpty() } public Fluid getType() { -@@ -47,7 +_,7 @@ +@@ -43,7 +_,7 @@ } public boolean isEmpty() { @@ -20,4 +20,4 @@ + return this.isEmpty; // Paper - Perf: moved into constructor } - public float getHeight(BlockGetter level, BlockPos pos) { + public float getHeight(final BlockGetter level, final BlockPos pos) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/material/LavaFluid.java.patch b/paper-server/patches/sources/net/minecraft/world/level/material/LavaFluid.java.patch index daf7d56d473f..af90667de5e3 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/material/LavaFluid.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/material/LavaFluid.java.patch @@ -1,38 +1,38 @@ --- a/net/minecraft/world/level/material/LavaFluid.java +++ b/net/minecraft/world/level/material/LavaFluid.java -@@ -94,6 +_,13 @@ - BlockState blockState = level.getBlockState(blockPos); +@@ -95,6 +_,13 @@ + BlockState blockState = level.getBlockState(testPos); if (blockState.isAir()) { - if (this.hasFlammableNeighbours(level, blockPos)) { + if (this.hasFlammableNeighbours(level, testPos)) { + // CraftBukkit start - Prevent lava putting something on fire -+ if (!level.getBlockState(blockPos).is(Blocks.FIRE)) { -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, blockPos, pos).isCancelled()) { ++ if (!level.getBlockState(testPos).is(Blocks.FIRE)) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, testPos, pos).isCancelled()) { + continue; + } + } + // CraftBukkit end - level.setBlockAndUpdate(blockPos, BaseFireBlock.getState(level, blockPos)); + level.setBlockAndUpdate(testPos, BaseFireBlock.getState(level, testPos)); return; } -@@ -109,6 +_,14 @@ +@@ -110,6 +_,14 @@ } - if (level.isEmptyBlock(blockPos1.above()) && this.isFlammable(level, blockPos1)) { + if (level.isEmptyBlock(testPos.above()) && this.isFlammable(level, testPos)) { + // CraftBukkit start - Prevent lava putting something on fire -+ BlockPos up = blockPos1.above(); ++ BlockPos up = testPos.above(); + if (!level.getBlockState(up).is(Blocks.FIRE)) { + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, up, pos).isCancelled()) { + continue; + } + } + // CraftBukkit end - Prevent lava putting something on fire - level.setBlockAndUpdate(blockPos1.above(), BaseFireBlock.getState(level, blockPos1)); + level.setBlockAndUpdate(testPos.above(), BaseFireBlock.getState(level, testPos)); } } -@@ -118,9 +_,10 @@ +@@ -119,9 +_,10 @@ @Override - protected void entityInside(Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier) { + protected void entityInside(final Level level, final BlockPos pos, final Entity entity, final InsideBlockEffectApplier effectApplier) { + BlockPos savedPos = pos.immutable(); // Paper - track lava contact effectApplier.apply(InsideBlockEffectType.CLEAR_FREEZE); effectApplier.apply(InsideBlockEffectType.LAVA_IGNITE); @@ -40,10 +40,10 @@ + effectApplier.runAfter(InsideBlockEffectType.LAVA_IGNITE, ignitedEntity -> ignitedEntity.lavaHurt(savedPos)); // Paper - track lava contact } - private boolean hasFlammableNeighbours(LevelReader level, BlockPos pos) { -@@ -207,7 +_,11 @@ - FluidState fluidState1 = level.getFluidState(pos); - if (this.is(FluidTags.LAVA) && fluidState1.is(FluidTags.WATER)) { + private boolean hasFlammableNeighbours(final LevelReader level, final BlockPos pos) { +@@ -208,7 +_,11 @@ + FluidState fluidState = level.getFluidState(pos); + if (this.is(FluidTags.LAVA) && fluidState.is(FluidTags.WATER)) { if (state.getBlock() instanceof LiquidBlock) { - level.setBlock(pos, Blocks.STONE.defaultBlockState(), Block.UPDATE_ALL); + // CraftBukkit start @@ -54,7 +54,7 @@ } this.fizz(level, pos); -@@ -225,7 +_,7 @@ +@@ -226,7 +_,7 @@ @Override protected float getExplosionResistance() { diff --git a/paper-server/patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch b/paper-server/patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch index 0accdf667c84..abad32b7d924 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch @@ -5,15 +5,15 @@ } + // Paper start - Add BlockBreakBlockEvent -+ @Override ++ @Override + protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state, BlockPos source) { + BlockEntity blockEntity = state.hasBlockEntity() ? level.getBlockEntity(pos) : null; -+ Block.dropResources(state, level, pos, blockEntity, source); ++ Block.dropResources(state, (Level) level, pos, blockEntity, source); + } + // Paper end - Add BlockBreakBlockEvent + @Override - protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state) { + protected void beforeDestroyingBlock(final LevelAccessor level, final BlockPos pos, final BlockState state) { BlockEntity blockEntity = state.hasBlockEntity() ? level.getBlockEntity(pos) : null; @@ -120,7 +_,7 @@ diff --git a/paper-server/patches/sources/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java.patch b/paper-server/patches/sources/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java.patch index cacf29e39716..14d2fd86288a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java.patch @@ -1,9 +1,9 @@ --- a/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java +++ b/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -@@ -477,7 +_,12 @@ +@@ -504,7 +_,12 @@ } - protected static PathType getPathTypeFromState(BlockGetter level, BlockPos pos) { + protected static PathType getPathTypeFromState(final BlockGetter level, final BlockPos pos) { - BlockState blockState = level.getBlockState(pos); + // Paper start - Do not load chunks during pathfinding + BlockState blockState = level.getBlockStateIfLoaded(pos); diff --git a/paper-server/patches/sources/net/minecraft/world/level/portal/PortalForcer.java.patch b/paper-server/patches/sources/net/minecraft/world/level/portal/PortalForcer.java.patch index 99601fb65303..94746ee3e6ba 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/portal/PortalForcer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/portal/PortalForcer.java.patch @@ -5,90 +5,90 @@ } + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - public Optional findClosestPortalPosition(BlockPos exitPos, boolean isNether, WorldBorder worldBorder) { + public Optional findClosestPortalPosition(final BlockPos approximateExitPos, final boolean toNether, final WorldBorder worldBorder) { + // CraftBukkit start -+ return this.findClosestPortalPosition(exitPos, worldBorder, isNether ? 16 : 128); // Search Radius ++ return this.findClosestPortalPosition(approximateExitPos, worldBorder, toNether ? 16 : 128); // Search Radius + } + -+ public Optional findClosestPortalPosition(BlockPos exitPos, WorldBorder worldBorder, int i) { ++ public Optional findClosestPortalPosition(final BlockPos approximateExitPos, final WorldBorder worldBorder, final int radius) { PoiManager poiManager = this.level.getPoiManager(); -- int i = isNether ? 16 : 128; -+ // int i = isNether ? 16 : 128; +- int radius = toNether ? 16 : 128; ++ // int radius = toNether ? 16 : 128; + // CraftBukkit end - poiManager.ensureLoadedAndValid(this.level, exitPos, i); - return poiManager.getInSquare(holder -> holder.is(PoiTypes.NETHER_PORTAL), exitPos, i, PoiManager.Occupancy.ANY) + poiManager.ensureLoadedAndValid(this.level, approximateExitPos, radius); + return poiManager.getInSquare(type -> type.is(PoiTypes.NETHER_PORTAL), approximateExitPos, radius, PoiManager.Occupancy.ANY) .map(PoiRecord::getPos) .filter(worldBorder::isWithinBounds) + .filter(pos -> !(this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> pos.getY() >= v))) // Paper - Configurable nether ceiling damage - .filter(blockPos -> this.level.getBlockState(blockPos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS)) - .min(Comparator.comparingDouble(blockPos -> blockPos.distSqr(exitPos)).thenComparingInt(Vec3i::getY)); + .filter(pos -> this.level.getBlockState(pos).hasProperty(BlockStateProperties.HORIZONTAL_AXIS)) + .min(Comparator.comparingDouble(p -> p.distSqr(approximateExitPos)).thenComparingInt(Vec3i::getY)); } - public Optional createPortal(BlockPos pos, Direction.Axis axis) { + public Optional createPortal(final BlockPos origin, final Direction.Axis portalAxis) { + // CraftBukkit start -+ return this.createPortal(pos, axis, null, 16); ++ return this.createPortal(origin, portalAxis, null, 16); + } + -+ public Optional createPortal(BlockPos pos, Direction.Axis axis, @javax.annotation.Nullable net.minecraft.world.entity.Entity entity, int createRadius) { ++ public Optional createPortal(final BlockPos origin, final Direction.Axis portalAxis, final net.minecraft.world.entity.@org.jspecify.annotations.Nullable Entity entity, final int createRadius) { + // CraftBukkit end - Direction direction = Direction.get(Direction.AxisDirection.POSITIVE, axis); - double d = -1.0; - BlockPos blockPos = null; + Direction direction = Direction.get(Direction.AxisDirection.POSITIVE, portalAxis); + double closestFullDistanceSqr = -1.0; + BlockPos closestFullPosition = null; @@ -58,10 +_,15 @@ - BlockPos blockPos1 = null; + BlockPos closestPartialPosition = null; WorldBorder worldBorder = this.level.getWorldBorder(); - int min = Math.min(this.level.getMaxY(), this.level.getMinY() + this.level.getLogicalHeight() - 1); + int maxPlaceableY = Math.min(this.level.getMaxY(), this.level.getMinY() + this.level.getLogicalHeight() - 1); + // Paper start - Configurable nether ceiling damage; make sure the max height doesn't exceed the void damage height + if (this.level.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.enabled()) { -+ min = Math.min(min, this.level.paperConfig().environment.netherCeilingVoidDamageHeight.intValue() - 1); ++ maxPlaceableY = Math.min(maxPlaceableY, this.level.paperConfig().environment.netherCeilingVoidDamageHeight.intValue() - 1); + } + // Paper end - Configurable nether ceiling damage - int i = 1; - BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); + int edgeDistance = 1; + BlockPos.MutableBlockPos mutable = origin.mutable(); -- for (BlockPos.MutableBlockPos mutableBlockPos1 : BlockPos.spiralAround(pos, 16, Direction.EAST, Direction.SOUTH)) { -+ for (BlockPos.MutableBlockPos mutableBlockPos1 : BlockPos.spiralAround(pos, createRadius, Direction.EAST, Direction.SOUTH)) { // CraftBukkit - int min1 = Math.min(min, this.level.getHeight(Heightmap.Types.MOTION_BLOCKING, mutableBlockPos1.getX(), mutableBlockPos1.getZ())); - if (worldBorder.isWithinBounds(mutableBlockPos1) && worldBorder.isWithinBounds(mutableBlockPos1.move(direction, 1))) { - mutableBlockPos1.move(direction.getOpposite(), 1); +- for (BlockPos.MutableBlockPos columnPos : BlockPos.spiralAround(origin, 16, Direction.EAST, Direction.SOUTH)) { ++ for (BlockPos.MutableBlockPos columnPos : BlockPos.spiralAround(origin, createRadius, Direction.EAST, Direction.SOUTH)) { // CraftBukkit + int height = Math.min(maxPlaceableY, this.level.getHeight(Heightmap.Types.MOTION_BLOCKING, columnPos.getX(), columnPos.getZ())); + if (worldBorder.isWithinBounds(columnPos) && worldBorder.isWithinBounds(columnPos.move(direction, 1))) { + columnPos.move(direction.getOpposite(), 1); @@ -105,6 +_,7 @@ - d = d1; + closestFullDistanceSqr = closestPartialDistanceSqr; } + org.bukkit.craftbukkit.util.BlockStateListPopulator blockList = new org.bukkit.craftbukkit.util.BlockStateListPopulator(this.level); // CraftBukkit - Use BlockStateListPopulator - if (d == -1.0) { - int max = Math.max(this.level.getMinY() - -1, 70); - int i4 = min - 9; -@@ -123,7 +_,7 @@ - mutableBlockPos.setWithOffset( - blockPos, i2 * direction.getStepX() + i1x * clockWise.getStepX(), i3, i2 * direction.getStepZ() + i1x * clockWise.getStepZ() + if (closestFullDistanceSqr == -1.0) { + int minStartY = Math.max(this.level.getMinY() - -1, 70); + int maxStartY = maxPlaceableY - 9; +@@ -129,7 +_,7 @@ + height, + width * direction.getStepZ() + box * clockWise.getStepZ() ); -- this.level.setBlockAndUpdate(mutableBlockPos, blockState); -+ blockList.setBlock(mutableBlockPos, blockState, Block.UPDATE_ALL); // CraftBukkit +- this.level.setBlockAndUpdate(mutable, blockState); ++ blockList.setBlock(mutable, blockState, Block.UPDATE_ALL); // CraftBukkit } } } -@@ -133,7 +_,7 @@ - for (int i4 = -1; i4 < 4; i4++) { - if (max == -1 || max == 2 || i4 == -1 || i4 == 3) { - mutableBlockPos.setWithOffset(blockPos, max * direction.getStepX(), i4, max * direction.getStepZ()); -- this.level.setBlock(mutableBlockPos, Blocks.OBSIDIAN.defaultBlockState(), Block.UPDATE_ALL); -+ blockList.setBlock(mutableBlockPos, Blocks.OBSIDIAN.defaultBlockState(), Block.UPDATE_ALL); // CraftBukkit +@@ -139,7 +_,7 @@ + for (int height = -1; height < 4; height++) { + if (width == -1 || width == 2 || height == -1 || height == 3) { + mutable.setWithOffset(closestFullPosition, width * direction.getStepX(), height, width * direction.getStepZ()); +- this.level.setBlock(mutable, Blocks.OBSIDIAN.defaultBlockState(), Block.UPDATE_ALL); ++ blockList.setBlock(mutable, Blocks.OBSIDIAN.defaultBlockState(), Block.UPDATE_ALL); // CraftBukkit } } } -@@ -143,10 +_,20 @@ - for (int i4x = 0; i4x < 2; i4x++) { - for (int min1 = 0; min1 < 3; min1++) { - mutableBlockPos.setWithOffset(blockPos, i4x * direction.getStepX(), min1, i4x * direction.getStepZ()); -- this.level.setBlock(mutableBlockPos, blockState1, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); -+ blockList.setBlock(mutableBlockPos, blockState1, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); // CraftBukkit +@@ -149,10 +_,20 @@ + for (int width = 0; width < 2; width++) { + for (int heightx = 0; heightx < 3; heightx++) { + mutable.setWithOffset(closestFullPosition, width * direction.getStepX(), heightx, width * direction.getStepZ()); +- this.level.setBlock(mutable, portalBlockState, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); ++ blockList.setBlock(mutable, portalBlockState, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); // CraftBukkit } } + // CraftBukkit start + org.bukkit.World bworld = this.level.getWorld(); -+ org.bukkit.event.world.PortalCreateEvent event = new org.bukkit.event.world.PortalCreateEvent((java.util.List) (java.util.List) blockList.getSnapshotBlocks(), bworld, (entity == null) ? null : entity.getBukkitEntity(), org.bukkit.event.world.PortalCreateEvent.CreateReason.NETHER_PAIR); ++ org.bukkit.event.world.PortalCreateEvent event = new org.bukkit.event.world.PortalCreateEvent((java.util.List) (java.util.List) blockList.getSnapshotBlocks(), bworld, entity == null ? null : entity.getBukkitEntity(), org.bukkit.event.world.PortalCreateEvent.CreateReason.NETHER_PAIR); + + this.level.getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { @@ -96,20 +96,20 @@ + } + blockList.placeBlocks(); + // CraftBukkit end - return Optional.of(new BlockUtil.FoundRectangle(blockPos.immutable(), 2, 3)); + return Optional.of(new BlockUtil.FoundRectangle(closestFullPosition.immutable(), 2, 3)); } -@@ -166,6 +_,13 @@ - i1, - direction.getStepZ() * i + clockWise.getStepZ() * offsetScale +@@ -169,6 +_,13 @@ + mutable.setWithOffset( + origin, direction.getStepX() * width + clockWise.getStepX() * offset, height, direction.getStepZ() * width + clockWise.getStepZ() * offset ); + // Paper start - Protect Bedrock and End Portal/Frames from being destroyed + if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits) { -+ if (!this.level.getBlockState(offsetPos).isDestroyable()) { ++ if (!this.level.getBlockState(mutable).isDestroyable()) { + return false; + } + } + // Paper end - Protect Bedrock and End Portal/Frames from being destroyed - if (i1 < 0 && !this.level.getBlockState(offsetPos).isSolid()) { + if (height < 0 && !this.level.getBlockState(mutable).isSolid()) { return false; } diff --git a/paper-server/patches/sources/net/minecraft/world/level/portal/PortalShape.java.patch b/paper-server/patches/sources/net/minecraft/world/level/portal/PortalShape.java.patch index 9b1d58422549..17f9e3ae898f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/portal/PortalShape.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/portal/PortalShape.java.patch @@ -1,143 +1,148 @@ --- a/net/minecraft/world/level/portal/PortalShape.java +++ b/net/minecraft/world/level/portal/PortalShape.java -@@ -38,8 +_,12 @@ +@@ -38,10 +_,14 @@ private final BlockPos bottomLeft; private final int height; private final int width; + // CraftBukkit start - add field + private final org.bukkit.craftbukkit.util.BlockStateListPopulator blocks; -- private PortalShape(Direction.Axis axis, int numPortalBlocks, Direction rightDir, BlockPos bottomLeft, int width, int height) { -+ private PortalShape(Direction.Axis axis, int numPortalBlocks, Direction rightDir, BlockPos bottomLeft, int width, int height, org.bukkit.craftbukkit.util.BlockStateListPopulator blocks) { + private PortalShape( +- final Direction.Axis axis, final int portalBlockCount, final Direction rightDir, final BlockPos bottomLeft, final int width, final int height ++ final Direction.Axis axis, final int portalBlockCount, final Direction rightDir, final BlockPos bottomLeft, final int width, final int height, final org.bukkit.craftbukkit.util.BlockStateListPopulator blocks + ) { + this.blocks = blocks; + // CraftBukkit end this.axis = axis; - this.numPortalBlocks = numPortalBlocks; + this.numPortalBlocks = portalBlockCount; this.rightDir = rightDir; -@@ -63,23 +_,24 @@ +@@ -67,23 +_,24 @@ } - public static PortalShape findAnyShape(BlockGetter level, BlockPos bottomLeft, Direction.Axis axis) { + public static PortalShape findAnyShape(final BlockGetter level, final BlockPos pos, final Direction.Axis axis) { + org.bukkit.craftbukkit.util.BlockStateListPopulator blocks = new org.bukkit.craftbukkit.util.BlockStateListPopulator(((LevelAccessor) level).getMinecraftWorld()); // CraftBukkit - Direction direction = axis == Direction.Axis.X ? Direction.WEST : Direction.SOUTH; -- BlockPos blockPos = calculateBottomLeft(level, direction, bottomLeft); -+ BlockPos blockPos = calculateBottomLeft(level, direction, bottomLeft, blocks); // CraftBukkit - if (blockPos == null) { -- return new PortalShape(axis, 0, direction, bottomLeft, 0, 0); -+ return new PortalShape(axis, 0, direction, bottomLeft, 0, 0, blocks); // CraftBukkit + Direction rightDir = axis == Direction.Axis.X ? Direction.WEST : Direction.SOUTH; +- BlockPos bottomLeft = calculateBottomLeft(level, rightDir, pos); ++ BlockPos bottomLeft = calculateBottomLeft(level, rightDir, pos, blocks); // CraftBukkit + if (bottomLeft == null) { +- return new PortalShape(axis, 0, rightDir, pos, 0, 0); ++ return new PortalShape(axis, 0, rightDir, pos, 0, 0, blocks); // CraftBukkit } else { -- int i = calculateWidth(level, blockPos, direction); -+ int i = calculateWidth(level, blockPos, direction, blocks); // CraftBukkit - if (i == 0) { -- return new PortalShape(axis, 0, direction, blockPos, 0, 0); -+ return new PortalShape(axis, 0, direction, blockPos, 0, 0, blocks); // CraftBukkit +- int width = calculateWidth(level, bottomLeft, rightDir); ++ int width = calculateWidth(level, bottomLeft, rightDir, blocks); // CraftBukkit + if (width == 0) { +- return new PortalShape(axis, 0, rightDir, bottomLeft, 0, 0); ++ return new PortalShape(axis, 0, rightDir, bottomLeft, 0, 0, blocks); // CraftBukkit } else { - MutableInt mutableInt = new MutableInt(); -- int i1 = calculateHeight(level, blockPos, direction, i, mutableInt); -- return new PortalShape(axis, mutableInt.intValue(), direction, blockPos, i, i1); -+ int i1 = calculateHeight(level, blockPos, direction, i, mutableInt, blocks); // CraftBukkit -+ return new PortalShape(axis, mutableInt.intValue(), direction, blockPos, i, i1, blocks); // CraftBukkit + MutableInt portalBlockCountOutput = new MutableInt(); +- int height = calculateHeight(level, bottomLeft, rightDir, width, portalBlockCountOutput); +- return new PortalShape(axis, portalBlockCountOutput.intValue(), rightDir, bottomLeft, width, height); ++ int height = calculateHeight(level, bottomLeft, rightDir, width, portalBlockCountOutput, blocks); // CraftBukkit ++ return new PortalShape(axis, portalBlockCountOutput.intValue(), rightDir, bottomLeft, width, height, blocks); // CraftBukkit } } } -- private static @Nullable BlockPos calculateBottomLeft(BlockGetter level, Direction direction, BlockPos pos) { -+ private static @Nullable BlockPos calculateBottomLeft(BlockGetter level, Direction direction, BlockPos pos, org.bukkit.craftbukkit.util.BlockStateListPopulator blocks) { // CraftBukkit - int max = Math.max(level.getMinY(), pos.getY() - 21); +- private static @Nullable BlockPos calculateBottomLeft(final BlockGetter level, final Direction rightDir, BlockPos pos) { ++ private static @Nullable BlockPos calculateBottomLeft(final BlockGetter level, final Direction rightDir, BlockPos pos, final org.bukkit.craftbukkit.util.BlockStateListPopulator blocks) { // CraftBukkit + int minY = Math.max(level.getMinY(), pos.getY() - 21); - while (pos.getY() > max && isEmpty(level.getBlockState(pos.below()))) { -@@ -87,16 +_,16 @@ + while (pos.getY() > minY && isEmpty(level.getBlockState(pos.below()))) { +@@ -91,16 +_,16 @@ } - Direction opposite = direction.getOpposite(); -- int i = getDistanceUntilEdgeAboveFrame(level, pos, opposite) - 1; -+ int i = getDistanceUntilEdgeAboveFrame(level, pos, opposite, blocks) - 1; // CraftBukkit - return i < 0 ? null : pos.relative(opposite, i); + Direction leftDir = rightDir.getOpposite(); +- int edge = getDistanceUntilEdgeAboveFrame(level, pos, leftDir) - 1; ++ int edge = getDistanceUntilEdgeAboveFrame(level, pos, leftDir, blocks) - 1; // CraftBukkit + return edge < 0 ? null : pos.relative(leftDir, edge); } -- private static int calculateWidth(BlockGetter level, BlockPos bottomLeft, Direction direction) { -- int distanceUntilEdgeAboveFrame = getDistanceUntilEdgeAboveFrame(level, bottomLeft, direction); -+ private static int calculateWidth(BlockGetter level, BlockPos bottomLeft, Direction direction, org.bukkit.craftbukkit.util.BlockStateListPopulator blocks) { // CraftBukkit -+ int distanceUntilEdgeAboveFrame = getDistanceUntilEdgeAboveFrame(level, bottomLeft, direction, blocks); // CraftBukkit - return distanceUntilEdgeAboveFrame >= 2 && distanceUntilEdgeAboveFrame <= 21 ? distanceUntilEdgeAboveFrame : 0; +- private static int calculateWidth(final BlockGetter level, final BlockPos bottomLeft, final Direction rightDir) { +- int width = getDistanceUntilEdgeAboveFrame(level, bottomLeft, rightDir); ++ private static int calculateWidth(final BlockGetter level, final BlockPos bottomLeft, final Direction rightDir, final org.bukkit.craftbukkit.util.BlockStateListPopulator blocks) { // CraftBukkit ++ int width = getDistanceUntilEdgeAboveFrame(level, bottomLeft, rightDir, blocks); // CraftBukkit + return width >= 2 && width <= 21 ? width : 0; } -- private static int getDistanceUntilEdgeAboveFrame(BlockGetter level, BlockPos pos, Direction direction) { -+ private static int getDistanceUntilEdgeAboveFrame(BlockGetter level, BlockPos pos, Direction direction, org.bukkit.craftbukkit.util.BlockStateListPopulator blocks) { // CraftBukkit - BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); +- private static int getDistanceUntilEdgeAboveFrame(final BlockGetter level, final BlockPos pos, final Direction direction) { ++ private static int getDistanceUntilEdgeAboveFrame(final BlockGetter level, final BlockPos pos, final Direction direction, final org.bukkit.craftbukkit.util.BlockStateListPopulator blocks) { // CraftBukkit + BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); - for (int i = 0; i <= 21; i++) { -@@ -104,6 +_,7 @@ - BlockState blockState = level.getBlockState(mutableBlockPos); + for (int width = 0; width <= 21; width++) { +@@ -108,6 +_,7 @@ + BlockState blockState = level.getBlockState(blockPos); if (!isEmpty(blockState)) { - if (FRAME.test(blockState, level, mutableBlockPos)) { -+ blocks.setBlock(mutableBlockPos, blockState, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); // CraftBukkit - lower left / right - return i; + if (FRAME.test(blockState, level, blockPos)) { ++ blocks.setBlock(blockPos, blockState, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); // CraftBukkit - lower left / right + return width; } break; -@@ -113,32 +_,34 @@ - if (!FRAME.test(blockState1, level, mutableBlockPos)) { +@@ -117,27 +_,29 @@ + if (!FRAME.test(belowState, level, blockPos)) { break; } -+ blocks.setBlock(mutableBlockPos, blockState1, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); // CraftBukkit - bottom row ++ blocks.setBlock(blockPos, belowState, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); // CraftBukkit - bottom row } return 0; } -- private static int calculateHeight(BlockGetter level, BlockPos pos, Direction direction, int width, MutableInt portalBlocks) { -+ private static int calculateHeight(BlockGetter level, BlockPos pos, Direction direction, int width, MutableInt portalBlocks, org.bukkit.craftbukkit.util.BlockStateListPopulator blocks) { // CraftBukkit - BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); -- int distanceUntilTop = getDistanceUntilTop(level, pos, direction, mutableBlockPos, width, portalBlocks); -- return distanceUntilTop >= 3 && distanceUntilTop <= 21 && hasTopFrame(level, pos, direction, mutableBlockPos, width, distanceUntilTop) -+ int distanceUntilTop = getDistanceUntilTop(level, pos, direction, mutableBlockPos, width, portalBlocks, blocks); // CraftBukkit -+ return distanceUntilTop >= 3 && distanceUntilTop <= 21 && hasTopFrame(level, pos, direction, mutableBlockPos, width, distanceUntilTop, blocks) // CraftBukkit - ? distanceUntilTop - : 0; + private static int calculateHeight( +- final BlockGetter level, final BlockPos bottomLeft, final Direction rightDir, final int width, final MutableInt portalBlockCount ++ final BlockGetter level, final BlockPos bottomLeft, final Direction rightDir, final int width, final MutableInt portalBlockCount, final org.bukkit.craftbukkit.util.BlockStateListPopulator blocks // CraftBukkit + ) { + BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); +- int height = getDistanceUntilTop(level, bottomLeft, rightDir, pos, width, portalBlockCount); +- return height >= 3 && height <= 21 && hasTopFrame(level, bottomLeft, rightDir, pos, width, height) ? height : 0; ++ int height = getDistanceUntilTop(level, bottomLeft, rightDir, pos, width, portalBlockCount, blocks); // CraftBukkit ++ return height >= 3 && height <= 21 && hasTopFrame(level, bottomLeft, rightDir, pos, width, height, blocks) ? height : 0; // CraftBukkit } -- private static boolean hasTopFrame(BlockGetter level, BlockPos pos, Direction direction, BlockPos.MutableBlockPos checkPos, int width, int distanceUntilTop) { -+ private static boolean hasTopFrame(BlockGetter level, BlockPos pos, Direction direction, BlockPos.MutableBlockPos checkPos, int width, int distanceUntilTop, org.bukkit.craftbukkit.util.BlockStateListPopulator blocks) { // CraftBukkit + private static boolean hasTopFrame( +- final BlockGetter level, final BlockPos bottomLeft, final Direction rightDir, final BlockPos.MutableBlockPos pos, final int width, final int height ++ final BlockGetter level, final BlockPos bottomLeft, final Direction rightDir, final BlockPos.MutableBlockPos pos, final int width, final int height, final org.bukkit.craftbukkit.util.BlockStateListPopulator blocks // CraftBukkit + ) { for (int i = 0; i < width; i++) { - BlockPos.MutableBlockPos mutableBlockPos = checkPos.set(pos).move(Direction.UP, distanceUntilTop).move(direction, i); - if (!FRAME.test(level.getBlockState(mutableBlockPos), level, mutableBlockPos)) { + BlockPos.MutableBlockPos framePos = pos.set(bottomLeft).move(Direction.UP, height).move(rightDir, i); + if (!FRAME.test(level.getBlockState(framePos), level, framePos)) { return false; } -+ blocks.setBlock(mutableBlockPos, level.getBlockState(mutableBlockPos), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); // CraftBukkit - upper row ++ blocks.setBlock(framePos, level.getBlockState(framePos), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); // CraftBukkit - upper row } return true; - } - - private static int getDistanceUntilTop( -- BlockGetter level, BlockPos pos, Direction direction, BlockPos.MutableBlockPos checkPos, int width, MutableInt portalBlocks -+ BlockGetter level, BlockPos pos, Direction direction, BlockPos.MutableBlockPos checkPos, int width, MutableInt portalBlocks, org.bukkit.craftbukkit.util.BlockStateListPopulator blocks // CraftBukkit +@@ -149,7 +_,7 @@ + final Direction rightDir, + final BlockPos.MutableBlockPos pos, + final int width, +- final MutableInt portalBlockCount ++ final MutableInt portalBlockCount, final org.bukkit.craftbukkit.util.BlockStateListPopulator blocks // CraftBukkit ) { - for (int i = 0; i < 21; i++) { - checkPos.set(pos).move(Direction.UP, i).move(direction, -1); -@@ -162,6 +_,10 @@ - portalBlocks.increment(); + for (int height = 0; height < 21; height++) { + pos.set(bottomLeft).move(Direction.UP, height).move(rightDir, -1); +@@ -173,6 +_,10 @@ + portalBlockCount.increment(); } } + // CraftBukkit start - left and right -+ blocks.setBlock(checkPos.set(pos).move(Direction.UP, i).move(direction, -1), level.getBlockState(checkPos), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); -+ blocks.setBlock(checkPos.set(pos).move(Direction.UP, i).move(direction, width), level.getBlockState(checkPos), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); ++ blocks.setBlock(pos.set(bottomLeft).move(Direction.UP, height).move(rightDir, -1), level.getBlockState(pos), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); ++ blocks.setBlock(pos.set(bottomLeft).move(Direction.UP, height).move(rightDir, width), level.getBlockState(pos), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); + // CraftBukkit end } return 21; -@@ -175,10 +_,23 @@ +@@ -186,10 +_,23 @@ return this.width >= 2 && this.width <= 21 && this.height >= 3 && this.height <= 21; } -- public void createPortalBlocks(LevelAccessor level) { +- public void createPortalBlocks(final LevelAccessor level) { + // CraftBukkit start - return boolean, add entity -+ public boolean createPortalBlocks(LevelAccessor level, Entity entity) { ++ public boolean createPortalBlocks(final LevelAccessor level, final Entity entity) { + org.bukkit.World bworld = level.getMinecraftWorld().getWorld(); + // Copy below for loop - BlockState blockState = Blocks.NETHER_PORTAL.defaultBlockState().setValue(NetherPortalBlock.AXIS, this.axis); + BlockState portalState = Blocks.NETHER_PORTAL.defaultBlockState().setValue(NetherPortalBlock.AXIS, this.axis); BlockPos.betweenClosed(this.bottomLeft, this.bottomLeft.relative(Direction.UP, this.height - 1).relative(this.rightDir, this.width - 1)) -+ .forEach(pos -> this.blocks.setBlock(pos, blockState, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE)); ++ .forEach(pos -> this.blocks.setBlock(pos, portalState, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE)); + org.bukkit.event.world.PortalCreateEvent event = new org.bukkit.event.world.PortalCreateEvent((java.util.List) (java.util.List) this.blocks.getSnapshotBlocks(), bworld, (entity == null) ? null : entity.getBukkitEntity(), org.bukkit.event.world.PortalCreateEvent.CreateReason.FIRE); + level.getMinecraftWorld().getServer().server.getPluginManager().callEvent(event); // todo the list is not really mutable here unlike other call and the portal frame is included + @@ -146,7 +151,7 @@ + } + // CraftBukkit end + BlockPos.betweenClosed(this.bottomLeft, this.bottomLeft.relative(Direction.UP, this.height - 1).relative(this.rightDir, this.width - 1)) - .forEach(pos -> level.setBlock(pos, blockState, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE)); + .forEach(pos -> level.setBlock(pos, portalState, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE)); + return true; // CraftBukkit } diff --git a/paper-server/patches/sources/net/minecraft/world/level/portal/TeleportTransition.java.patch b/paper-server/patches/sources/net/minecraft/world/level/portal/TeleportTransition.java.patch index e497a2065335..63db595304f0 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/portal/TeleportTransition.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/portal/TeleportTransition.java.patch @@ -17,15 +17,15 @@ + // CraftBukkit end + public TeleportTransition( - ServerLevel newLevel, Vec3 position, Vec3 deltaMovement, float yRot, float xRot, TeleportTransition.PostTeleportTransition postTeleportTransition + final ServerLevel newLevel, + final Vec3 pos, +@@ -46,7 +_,21 @@ + final Set relatives, + final TeleportTransition.PostTeleportTransition postTeleportTransition ) { -@@ -41,7 +_,21 @@ - Set relatives, - TeleportTransition.PostTeleportTransition postTeleportTransition - ) { -- this(newLevel, position, deltaMovement, yRot, xRot, false, false, relatives, postTeleportTransition); +- this(newLevel, pos, speed, yRot, xRot, false, false, relatives, postTeleportTransition); + // CraftBukkit start -+ this(newLevel, position, deltaMovement, yRot, xRot, relatives, postTeleportTransition, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN); ++ this(newLevel, pos, speed, yRot, xRot, relatives, postTeleportTransition, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN); + } + public TeleportTransition( + ServerLevel newLevel, @@ -41,8 +41,8 @@ + // CraftBukkit end } - private static void playPortalSound(Entity entity) { -@@ -100,7 +_,8 @@ + private static void playPortalSound(final Entity entity) { +@@ -105,7 +_,8 @@ this.missingRespawnBlock(), this.asPassenger(), this.relatives(), @@ -52,7 +52,7 @@ ); } -@@ -114,7 +_,8 @@ +@@ -119,7 +_,8 @@ this.missingRespawnBlock(), this.asPassenger(), this.relatives(), @@ -62,7 +62,7 @@ ); } -@@ -128,7 +_,8 @@ +@@ -133,7 +_,8 @@ this.missingRespawnBlock(), true, this.relatives(), diff --git a/paper-server/patches/sources/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java.patch b/paper-server/patches/sources/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java.patch index bdae25a276df..5fd5b79ef7ef 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java +++ b/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java -@@ -145,7 +_,7 @@ +@@ -158,7 +_,7 @@ orientation = this.orientation.withFront(direction); } -- NeighborUpdater.executeUpdate(level, blockState, blockPos, this.sourceBlock, orientation, false); -+ NeighborUpdater.executeUpdate(level, blockState, blockPos, this.sourceBlock, orientation, false, this.sourcePos); // Paper - Add source block to BlockPhysicsEvent +- NeighborUpdater.executeUpdate(level, state, neighborPos, this.sourceBlock, orientation, false); ++ NeighborUpdater.executeUpdate(level, state, neighborPos, this.sourceBlock, orientation, false, this.sourcePos); // Paper - Add source block to BlockPhysicsEvent if (this.idx < NeighborUpdater.UPDATE_ORDER.length && NeighborUpdater.UPDATE_ORDER[this.idx] == this.skipDirection) { this.idx++; } diff --git a/paper-server/patches/sources/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java.patch b/paper-server/patches/sources/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java.patch index 772d6eb81bc4..f7be4cf5d8f9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java +++ b/net/minecraft/world/level/redstone/DefaultRedstoneWireEvaluator.java -@@ -18,7 +_,16 @@ - @Override - public void updatePowerStrength(Level level, BlockPos pos, BlockState state, @Nullable Orientation orientation, boolean updateShape) { - int i = this.calculateTargetStrength(level, pos); -- if (state.getValue(RedStoneWireBlock.POWER) != i) { +@@ -20,7 +_,16 @@ + final Level level, final BlockPos pos, final BlockState state, final @Nullable Orientation orientation, final boolean skipShapeUpdates + ) { + int targetStrength = this.calculateTargetStrength(level, pos); +- if (state.getValue(RedStoneWireBlock.POWER) != targetStrength) { + // CraftBukkit start + int oldPower = state.getValue(RedStoneWireBlock.POWER); -+ if (oldPower != i) { -+ org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), oldPower, i); ++ if (oldPower != targetStrength) { ++ org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), oldPower, targetStrength); + level.getCraftServer().getPluginManager().callEvent(event); + -+ i = event.getNewCurrent(); ++ targetStrength = event.getNewCurrent(); + } -+ if (oldPower != i) { ++ if (oldPower != targetStrength) { + // CraftBukkit end if (level.getBlockState(pos) == state) { - level.setBlock(pos, state.setValue(RedStoneWireBlock.POWER, i), Block.UPDATE_CLIENTS); + level.setBlock(pos, state.setValue(RedStoneWireBlock.POWER, targetStrength), Block.UPDATE_CLIENTS); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/redstone/ExperimentalRedstoneWireEvaluator.java.patch b/paper-server/patches/sources/net/minecraft/world/level/redstone/ExperimentalRedstoneWireEvaluator.java.patch index 15f6e44d7221..e633621dfe80 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/redstone/ExperimentalRedstoneWireEvaluator.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/redstone/ExperimentalRedstoneWireEvaluator.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/world/level/redstone/ExperimentalRedstoneWireEvaluator.java +++ b/net/minecraft/world/level/redstone/ExperimentalRedstoneWireEvaluator.java -@@ -39,7 +_,16 @@ - int intValue = entry.getIntValue(); - int i = unpackPower(intValue); - BlockState blockState = level.getBlockState(blockPos); -- if (blockState.is(this.wireBlock) && !blockState.getValue(RedStoneWireBlock.POWER).equals(i)) { +@@ -45,7 +_,16 @@ + int packed = next.getIntValue(); + int newLevel = unpackPower(packed); + BlockState state = level.getBlockState(pos); +- if (state.is(this.wireBlock) && !state.getValue(RedStoneWireBlock.POWER).equals(newLevel)) { + // CraftBukkit start -+ int oldPower = blockState.getValue(RedStoneWireBlock.POWER); // Paper - Call BlockRedstoneEvent properly; get the previous power from the right state -+ if (oldPower != i) { -+ org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos), oldPower, i); ++ int oldPower = state.getValue(RedStoneWireBlock.POWER); // Paper - Call BlockRedstoneEvent properly; get the previous power from the right state ++ if (oldPower != newLevel) { ++ org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), oldPower, newLevel); + level.getCraftServer().getPluginManager().callEvent(event); + -+ i = event.getNewCurrent(); ++ newLevel = event.getNewCurrent(); + } -+ if (blockState.is(this.wireBlock) && oldPower != i) { ++ if (state.is(this.wireBlock) && oldPower != newLevel) { + // CraftBukkit end - int i1 = Block.UPDATE_CLIENTS; - if (!updateShape || !flag) { - i1 |= Block.UPDATE_SKIP_SHAPE_UPDATE_ON_WIRE; + int updateFlags = Block.UPDATE_CLIENTS; + if (!shapeUpdateWiresAroundInitialPosition || !initialWire) { + updateFlags |= Block.UPDATE_SKIP_SHAPE_UPDATE_ON_WIRE; diff --git a/paper-server/patches/sources/net/minecraft/world/level/redstone/NeighborUpdater.java.patch b/paper-server/patches/sources/net/minecraft/world/level/redstone/NeighborUpdater.java.patch index fa7488532957..d9bc406db00b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/redstone/NeighborUpdater.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/redstone/NeighborUpdater.java.patch @@ -1,29 +1,29 @@ --- a/net/minecraft/world/level/redstone/NeighborUpdater.java +++ b/net/minecraft/world/level/redstone/NeighborUpdater.java -@@ -42,8 +_,26 @@ - } - - static void executeUpdate(Level level, BlockState state, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { +@@ -66,8 +_,26 @@ + final @Nullable Orientation orientation, + final boolean movedByPiston + ) { + // Paper start - Add source block to BlockPhysicsEvent -+ executeUpdate(level, state, pos, neighborBlock, orientation, movedByPiston, pos); ++ executeUpdate(level, state, pos, changedBlock, orientation, movedByPiston, pos); + } + -+ static void executeUpdate(Level level, BlockState state, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston, BlockPos sourcePos) { ++ static void executeUpdate(Level level, BlockState state, BlockPos pos, Block changedBlock, @Nullable Orientation orientation, boolean movedByPiston, BlockPos sourcePos) { + // Paper end - Add source block to BlockPhysicsEvent try { + // CraftBukkit start -+ org.bukkit.event.block.BlockPhysicsEvent event = new org.bukkit.event.block.BlockPhysicsEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(state), org.bukkit.craftbukkit.block.CraftBlock.at(level, sourcePos)); // Paper - Add source block to BlockPhysicsEvent ++ org.bukkit.event.block.BlockPhysicsEvent event = new org.bukkit.event.block.BlockPhysicsEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), state.asBlockData(), org.bukkit.craftbukkit.block.CraftBlock.at(level, sourcePos)); // Paper - Add source block to BlockPhysicsEvent + level.getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + // CraftBukkit end - state.handleNeighborChanged(level, pos, neighborBlock, orientation, movedByPiston); + state.handleNeighborChanged(level, pos, changedBlock, orientation, movedByPiston); + // Spigot start + } catch (StackOverflowError ex) { + level.lastPhysicsProblem = pos.immutable(); + // Spigot end } catch (Throwable var9) { - CrashReport crashReport = CrashReport.forThrowable(var9, "Exception while updating neighbours"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Block being updated"); + CrashReport report = CrashReport.forThrowable(var9, "Exception while updating neighbours"); + CrashReportCategory category = report.addCategory("Block being updated"); diff --git a/paper-server/patches/sources/net/minecraft/world/level/saveddata/WeatherData.java.patch b/paper-server/patches/sources/net/minecraft/world/level/saveddata/WeatherData.java.patch new file mode 100644 index 000000000000..93146e5d9de2 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/saveddata/WeatherData.java.patch @@ -0,0 +1,67 @@ +--- a/net/minecraft/world/level/saveddata/WeatherData.java ++++ b/net/minecraft/world/level/saveddata/WeatherData.java +@@ -50,6 +_,25 @@ + } + + public void setThundering(final boolean thundering) { ++ // Paper start - Add cause to Weather/ThunderChangeEvents ++ this.setThundering(thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.UNKNOWN); ++ } ++ public void setThundering(boolean thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause cause) { ++ // Paper end - Add cause to Weather/ThunderChangeEvents ++ // CraftBukkit start ++ if (this.thundering == thundering) { ++ return; ++ } ++ ++ org.bukkit.World world = this.level == null ? null : this.level.getWorld(); ++ if (world != null) { ++ org.bukkit.event.weather.ThunderChangeEvent thunder = new org.bukkit.event.weather.ThunderChangeEvent(world, thundering, cause); // Paper - Add cause to Weather/ThunderChangeEvents ++ org.bukkit.Bukkit.getServer().getPluginManager().callEvent(thunder); ++ if (thunder.isCancelled()) { ++ return; ++ } ++ } ++ // CraftBukkit end + this.thundering = thundering; + this.setDirty(); + } +@@ -68,6 +_,26 @@ + } + + public void setRaining(final boolean raining) { ++ // Paper start - Add cause to Weather/ThunderChangeEvents ++ this.setRaining(raining, org.bukkit.event.weather.WeatherChangeEvent.Cause.UNKNOWN); ++ } ++ ++ public void setRaining(boolean raining, org.bukkit.event.weather.WeatherChangeEvent.Cause cause) { ++ // Paper end - Add cause to Weather/ThunderChangeEvents ++ // CraftBukkit start ++ if (this.raining == raining) { ++ return; ++ } ++ ++ org.bukkit.World world = this.level == null ? null : this.level.getWorld(); ++ if (world != null) { ++ org.bukkit.event.weather.WeatherChangeEvent weather = new org.bukkit.event.weather.WeatherChangeEvent(world, raining, cause); // Paper - Add cause to Weather/ThunderChangeEvents ++ org.bukkit.Bukkit.getServer().getPluginManager().callEvent(weather); ++ if (weather.isCancelled()) { ++ return; ++ } ++ } ++ // CraftBukkit end + this.raining = raining; + this.setDirty(); + } +@@ -80,4 +_,11 @@ + this.rainTime = rainTime; + this.setDirty(); + } ++ ++ // Paper start - pass level for events ++ private net.minecraft.server.level.@org.jspecify.annotations.Nullable ServerLevel level; ++ public void setLevel(final net.minecraft.server.level.ServerLevel level) { ++ this.level = level; ++ } ++ // Paper end - pass level for events + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java.patch b/paper-server/patches/sources/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java.patch index 2ac346fb508f..0a014f3cb6c1 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java.patch @@ -1,15 +1,15 @@ --- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java +++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java -@@ -50,7 +_,7 @@ +@@ -48,7 +_,7 @@ private static final String FRAME_PREFIX = "frame-"; public static final Codec CODEC = RecordCodecBuilder.create( - instance -> instance.group( -- Level.RESOURCE_KEY_CODEC.fieldOf("dimension").forGetter(mapItemSavedData -> mapItemSavedData.dimension), + i -> i.group( +- Level.RESOURCE_KEY_CODEC.fieldOf("dimension").forGetter(m -> m.dimension), + createUUIDBackedDimensionKeyCodec().forGetter(MapItemSavedData::packUUIDBackedDimension), // Paper - store target world by uuid in addition to dimension - Codec.INT.fieldOf("xCenter").forGetter(mapItemSavedData -> mapItemSavedData.centerX), - Codec.INT.fieldOf("zCenter").forGetter(mapItemSavedData -> mapItemSavedData.centerZ), - Codec.BYTE.optionalFieldOf("scale", (byte)0).forGetter(mapItemSavedData -> mapItemSavedData.scale), -@@ -74,6 +_,7 @@ + Codec.INT.fieldOf("xCenter").forGetter(m -> m.centerX), + Codec.INT.fieldOf("zCenter").forGetter(m -> m.centerZ), + Codec.BYTE.optionalFieldOf("scale", (byte)0).forGetter(m -> m.scale), +@@ -69,6 +_,7 @@ public byte scale; public byte[] colors = new byte[16384]; public boolean locked; @@ -17,7 +17,7 @@ public final List carriedBy = Lists.newArrayList(); public final Map carriedByPlayers = Maps.newHashMap(); private final Map bannerMarkers = Maps.newHashMap(); -@@ -81,6 +_,13 @@ +@@ -76,6 +_,13 @@ private final Map frameMarkers = Maps.newHashMap(); private int trackedDecorationCount; @@ -28,10 +28,10 @@ + public MapId id; + // CraftBukkit end + - public static SavedDataType type(MapId mapId) { - return new SavedDataType<>(mapId.key(), () -> { + public static SavedDataType type(final MapId id) { + return new SavedDataType<>(Identifier.withDefaultNamespace(id.key()), () -> { throw new IllegalStateException("Should never create an empty map saved data"); -@@ -97,7 +_,29 @@ +@@ -98,7 +_,29 @@ this.trackingPosition = trackingPosition; this.unlimitedTracking = unlimitedTracking; this.locked = locked; @@ -61,86 +61,86 @@ + // Paper end - store target world by uuid in addition to dimension private MapItemSavedData( - ResourceKey dimension, -@@ -129,6 +_,8 @@ - MapDecorationTypes.FRAME, null, getFrameKey(mapFrame.entityId()), mapFrame.pos().getX(), mapFrame.pos().getZ(), mapFrame.rotation(), null - ); + final ResourceKey dimension, +@@ -126,6 +_,8 @@ + this.frameMarkers.put(frame.getId(), frame); + this.addDecoration(MapDecorationTypes.FRAME, null, getFrameKey(frame.entityId()), frame.pos().getX(), frame.pos().getZ(), frame.rotation(), null); } + + this.vanillaRender.buffer = colors.array(); // Paper - Use Vanilla map renderer when possible } public static MapItemSavedData createFresh( -@@ -206,6 +_,7 @@ +@@ -209,6 +_,7 @@ } - MapFrame mapFrame1 = new MapFrame(pos, frame.getDirection().get2DDataValue() * 90, frame.getId()); -+ if (this.decorations.size() < player.level().paperConfig().maps.itemFrameCursorLimit) { // Paper - Limit item frame cursors on maps + MapFrame mapFrame = new MapFrame(pos, placedInFrame.getDirection().get2DDataValue() * 90, placedInFrame.getId()); ++ if (this.decorations.size() < tickingPlayer.level().paperConfig().maps.itemFrameCursorLimit) { // Paper - Limit item frame cursors on maps this.addDecoration( - MapDecorationTypes.FRAME, player.level(), getFrameKey(frame.getId()), pos.getX(), pos.getZ(), frame.getDirection().get2DDataValue() * 90, null - ); -@@ -213,6 +_,7 @@ - if (!mapFrame1.equals(mapFrame2)) { + MapDecorationTypes.FRAME, + tickingPlayer.level(), +@@ -222,6 +_,7 @@ + if (!mapFrame.equals(oldFrame)) { this.setDirty(); } + } // Paper - Limit item frame cursors on maps } - MapDecorations mapDecorations = mapStack.getOrDefault(DataComponents.MAP_DECORATIONS, MapDecorations.EMPTY); -@@ -243,7 +_,7 @@ + MapDecorations staticDecorations = itemStack.getOrDefault(DataComponents.MAP_DECORATIONS, MapDecorations.EMPTY); +@@ -252,7 +_,7 @@ this.trackedDecorationCount--; } - this.setDecorationsDirty(); -+ if (mapDecoration != null) this.setDecorationsDirty(); // Paper - only mark dirty if a change occurs ++ if (decoration != null) this.setDecorationsDirty(); // Paper - only mark dirty if a change occurs } - public static void addTargetDecoration(ItemStack stack, BlockPos pos, String type, Holder mapDecorationType) { -@@ -352,7 +_,12 @@ + public static void addTargetDecoration(final ItemStack itemStack, final BlockPos position, final String key, final Holder decorationType) { +@@ -375,7 +_,12 @@ } - public void setColorsDirty(int x, int z) { + public void setColorsDirty(final int x, final int y) { - this.setDirty(); + // Paper start - Fix unnecessary map data saves -+ this.setColorsDirty(x, z, true); ++ this.setColorsDirty(x, y, true); + } -+ public void setColorsDirty(int x, int z, boolean markFileDirty) { ++ public void setColorsDirty(final int x, final int y, final boolean markFileDirty) { + if (markFileDirty) this.setDirty(); + // Paper end - Fix unnecessary map data saves for (MapItemSavedData.HoldingPlayer holdingPlayer : this.carriedBy) { - holdingPlayer.markColorsDirty(x, z); -@@ -393,7 +_,7 @@ + holdingPlayer.markColorsDirty(x, y); +@@ -416,7 +_,7 @@ return true; } - if (!this.isTrackedCountOverLimit(256)) { + if (!this.isTrackedCountOverLimit(((Level) level).paperConfig().maps.itemFrameCursorLimit)) { // Paper - Limit item frame cursors on maps - this.bannerMarkers.put(mapBanner.getId(), mapBanner); - this.addDecoration(mapBanner.getDecoration(), level, mapBanner.getId(), d, d1, 180.0, mapBanner.name().orElse(null)); + this.bannerMarkers.put(banner.getId(), banner); + this.addDecoration(banner.getDecoration(), level, banner.getId(), xPos, zPos, 180.0, banner.name().orElse(null)); this.setDirty(); -@@ -495,7 +_,7 @@ +@@ -524,7 +_,7 @@ this.player = player; } - private MapItemSavedData.MapPatch createPatch() { -+ private MapItemSavedData.MapPatch createPatch(byte[] buffer) { // CraftBukkit - int i = this.minDirtyX; - int i1 = this.minDirtyY; - int i2 = this.maxDirtyX + 1 - this.minDirtyX; -@@ -504,7 +_,7 @@ ++ private MapItemSavedData.MapPatch createPatch(final byte[] buffer) { // CraftBukkit + int startX = this.minDirtyX; + int startY = this.minDirtyY; + int width = this.maxDirtyX + 1 - this.minDirtyX; +@@ -533,7 +_,7 @@ - for (int i4 = 0; i4 < i2; i4++) { - for (int i5 = 0; i5 < i3; i5++) { -- bytes[i4 + i5 * i2] = MapItemSavedData.this.colors[i + i4 + (i1 + i5) * 128]; -+ bytes[i4 + i5 * i2] = buffer[i + i4 + (i1 + i5) * 128]; // CraftBukkit + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { +- patch[x + y * width] = MapItemSavedData.this.colors[startX + x + (startY + y) * 128]; ++ patch[x + y * width] = buffer[startX + x + (startY + y) * 128]; // CraftBukkit } } -@@ -513,17 +_,38 @@ +@@ -542,17 +_,38 @@ - @Nullable Packet nextUpdatePacket(MapId mapId) { - MapItemSavedData.MapPatch mapPatch; + private @Nullable Packet nextUpdatePacket(final MapId id) { + MapItemSavedData.MapPatch patch; + // Paper start + if (!this.dirtyData && this.tick % 5 != 0) { + // this won't end up sending, so don't render it! @@ -154,17 +154,17 @@ + // Paper end if (this.dirtyData) { this.dirtyData = false; -- mapPatch = this.createPatch(); -+ mapPatch = this.createPatch(render.buffer); // CraftBukkit +- patch = this.createPatch(); ++ patch = this.createPatch(render.buffer); // CraftBukkit } else { - mapPatch = null; + patch = null; } - Collection collection; + Collection decorations; - if (this.dirtyDecorations && this.tick++ % 5 == 0) { + if ((!vanillaMaps || this.dirtyDecorations) && this.tick++ % 5 == 0) { // Paper - bypass dirtyDecorations for custom maps this.dirtyDecorations = false; -- collection = MapItemSavedData.this.decorations.values(); +- decorations = MapItemSavedData.this.decorations.values(); + // CraftBukkit start + Collection icons = new java.util.ArrayList<>(); + if (vanillaMaps) this.addSeenPlayers(icons); // Paper @@ -174,12 +174,12 @@ + icons.add(new MapDecoration(org.bukkit.craftbukkit.map.CraftMapCursor.CraftType.bukkitToMinecraftHolder(cursor.getType()), cursor.getX(), cursor.getY(), cursor.getDirection(), Optional.ofNullable(io.papermc.paper.adventure.PaperAdventure.asVanilla(cursor.caption())))); + } + } -+ collection = icons; ++ decorations = icons; + // CraftBukkit end } else { - collection = null; + decorations = null; } -@@ -551,6 +_,23 @@ +@@ -580,6 +_,23 @@ private void markDecorationsDirty() { this.dirtyDecorations = true; } @@ -202,8 +202,8 @@ + // Paper end } - record MapDecorationLocation(Holder type, byte x, byte y, byte rot) { -@@ -595,4 +_,71 @@ + private record MapDecorationLocation(Holder type, byte x, byte y, byte rot) { +@@ -624,4 +_,71 @@ } } } @@ -213,14 +213,14 @@ + + } + record UUIDBackedDimension(@Nullable ResourceKey resourceKey, @Nullable UUIDAndError uuid) { -+ public UUIDBackedDimension(final @org.jetbrains.annotations.NotNull ResourceKey resourceKey) { ++ public UUIDBackedDimension(final ResourceKey resourceKey) { + this(resourceKey, null); + } + public UUIDBackedDimension { + com.google.common.base.Preconditions.checkArgument(resourceKey != null || uuid != null, "Created uuid backed dimension with null level and uuid. This is a bug"); + } + -+ public @org.jetbrains.annotations.NotNull ResourceKey resolveOrThrow() { ++ public ResourceKey resolveOrThrow() { + if (resourceKey != null) return resourceKey; + + final org.bukkit.World worldByUUID = org.bukkit.Bukkit.getWorld(uuid.uuid()); diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/DimensionDataStorage.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/DimensionDataStorage.java.patch deleted file mode 100644 index 81c5212d5251..000000000000 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/DimensionDataStorage.java.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- a/net/minecraft/world/level/storage/DimensionDataStorage.java -+++ b/net/minecraft/world/level/storage/DimensionDataStorage.java -@@ -145,7 +_,7 @@ - } else { - int i = Util.maxAllowedExecutorThreads(); - int size = map.size(); -- if (size > i) { -+ if (false && size > i) { // Paper - Separate dimension data IO pool; just throw them into the fixed pool queue - this.pendingWriteFuture = this.pendingWriteFuture.thenCompose(object -> { - List> list = new ArrayList<>(i); - int i1 = Mth.positiveCeilDiv(size, i); -@@ -166,7 +_,7 @@ - object -> CompletableFuture.allOf( - map.entrySet() - .stream() -- .map(entry -> CompletableFuture.runAsync(() -> this.tryWrite(entry.getKey(), entry.getValue()), Util.ioPool())) -+ .map(entry -> CompletableFuture.runAsync(() -> this.tryWrite(entry.getKey(), entry.getValue()), Util.DIMENSION_DATA_IO_POOL)) // Paper - Separate dimension data IO pool - .toArray(CompletableFuture[]::new) - ) - ); diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/LevelData.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/LevelData.java.patch index 41797a9d2508..8cf7cb88a4d8 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/LevelData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/LevelData.java.patch @@ -1,15 +1,11 @@ --- a/net/minecraft/world/level/storage/LevelData.java +++ b/net/minecraft/world/level/storage/LevelData.java -@@ -72,5 +_,25 @@ +@@ -62,5 +_,21 @@ public BlockPos pos() { return this.globalPos.pos(); } + + // Paper start -+ public RespawnData withLevel(ResourceKey dimension) { -+ return new RespawnData(GlobalPos.of(dimension, this.pos()), this.yaw, this.pitch); -+ } -+ + /** + * Equals without checking dimension. + * diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/LevelStorageSource.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/LevelStorageSource.java.patch index af68b818219b..6daa64c0a0d4 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/LevelStorageSource.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/LevelStorageSource.java.patch @@ -1,78 +1,38 @@ --- a/net/minecraft/world/level/storage/LevelStorageSource.java +++ b/net/minecraft/world/level/storage/LevelStorageSource.java -@@ -142,6 +_,7 @@ - PrimaryLevelData primaryLevelData = PrimaryLevelData.parse( - dynamic, levelSettings, complete.specialWorldProperty(), worldGenSettings.options(), lifecycle - ); -+ primaryLevelData.pdc = (Tag) dynamic.getElement("BukkitValues", null); // CraftBukkit - Add PDC to world - return new LevelDataAndDimensions(primaryLevelData, complete); - } - -@@ -336,25 +_,39 @@ - return this.backupDir; - } - -- public LevelStorageSource.LevelStorageAccess validateAndCreateAccess(String saveName) throws IOException, ContentValidationException { -+ public LevelStorageSource.LevelStorageAccess validateAndCreateAccess(String saveName, ResourceKey dimensionType) throws IOException, ContentValidationException { // CraftBukkit - Path levelPath = this.getLevelPath(saveName); -- List list = this.worldDirValidator.validateDirectory(levelPath, true); -+ List list = Boolean.getBoolean("paper.disableWorldSymlinkValidation") ? List.of() : this.worldDirValidator.validateDirectory(levelPath, true); // Paper - add skipping of symlinks scan - if (!list.isEmpty()) { - throw new ContentValidationException(levelPath, list); +@@ -149,12 +_,13 @@ + final WorldDataConfiguration dataConfiguration, + final Registry datapackDimensions, + final HolderLookup.Provider registryAccess ++ , final ResourceKey dimension // Paper - pass dimension + ) { + if (DataFixers.getFileFixer().requiresFileFixing(NbtUtils.getDataVersion(levelDataTag))) { + throw new IllegalStateException("Cannot get level data without file fixing first"); } else { -- return new LevelStorageSource.LevelStorageAccess(saveName, levelPath); -+ return new LevelStorageSource.LevelStorageAccess(saveName, levelPath, dimensionType); // CraftBukkit - } - } - -- public LevelStorageSource.LevelStorageAccess createAccess(String saveName) throws IOException { -+ public LevelStorageSource.LevelStorageAccess createAccess(String saveName, ResourceKey dimensionType) throws IOException { // CraftBukkit - Path levelPath = this.getLevelPath(saveName); -- return new LevelStorageSource.LevelStorageAccess(saveName, levelPath); -+ return new LevelStorageSource.LevelStorageAccess(saveName, levelPath, dimensionType); // CraftBukkit + Dynamic dataTag = RegistryOps.injectRegistryContext(levelDataTag, registryAccess); +- WorldGenSettings worldGenSettings = readExistingSavedData(worldAccess, registryAccess, WorldGenSettings.TYPE) ++ WorldGenSettings worldGenSettings = readExistingSavedData(worldAccess, dimension, registryAccess, WorldGenSettings.TYPE) // Paper - pass dimension + .mapOrElse( + Function.identity(), + error -> { +@@ -174,9 +_,9 @@ } - public DirectoryValidator getWorldDirValidator() { - return this.worldDirValidator; - } - -+ // CraftBukkit start -+ public static Path getStorageFolder(Path path, ResourceKey dimensionType) { -+ if (dimensionType == LevelStem.OVERWORLD) { -+ return path; -+ } else if (dimensionType == LevelStem.NETHER) { -+ return path.resolve("DIM-1"); -+ } else if (dimensionType == LevelStem.END) { -+ return path.resolve("DIM1"); -+ } else { -+ return path.resolve("dimensions").resolve(dimensionType.identifier().getNamespace()).resolve(dimensionType.identifier().getPath()); -+ } -+ } -+ // CraftBukkit end -+ - public record LevelCandidates(List levels) implements Iterable { - public boolean isEmpty() { - return this.levels.isEmpty(); -@@ -405,8 +_,12 @@ - public final LevelStorageSource.LevelDirectory levelDirectory; - private final String levelId; - private final Map resources = Maps.newHashMap(); -+ // CraftBukkit start -+ public final ResourceKey dimensionType; - -- LevelStorageAccess(final String levelId, final Path levelDir) throws IOException { -+ LevelStorageAccess(final String levelId, final Path levelDir, final ResourceKey dimensionType) throws IOException { -+ this.dimensionType = dimensionType; -+ // CraftBukkit end - this.levelId = levelId; - this.levelDirectory = new LevelStorageSource.LevelDirectory(levelDir); - this.lock = DirectoryLock.create(levelDir); -@@ -449,7 +_,7 @@ - } - - public Path getDimensionPath(ResourceKey dimensionPath) { -- return DimensionType.getStorageFolder(dimensionPath, this.levelDirectory.path()); -+ return LevelStorageSource.getStorageFolder(this.levelDirectory.path(), this.dimensionType); // CraftBukkit - } - - private void checkLock() { + public static DataResult readExistingSavedData( +- final LevelStorageSource.LevelStorageAccess access, final HolderLookup.Provider registryAccess, final SavedDataType savedDataType ++ final LevelStorageSource.LevelStorageAccess access, final ResourceKey dimension, final HolderLookup.Provider registryAccess, final SavedDataType savedDataType // Paper - pass dimension + ) { +- Path dataLocation = savedDataType.id().withSuffix(".dat").resolveAgainst(access.getLevelPath(LevelResource.DATA)); ++ Path dataLocation = savedDataType.id().withSuffix(".dat").resolveAgainst(access.getDimensionPath(dimension).resolve(LevelResource.DATA.id())); // Paper - use dimension scoped data (we 'demote' some data from global and add our own dimension scoped data) + + CompoundTag fileContents; + try { +@@ -396,7 +_,7 @@ + + public LevelStorageSource.LevelStorageAccess validateAndCreateAccess(final String levelId) throws IOException, ContentValidationException { + Path levelPath = this.getLevelPath(levelId); +- List validationResults = this.worldDirValidator.validateDirectory(levelPath, true); ++ List validationResults = Boolean.getBoolean("paper.disableWorldSymlinkValidation") ? List.of() : this.worldDirValidator.validateDirectory(levelPath, true); // Paper - add skipping of symlinks scan + if (!validationResults.isEmpty()) { + throw new ContentValidationException(levelPath, validationResults); + } else { diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/PlayerDataStorage.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/PlayerDataStorage.java.patch index ec0dfb0392aa..3403b87ebef5 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/PlayerDataStorage.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/PlayerDataStorage.java.patch @@ -3,14 +3,14 @@ @@ -31,6 +_,7 @@ } - public void save(Player player) { + public void save(final Player player) { + if (org.spigotmc.SpigotConfig.disablePlayerDataSaving) return; // Spigot - try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), LOGGER)) { - TagValueOutput tagValueOutput = TagValueOutput.createWithContext(scopedCollector, player.registryAccess()); - player.saveWithoutId(tagValueOutput); + try (ProblemReporter.ScopedCollector reporter = new ProblemReporter.ScopedCollector(player.problemPath(), LOGGER)) { + TagValueOutput output = TagValueOutput.createWithContext(reporter, player.registryAccess()); + player.saveWithoutId(output); @@ -42,7 +_,7 @@ - Path path3 = path.resolve(player.getStringUUID() + ".dat_old"); - Util.safeReplaceFile(path2, path1, path3); + Path oldFile = playerDirPath.resolve(player.getStringUUID() + ".dat_old"); + Util.safeReplaceFile(realFile, tmpFile, oldFile); } catch (Exception var11) { - LOGGER.warn("Failed to save player data for {}", player.getPlainTextName()); + LOGGER.warn("Failed to save player data for {}", player.getPlainTextName(), var11); // Paper - Print exception @@ -19,25 +19,25 @@ @@ -62,9 +_,25 @@ - private Optional load(NameAndId nameAndId, String suffix) { - File file = new File(this.playerDir, nameAndId.id() + suffix); + private Optional load(final NameAndId nameAndId, final String suffix) { + File realFile = new File(this.playerDir, nameAndId.id() + suffix); + // Spigot start + boolean usingWrongFile = false; -+ if (org.bukkit.Bukkit.getOnlineMode() && !file.exists()) { // Paper - Check online mode first -+ file = new File(this.playerDir, net.minecraft.core.UUIDUtil.createOfflinePlayerUUID(nameAndId.name()) + suffix); -+ if (file.exists()) { ++ if (org.bukkit.Bukkit.getOnlineMode() && !realFile.exists()) { // Paper - Check online mode first ++ realFile = new File(this.playerDir, net.minecraft.core.UUIDUtil.createOfflinePlayerUUID(nameAndId.name()) + suffix); ++ if (realFile.exists()) { + usingWrongFile = true; + LOGGER.warn("Using offline mode UUID file for player {} as it is the only copy we can find.", nameAndId.name()); + } + } + // Spigot end - if (file.exists() && file.isFile()) { + if (realFile.exists() && realFile.isFile()) { try { -- return Optional.of(NbtIo.readCompressed(file.toPath(), NbtAccounter.unlimitedHeap())); +- return Optional.of(NbtIo.readCompressed(realFile.toPath(), NbtAccounter.unlimitedHeap())); + // Spigot start -+ Optional optional = Optional.of(NbtIo.readCompressed(file.toPath(), NbtAccounter.unlimitedHeap())); ++ Optional optional = Optional.of(NbtIo.readCompressed(realFile.toPath(), NbtAccounter.unlimitedHeap())); + if (usingWrongFile) { -+ file.renameTo(new File(file.getPath() + ".offline-read")); ++ realFile.renameTo(new File(realFile.getPath() + ".offline-read")); + } + return optional; + // Spigot end @@ -45,7 +45,7 @@ LOGGER.warn("Failed to load player data for {}", nameAndId.name()); } @@ -84,4 +_,10 @@ - return DataFixTypes.PLAYER.updateToCurrentVersion(this.fixerUpper, compoundTag, dataVersion); + return DataFixTypes.PLAYER.updateToCurrentVersion(this.fixerUpper, tag, version); }); } + diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/PrimaryLevelData.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/PrimaryLevelData.java.patch index 75ca0c09e94b..7d5e36ace649 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/PrimaryLevelData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/PrimaryLevelData.java.patch @@ -1,51 +1,29 @@ --- a/net/minecraft/world/level/storage/PrimaryLevelData.java +++ b/net/minecraft/world/level/storage/PrimaryLevelData.java -@@ -48,6 +_,8 @@ +@@ -35,6 +_,8 @@ private final PrimaryLevelData.SpecialWorldProperty specialWorldProperty; private final Lifecycle worldGenSettingsLifecycle; private LevelData.RespawnData respawnData; -+ private static final String PAPER_RESPAWN_DIMENSION = "paperSpawnDimension"; // Paper ++ public static final String PAPER_RESPAWN_DIMENSION = "paperSpawnDimension"; // Paper + public net.minecraft.resources.ResourceKey respawnDimension = net.minecraft.world.level.Level.OVERWORLD; // Paper private long gameTime; - private long dayTime; - private final @Nullable CompoundTag loadedPlayerTag; -@@ -71,6 +_,21 @@ - private final Set removedFeatureFlags; - private final TimerQueue scheduledEvents; - -+ // CraftBukkit start - Add world and pdc -+ public net.minecraft.core.Registry customDimensions; -+ private net.minecraft.server.level.ServerLevel world; -+ protected net.minecraft.nbt.Tag pdc; -+ -+ public void setWorld(net.minecraft.server.level.ServerLevel world) { -+ if (this.world != null) { -+ return; -+ } -+ this.world = world; -+ world.getWorld().readBukkitValues(this.pdc); -+ this.pdc = null; -+ } -+ // CraftBukkit end -+ - private PrimaryLevelData( - @Nullable CompoundTag loadedPlayerTag, - boolean wasModded, -@@ -168,7 +_,7 @@ - Lifecycle worldGenSettingsLifecycle + private final @Nullable UUID singlePlayerUUID; + private final int version; +@@ -93,7 +_,7 @@ ) { - long _long = tag.get("Time").asLong(0L); + long gameTime = input.get("Time").asLong(0L); + LevelVersion levelVersion = LevelVersion.parse(input); - return new PrimaryLevelData( + PrimaryLevelData data = new PrimaryLevelData( // Paper - tag.get("Player").flatMap(CompoundTag.CODEC::parse).result().orElse(null), - tag.get("WasModded").asBoolean(false), - tag.get("spawn").read(LevelData.RespawnData.CODEC).result().orElse(LevelData.RespawnData.DEFAULT), -@@ -202,6 +_,13 @@ + input.get("singleplayer_uuid").flatMap(UUIDUtil.CODEC::parse).result().orElse(null), + input.get("WasModded").asBoolean(false), + input.get("spawn").read(LevelData.RespawnData.CODEC).result().orElse(LevelData.RespawnData.DEFAULT), +@@ -106,6 +_,13 @@ specialWorldProperty, worldGenSettingsLifecycle ); + // Paper start -+ data.respawnDimension = tag.get(PAPER_RESPAWN_DIMENSION) ++ data.respawnDimension = input.get(PAPER_RESPAWN_DIMENSION) + .read(net.minecraft.world.level.Level.RESOURCE_KEY_CODEC) + .result() + .orElse(data.respawnData.dimension()); @@ -54,104 +32,44 @@ } @Override -@@ -230,11 +_,12 @@ - tag.put("Version", compoundTag); +@@ -128,20 +_,23 @@ + + writeVersionTag(tag); NbtUtils.addCurrentDataVersion(tag); - DynamicOps dynamicOps = registry.createSerializationContext(NbtOps.INSTANCE); -- WorldGenSettings.encode(dynamicOps, this.worldOptions, registry) -+ WorldGenSettings.encode(dynamicOps, this.worldOptions, new net.minecraft.world.level.levelgen.WorldDimensions(this.customDimensions != null ? this.customDimensions : registry.lookupOrThrow(net.minecraft.core.registries.Registries.LEVEL_STEM))) // CraftBukkit - .resultOrPartial(Util.prefix("WorldGenSettings: ", LOGGER::error)) - .ifPresent(worldOptionsTag -> tag.put("WorldGenSettings", worldOptionsTag)); - tag.putInt("GameType", this.settings.gameType().getId()); - tag.store("spawn", LevelData.RespawnData.CODEC, this.respawnData); +- tag.putInt("GameType", this.settings.gameType().getId()); +- tag.store("spawn", LevelData.RespawnData.CODEC, this.respawnData); +- tag.putLong("Time", this.gameTime); ++ tag.putInt("GameType", this.settings.gameType().getId()); // Paper - diff on change - PaperLevelOverrides.createFromRawLevelData ++ // TODO - snapshot - Missing WorldGenSettings.encode diff ++ tag.store("spawn", LevelData.RespawnData.CODEC, this.respawnData); // Paper - diff on change - PaperLevelOverrides.createFromRawLevelData + tag.store(PAPER_RESPAWN_DIMENSION, net.minecraft.world.level.Level.RESOURCE_KEY_CODEC, this.respawnDimension); // Paper - tag.putLong("Time", this.gameTime); - tag.putLong("DayTime", this.dayTime); - tag.putLong("LastPlayed", Util.getEpochMillis()); -@@ -266,6 +_,8 @@ - tag.putInt("WanderingTraderSpawnDelay", this.wanderingTraderSpawnDelay); - tag.putInt("WanderingTraderSpawnChance", this.wanderingTraderSpawnChance); - tag.storeNullable("WanderingTraderId", UUIDUtil.CODEC, this.wanderingTraderId); -+ tag.putString("Bukkit.Version", org.bukkit.Bukkit.getName() + "/" + org.bukkit.Bukkit.getVersion() + "/" + org.bukkit.Bukkit.getBukkitVersion()); // CraftBukkit -+ this.world.getWorld().storeBukkitValues(tag); // CraftBukkit - add pdc - } - - private static ListTag stringCollectionToTag(Set stringCollection) { -@@ -336,6 +_,25 @@ - - @Override - public void setThundering(boolean thundering) { -+ // Paper start - Add cause to Weather/ThunderChangeEvents -+ this.setThundering(thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.UNKNOWN); -+ } -+ public void setThundering(boolean thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause cause) { -+ // Paper end - Add cause to Weather/ThunderChangeEvents -+ // CraftBukkit start -+ if (this.thundering == thundering) { -+ return; -+ } -+ -+ org.bukkit.World world = org.bukkit.Bukkit.getWorld(this.getLevelName()); -+ if (world != null) { -+ org.bukkit.event.weather.ThunderChangeEvent thunder = new org.bukkit.event.weather.ThunderChangeEvent(world, thundering, cause); // Paper - Add cause to Weather/ThunderChangeEvents -+ org.bukkit.Bukkit.getServer().getPluginManager().callEvent(thunder); -+ if (thunder.isCancelled()) { -+ return; -+ } -+ } -+ // CraftBukkit end - this.thundering = thundering; - } ++ tag.putLong("Time", this.gameTime); // Paper - diff on change - PaperLevelOverrides.createFromRawLevelData + writeLastPlayed(tag); + tag.putString("LevelName", this.settings.levelName()); + tag.putInt("version", 19133); + tag.putBoolean("allowCommands", this.settings.allowCommands()); +- tag.putBoolean("initialized", this.initialized); +- tag.store("difficulty_settings", LevelSettings.DifficultySettings.CODEC, this.settings.difficultySettings()); ++ tag.putBoolean("initialized", this.initialized); // Paper - diff on change - PaperLevelOverrides.createFromRawLevelData ++ tag.store("difficulty_settings", LevelSettings.DifficultySettings.CODEC, this.settings.difficultySettings()); // Paper - diff on change - PaperLevelOverrides.createFromRawLevelData + if (singlePlayerUUID != null) { + tag.storeNullable("singleplayer_uuid", UUIDUtil.CODEC, singlePlayerUUID); + } -@@ -356,6 +_,26 @@ - - @Override - public void setRaining(boolean isRaining) { -+ // Paper start - Add cause to Weather/ThunderChangeEvents -+ this.setRaining(isRaining, org.bukkit.event.weather.WeatherChangeEvent.Cause.UNKNOWN); -+ } -+ -+ public void setRaining(boolean isRaining, org.bukkit.event.weather.WeatherChangeEvent.Cause cause) { -+ // Paper end - Add cause to Weather/ThunderChangeEvents -+ // CraftBukkit start -+ if (this.raining == isRaining) { -+ return; -+ } -+ -+ org.bukkit.World world = org.bukkit.Bukkit.getWorld(this.getLevelName()); -+ if (world != null) { -+ org.bukkit.event.weather.WeatherChangeEvent weather = new org.bukkit.event.weather.WeatherChangeEvent(world, isRaining, cause); // Paper - Add cause to Weather/ThunderChangeEvents -+ org.bukkit.Bukkit.getServer().getPluginManager().callEvent(weather); -+ if (weather.isCancelled()) { -+ return; -+ } -+ } -+ // CraftBukkit end - this.raining = isRaining; - } - -@@ -422,6 +_,12 @@ - @Override - public void setDifficulty(Difficulty difficulty) { - this.settings = this.settings.withDifficulty(difficulty); -+ // CraftBukkit start -+ net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket packet = new net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket(this.getDifficulty(), this.isDifficultyLocked()); -+ for (net.minecraft.server.level.ServerPlayer player : (java.util.List) (java.util.List) this.world.players()) { -+ player.connection.send(packet); -+ } -+ // CraftBukkit end + tag.store(WorldDataConfiguration.MAP_CODEC, this.settings.dataConfiguration()); ++ tag.putString("Bukkit.Version", org.bukkit.Bukkit.getName() + "/" + org.bukkit.Bukkit.getVersion() + "/" + org.bukkit.Bukkit.getBukkitVersion()); // CraftBukkit } - @Override -@@ -555,6 +_,14 @@ + public static void writeLastPlayed(final CompoundTag tag) { +@@ -322,6 +_,14 @@ public LevelSettings getLevelSettings() { return this.settings.copy(); } + + // CraftBukkit start - Check if the name stored in NBT is the correct one + public void checkName(String name) { -+ if (!this.settings.levelName.equals(name)) { -+ this.settings.levelName = name; ++ if (!this.settings.levelName().equals(name)) { ++ this.settings = this.settings.withLevelName(name); + } + } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/SavedDataStorage.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/SavedDataStorage.java.patch new file mode 100644 index 000000000000..ff0ee4e714cc --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/SavedDataStorage.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/level/storage/SavedDataStorage.java ++++ b/net/minecraft/world/level/storage/SavedDataStorage.java +@@ -156,7 +_,7 @@ + } else { + int threads = Util.maxAllowedExecutorThreads(); + int taskCount = tagsToSave.size(); +- if (taskCount > threads) { ++ if (false && taskCount > threads) { // Paper - Separate dimension data IO pool; just throw them into the fixed pool queue + this.pendingWriteFuture = this.pendingWriteFuture.thenCompose(ignored -> { + List> tasks = new ArrayList<>(threads); + int bucketSize = Mth.positiveCeilDiv(taskCount, threads); +@@ -177,7 +_,7 @@ + ignored -> CompletableFuture.allOf( + tagsToSave.entrySet() + .stream() +- .map(entry -> CompletableFuture.runAsync(() -> this.tryWrite(entry.getKey(), entry.getValue()), Util.ioPool())) ++ .map(entry -> CompletableFuture.runAsync(() -> this.tryWrite(entry.getKey(), entry.getValue()), Util.DIMENSION_DATA_IO_POOL)) // Paper - Separate dimension data IO pool + .toArray(CompletableFuture[]::new) + ) + ); diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/TagValueInput.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/TagValueInput.java.patch index b53963635f1d..8e21c6bc5380 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/TagValueInput.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/TagValueInput.java.patch @@ -13,6 +13,6 @@ + } + // Paper end - utility methods + - public static ValueInput create(ProblemReporter problemReporter, HolderLookup.Provider lookup, CompoundTag input) { - return new TagValueInput(problemReporter, new ValueInputContextHelper(lookup, NbtOps.INSTANCE), input); + public static ValueInput create(final ProblemReporter problemReporter, final HolderLookup.Provider holders, final CompoundTag tag) { + return new TagValueInput(problemReporter, new ValueInputContextHelper(holders, NbtOps.INSTANCE), tag); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/TagValueOutput.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/TagValueOutput.java.patch index ec5028cd5812..08b6e862af3a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/TagValueOutput.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/TagValueOutput.java.patch @@ -21,6 +21,6 @@ + } + // Paper end - utility methods + - public static TagValueOutput createWithContext(ProblemReporter problemReporter, HolderLookup.Provider lookup) { - return new TagValueOutput(problemReporter, lookup.createSerializationContext(NbtOps.INSTANCE), new CompoundTag()); + public static TagValueOutput createWithContext(final ProblemReporter problemReporter, final HolderLookup.Provider provider) { + return new TagValueOutput(problemReporter, provider.createSerializationContext(NbtOps.INSTANCE), new CompoundTag()); } diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/loot/LootDataType.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/loot/LootDataType.java.patch index 0b8df8cc19cd..7006279d9f7b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/loot/LootDataType.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/loot/LootDataType.java.patch @@ -1,20 +1,14 @@ --- a/net/minecraft/world/level/storage/loot/LootDataType.java +++ b/net/minecraft/world/level/storage/loot/LootDataType.java -@@ -32,9 +_,14 @@ - } - - private static LootDataType.Validator createLootTableValidator() { -- return (context, key, value) -> value.validate( -- context.setContextKeySet(value.getParamSet()).enterElement(new ProblemReporter.RootElementPathElement(key), key) -- ); +@@ -26,6 +_,11 @@ + ContextKeySet contextKeys = this.contextGetter.context(value); + ValidationContext rootContext = contextSource.context(contextKeys).enterElement(new ProblemReporter.RootElementPathElement(key), key); + value.validate(rootContext); + // CraftBukkit start -+ return (context, key, value) -> { -+ value.validate( -+ context.setContextKeySet(value.getParamSet()).enterElement(new ProblemReporter.RootElementPathElement(key), key) -+ ); -+ value.craftLootTable = new org.bukkit.craftbukkit.CraftLootTable(org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(key.identifier()), value); -+ // CraftBukkit end -+ }; ++ if (value instanceof LootTable lootTable) { ++ lootTable.craftLootTable = new org.bukkit.craftbukkit.CraftLootTable(org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(key.identifier()), lootTable); ++ } ++ // CraftBukkit end } - @FunctionalInterface + public void runValidation(final ValidationContextSource contextSource, final HolderLookup lookup) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/loot/LootTable.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/loot/LootTable.java.patch index 3e2647744fbb..d8cc204c17b9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/loot/LootTable.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/loot/LootTable.java.patch @@ -1,46 +1,46 @@ --- a/net/minecraft/world/level/storage/loot/LootTable.java +++ b/net/minecraft/world/level/storage/loot/LootTable.java -@@ -53,6 +_,7 @@ +@@ -52,6 +_,7 @@ private final List pools; private final List functions; private final BiFunction compositeFunction; + public org.bukkit.craftbukkit.CraftLootTable craftLootTable; // CraftBukkit - LootTable(ContextKeySet paramSet, Optional randomSequence, List pools, List functions) { - this.paramSet = paramSet; -@@ -63,9 +_,10 @@ + private LootTable( + final ContextKeySet paramSet, final Optional randomSequence, final List pools, final List functions +@@ -64,9 +_,10 @@ } - public static Consumer createStackSplitter(ServerLevel level, Consumer output) { + public static Consumer createStackSplitter(final ServerLevel level, final Consumer output) { + boolean skipSplitter = level != null && !level.paperConfig().fixes.splitOverstackedLoot; // Paper - preserve overstacked items - return itemStack -> { - if (itemStack.isItemEnabled(level.enabledFeatures())) { -- if (itemStack.getCount() < itemStack.getMaxStackSize()) { -+ if (skipSplitter || itemStack.getCount() < itemStack.getMaxStackSize()) { // Paper - preserve overstacked items - output.accept(itemStack); + return result -> { + if (result.isItemEnabled(level.enabledFeatures())) { +- if (result.getCount() < result.getMaxStackSize()) { ++ if (skipSplitter || result.getCount() < result.getMaxStackSize()) { // Paper - preserve overstacked items + output.accept(result); } else { - int count = itemStack.getCount(); -@@ -146,9 +_,22 @@ + int count = result.getCount(); +@@ -144,9 +_,22 @@ } - public void fill(Container container, LootParams params, long seed) { -- LootContext lootContext = new LootContext.Builder(params).withOptionalRandomSeed(seed).create(this.randomSequence); + public void fill(final Container container, final LootParams params, final long optionalRandomSeed) { +- LootContext context = new LootContext.Builder(params).withOptionalRandomSeed(optionalRandomSeed).create(this.randomSequence); + // CraftBukkit start -+ this.fill(container, params, seed == 0L ? null : RandomSource.create(seed), false); ++ this.fill(container, params, optionalRandomSeed == 0L ? null : RandomSource.create(optionalRandomSeed), false); + } + + public void fill(Container container, LootParams params, RandomSource randomSource, boolean plugin) { + // CraftBukkit end -+ LootContext lootContext = new LootContext.Builder(params).withOptionalRandomSource(randomSource).create(this.randomSequence); - ObjectArrayList randomItems = this.getRandomItems(lootContext); - RandomSource random = lootContext.getRandom(); ++ LootContext context = new LootContext.Builder(params).withOptionalRandomSource(randomSource).create(this.randomSequence); + ObjectArrayList itemStacks = this.getRandomItems(context); + RandomSource random = context.getRandom(); + // CraftBukkit start -+ org.bukkit.event.world.LootGenerateEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callLootGenerateEvent(container, this, lootContext, randomItems, plugin); ++ org.bukkit.event.world.LootGenerateEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callLootGenerateEvent(container, this, context, itemStacks, plugin); + if (event.isCancelled()) { + return; + } -+ randomItems = event.getLoot().stream().map(org.bukkit.craftbukkit.inventory.CraftItemStack::asNMSCopy).collect(ObjectArrayList.toList()); ++ itemStacks = event.getLoot().stream().map(org.bukkit.craftbukkit.inventory.CraftItemStack::asNMSCopy).collect(ObjectArrayList.toList()); + // CraftBukkit end List availableSlots = this.getAvailableSlots(container, random); - this.shuffleAndSplitItems(randomItems, availableSlots.size(), random); + this.shuffleAndSplitItems(itemStacks, availableSlots.size(), random); diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/loot/entries/LootPoolSingletonContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/loot/entries/LootPoolSingletonContainer.java.patch index 03a917ef41ea..6124d48cb417 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/loot/entries/LootPoolSingletonContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/loot/entries/LootPoolSingletonContainer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/storage/loot/entries/LootPoolSingletonContainer.java +++ b/net/minecraft/world/level/storage/loot/entries/LootPoolSingletonContainer.java -@@ -33,6 +_,10 @@ +@@ -39,6 +_,10 @@ ); } }; @@ -9,12 +9,12 @@ + private int lastWeight; + // Paper end - Configurable LootPool luck formula - protected LootPoolSingletonContainer(int weight, int quality, List conditions, List functions) { + protected LootPoolSingletonContainer(final int weight, final int quality, final List conditions, final List functions) { super(conditions); -@@ -127,7 +_,31 @@ - protected abstract class EntryBase implements LootPoolEntry { +@@ -135,7 +_,31 @@ + @Override - public int getWeight(float luck) { + public int getWeight(final float luck) { - return Math.max(Mth.floor(LootPoolSingletonContainer.this.weight + LootPoolSingletonContainer.this.quality * luck), 0); + // Paper start - Configurable LootPool luck formula + // SEE: https://luckformula.emc.gs for details and data diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/loot/functions/ExplorationMapFunction.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/loot/functions/ExplorationMapFunction.java.patch index 8aa494bf04f6..8862d67128e3 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/loot/functions/ExplorationMapFunction.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/loot/functions/ExplorationMapFunction.java.patch @@ -1,20 +1,30 @@ --- a/net/minecraft/world/level/storage/loot/functions/ExplorationMapFunction.java +++ b/net/minecraft/world/level/storage/loot/functions/ExplorationMapFunction.java -@@ -87,7 +_,16 @@ - Vec3 vec3 = context.getOptionalParameter(LootContextParams.ORIGIN); - if (vec3 != null) { +@@ -83,9 +_,25 @@ + Vec3 lootPos = context.getOptionalParameter(LootContextParams.ORIGIN); + if (lootPos != null) { ServerLevel level = context.getLevel(); -- BlockPos blockPos = level.findNearestMapStructure(this.destination, BlockPos.containing(vec3), this.searchRadius, this.skipKnownStructures); + // Paper start - Configurable cartographer treasure maps ++ // Simple heuristic for determining if this function is running in the context of a villager selecting ++ // an item for its offers. Technically other callers could satisfiy this but this minimises the diff and ++ // works for all plain vanilla usecases. ++ final boolean runningForVillagerTrade = context.hasParameter(LootContextParams.ADDITIONAL_COST_COMPONENT_ALLOWED) ++ && context.getOptionalParameter(LootContextParams.THIS_ENTITY) instanceof net.minecraft.world.entity.npc.villager.AbstractVillager; + if (!level.paperConfig().environment.treasureMaps.enabled) { + /* + * NOTE: I fear users will just get a plain map as their "treasure" + * This is preferable to disrespecting the config. + */ -+ return stack; ++ return runningForVillagerTrade ? net.minecraft.world.item.ItemStack.EMPTY : itemStack; + } ++ final boolean shouldSkipKnownStructures = runningForVillagerTrade ++ ? !level.paperConfig().environment.treasureMaps.findAlreadyDiscoveredVillager ++ : !level.paperConfig().environment.treasureMaps.findAlreadyDiscoveredLootTable.or(!this.skipKnownStructures); + // Paper end - Configurable cartographer treasure maps -+ BlockPos blockPos = level.findNearestMapStructure(this.destination, BlockPos.containing(vec3), this.searchRadius, !level.paperConfig().environment.treasureMaps.findAlreadyDiscoveredLootTable.or(!this.skipKnownStructures)); // Paper - Configurable cartographer treasure maps - if (blockPos != null) { - ItemStack itemStack = MapItem.create(level, blockPos.getX(), blockPos.getZ(), this.zoom, true, true); - MapItem.renderBiomePreviewMap(level, itemStack); + BlockPos nearestMapStructure = level.findNearestMapStructure( +- this.destination, BlockPos.containing(lootPos), this.searchRadius, this.skipKnownStructures +- ); ++ this.destination, BlockPos.containing(lootPos), this.searchRadius, shouldSkipKnownStructures); // Paper - Configurable cartographer treasure maps + if (nearestMapStructure != null) { + ItemStack map = MapItem.create(level, nearestMapStructure.getX(), nearestMapStructure.getZ(), this.zoom, true, true); + MapItem.renderBiomePreviewMap(level, map); diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/loot/predicates/ExplosionCondition.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/loot/predicates/ExplosionCondition.java.patch index 53cc48355e6b..44f3c15cdc3a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/loot/predicates/ExplosionCondition.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/loot/predicates/ExplosionCondition.java.patch @@ -1,12 +1,12 @@ --- a/net/minecraft/world/level/storage/loot/predicates/ExplosionCondition.java +++ b/net/minecraft/world/level/storage/loot/predicates/ExplosionCondition.java @@ -30,7 +_,8 @@ - if (_float != null) { + if (explosionRadius != null) { RandomSource random = context.getRandom(); - float f = 1.0F / _float; -- return random.nextFloat() <= f; + float probability = 1.0F / explosionRadius; +- return random.nextFloat() <= probability; + // CraftBukkit - <= to < to allow for plugins to completely disable block drops from explosions -+ return random.nextFloat() < f; ++ return random.nextFloat() < probability; } else { return true; } diff --git a/paper-server/patches/sources/net/minecraft/world/scores/PlayerTeam.java.patch b/paper-server/patches/sources/net/minecraft/world/scores/PlayerTeam.java.patch index e2ba03babf12..dc24bbf1dad6 100644 --- a/paper-server/patches/sources/net/minecraft/world/scores/PlayerTeam.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/scores/PlayerTeam.java.patch @@ -1,7 +1,7 @@ --- a/net/minecraft/world/scores/PlayerTeam.java +++ b/net/minecraft/world/scores/PlayerTeam.java @@ -222,7 +_,7 @@ - instance -> instance.group( + i -> i.group( Codec.STRING.fieldOf("Name").forGetter(PlayerTeam.Packed::name), ComponentSerialization.CODEC.optionalFieldOf("DisplayName").forGetter(PlayerTeam.Packed::displayName), - ChatFormatting.COLOR_CODEC.optionalFieldOf("TeamColor").forGetter(PlayerTeam.Packed::color), diff --git a/paper-server/patches/sources/net/minecraft/world/scores/Scoreboard.java.patch b/paper-server/patches/sources/net/minecraft/world/scores/Scoreboard.java.patch index 24a2d294325d..e6118569b330 100644 --- a/paper-server/patches/sources/net/minecraft/world/scores/Scoreboard.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/scores/Scoreboard.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/scores/Scoreboard.java +++ b/net/minecraft/world/scores/Scoreboard.java -@@ -375,7 +_,7 @@ +@@ -377,7 +_,7 @@ } protected List packPlayerTeams() { @@ -8,4 +8,4 @@ + return this.getPlayerTeams().stream().filter(p -> io.papermc.paper.configuration.GlobalConfiguration.get().scoreboards.saveEmptyScoreboardTeams || !p.getPlayers().isEmpty()).map(PlayerTeam::pack).toList(); // Paper - Don't save empty scoreboard teams to scoreboard.dat } - protected void loadPlayerTeam(PlayerTeam.Packed packed) { + protected void loadPlayerTeam(final PlayerTeam.Packed packed) { diff --git a/paper-server/patches/sources/net/minecraft/world/waypoints/WaypointTransmitter.java.patch b/paper-server/patches/sources/net/minecraft/world/waypoints/WaypointTransmitter.java.patch index f276035408d0..a8da863bf0c6 100644 --- a/paper-server/patches/sources/net/minecraft/world/waypoints/WaypointTransmitter.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/waypoints/WaypointTransmitter.java.patch @@ -3,8 +3,8 @@ @@ -20,6 +_,7 @@ Waypoint.Icon waypointIcon(); - static boolean doesSourceIgnoreReceiver(LivingEntity entity, ServerPlayer player) { -+ if (!player.getBukkitEntity().canSee(entity.getBukkitEntity())) return true; // Paper - ignore if entity is hidden from player - if (player.isSpectator()) { + static boolean doesSourceIgnoreReceiver(final LivingEntity source, final ServerPlayer receiver) { ++ if (!receiver.getBukkitEntity().canSee(source.getBukkitEntity())) return true; // Paper - ignore if entity is hidden from player + if (receiver.isSpectator()) { return false; - } else if (!entity.isSpectator() && !entity.hasIndirectPassenger(player)) { + } else if (!source.isSpectator() && !source.hasIndirectPassenger(receiver)) { diff --git a/paper-server/patches/sources/net/neoforged/art/internal/RenamerImpl.java.patch b/paper-server/patches/sources/net/neoforged/art/internal/RenamerImpl.java.patch deleted file mode 100644 index 69b4834654c1..000000000000 --- a/paper-server/patches/sources/net/neoforged/art/internal/RenamerImpl.java.patch +++ /dev/null @@ -1,67 +0,0 @@ ---- a/net/neoforged/art/internal/RenamerImpl.java -+++ b/net/neoforged/art/internal/RenamerImpl.java -@@ -35,7 +_,7 @@ - import net.neoforged.cliutils.progress.ProgressReporter; - import org.objectweb.asm.Opcodes; - --class RenamerImpl implements Renamer { -+public class RenamerImpl implements Renamer { // Paper - public - private static final ProgressReporter PROGRESS = ProgressReporter.getDefault(); - static final int MAX_ASM_VERSION = Opcodes.ASM9; - private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; -@@ -75,6 +_,11 @@ - - @Override - public void run(File input, File output) { -+ // Paper start - Add remappingSelf -+ this.run(input, output, false); -+ } -+ public void run(File input, File output, boolean remappingSelf) { -+ // Paper end - if (!this.setup) - this.setup(); - -@@ -105,10 +_,10 @@ - String name = e.getName(); - byte[] data; - try (InputStream entryInput = in.getInputStream(e)) { -- data = readAllBytes(entryInput, e.getSize()); -+ data = entryInput.readAllBytes(); // Paper - Use readAllBytes - } - -- if (name.endsWith(".class")) -+ if (name.endsWith(".class") && !name.contains("META-INF/")) // Paper - Skip META-INF entries - oldEntries.add(ClassEntry.create(name, e.getTime(), data)); - else if (name.equals(MANIFEST_NAME)) - oldEntries.add(ManifestEntry.create(e.getTime(), data)); -@@ -163,15 +_,29 @@ - List newEntries = async.invokeAll(oldEntries, Entry::getName, this::processEntry); - - logger.accept("Adding extras"); -- transformers.forEach(t -> newEntries.addAll(t.getExtras())); -+ // Paper start - I'm pretty sure the duplicates are because the input is already on the classpath -+ List finalNewEntries = newEntries; -+ transformers.forEach(t -> finalNewEntries.addAll(t.getExtras())); - - Set seen = new HashSet<>(); -+ if (remappingSelf) { -+ // deduplicate -+ List n = new ArrayList<>(); -+ for (final Entry e : newEntries) { -+ if (seen.add(e.getName())) { -+ n.add(e); -+ } -+ } -+ newEntries = n; -+ } else { - String dupes = newEntries.stream().map(Entry::getName) - .filter(n -> !seen.add(n)) - .sorted() - .collect(Collectors.joining(", ")); - if (!dupes.isEmpty()) - throw new IllegalStateException("Duplicate entries detected: " + dupes); -+ } -+ // Paper end - - // We care about stable output, so sort, and single thread write. - logger.accept("Sorting"); diff --git a/paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftFarm.java b/paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftFarmland.java similarity index 74% rename from paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftFarm.java rename to paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftFarmland.java index 77b3279c9550..cc4f5ef54769 100644 --- a/paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftFarm.java +++ b/paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftFarmland.java @@ -1,7 +1,7 @@ package org.bukkit.craftbukkit.block.impl; import io.papermc.paper.annotation.GeneratedClass; -import net.minecraft.world.level.block.FarmBlock; +import net.minecraft.world.level.block.FarmlandBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.IntegerProperty; import org.bukkit.block.data.type.Farmland; @@ -10,10 +10,10 @@ @NullMarked @GeneratedClass -public class CraftFarm extends CraftBlockData implements Farmland { - private static final IntegerProperty MOISTURE = FarmBlock.MOISTURE; +public class CraftFarmland extends CraftBlockData implements Farmland { + private static final IntegerProperty MOISTURE = FarmlandBlock.MOISTURE; - public CraftFarm(BlockState state) { + public CraftFarmland(BlockState state) { super(state); } diff --git a/paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftSnowyDirt.java b/paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftSnowy.java similarity index 71% rename from paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftSnowyDirt.java rename to paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftSnowy.java index de4248db7661..b839b91d69a2 100644 --- a/paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftSnowyDirt.java +++ b/paper-server/src/generated/java/org/bukkit/craftbukkit/block/impl/CraftSnowy.java @@ -1,7 +1,7 @@ package org.bukkit.craftbukkit.block.impl; import io.papermc.paper.annotation.GeneratedClass; -import net.minecraft.world.level.block.SnowyDirtBlock; +import net.minecraft.world.level.block.SnowyBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BooleanProperty; import org.bukkit.block.data.Snowable; @@ -10,10 +10,10 @@ @NullMarked @GeneratedClass -public class CraftSnowyDirt extends CraftBlockData implements Snowable { - private static final BooleanProperty SNOWY = SnowyDirtBlock.SNOWY; +public class CraftSnowy extends CraftBlockData implements Snowable { + private static final BooleanProperty SNOWY = SnowyBlock.SNOWY; - public CraftSnowyDirt(BlockState state) { + public CraftSnowy(BlockState state) { super(state); } diff --git a/paper-server/src/main/java/ca/spottedleaf/moonrise/common/misc/AllocatingRateLimiter.java b/paper-server/src/main/java/ca/spottedleaf/moonrise/common/misc/AllocatingRateLimiter.java index 9c0eff9017b2..9e5a7ad30f62 100644 --- a/paper-server/src/main/java/ca/spottedleaf/moonrise/common/misc/AllocatingRateLimiter.java +++ b/paper-server/src/main/java/ca/spottedleaf/moonrise/common/misc/AllocatingRateLimiter.java @@ -1,75 +1,110 @@ package ca.spottedleaf.moonrise.common.misc; -public final class AllocatingRateLimiter { +final class AllocatingRateLimiter { - // max difference granularity in ns - private final long maxGranularity; + private double rate; + private long intervalNS; + private double maxAllocation; + // the allocation that has been taken + private double takeCarry = 0.0; + // the allocation that has yet to be taken private double allocation = 0.0; private long lastAllocationUpdate; - // the carry is used to store the remainder of the last take, so that the take amount remains the same (minus floating point error) - // over any time period using take regardless of the number of take calls or the intervals between the take calls - // i.e. take obtains 3.5 elements, stores 0.5 to this field for the next take() call to use and returns 3 - private double takeCarry = 0.0; - private long lastTakeUpdate; - public AllocatingRateLimiter(final long maxGranularity) { - this.maxGranularity = maxGranularity; + // maxGranularity in nanoseconds, rate in units/second, maxAllocation in units, allocation in units, time in nanoseconds + public AllocatingRateLimiter(final double rate, final long intervalNS, + final double allocation, final long time) { + this.reset(rate, intervalNS, allocation, time); } - public void reset(final long time) { - this.allocation = 0.0; - this.lastAllocationUpdate = time; + public double getRate() { + return this.rate; + } + + public long getIntervalNS() { + return this.intervalNS; + } + + public double getAllocation() { + return this.allocation; + } + + public long getLastAllocationUpdate() { + return this.lastAllocationUpdate; + } + + public static double getMaxAllocation(final double rate, final long intervalNS) { + return rate * ((double)intervalNS / 1.0E9); + } + + // maxGranularity in nanoseconds, rate in units/second, maxAllocation in units, allocation in units, time in nanoseconds + public void reset(final double rate, final long intervalNS, final double allocation, final long time) { + this.rate = rate; + this.intervalNS = intervalNS; + this.maxAllocation = getMaxAllocation(rate, intervalNS); + this.takeCarry = 0.0; - this.lastTakeUpdate = time; + this.allocation = Math.min(allocation, this.maxAllocation); + this.lastAllocationUpdate = time; } - // rate in units/s, and time in ns - public void tickAllocation(final long time, final double rate, final double maxAllocation) { - final long diff = Math.min(this.maxGranularity, time - this.lastAllocationUpdate); + // time in ns + public void tickAllocation(final long time) { + final long diff = Math.max(0L, time - this.lastAllocationUpdate); this.lastAllocationUpdate = time; - this.allocation = Math.min(maxAllocation - this.takeCarry, this.allocation + rate * (diff*1.0E-9D)); + this.allocation = Math.min(this.maxAllocation, this.allocation + this.rate * ((double)diff / 1.0E9)); } - public long previewAllocation(final long time, final double rate, final long maxTake) { - if (maxTake < 1L) { + public long previewAllocation(final long maxTake) { + if (maxTake < 0L) { return 0L; } - - final long diff = Math.min(this.maxGranularity, time - this.lastTakeUpdate); - - // note: abs(takeCarry) <= 1.0 - final double take = Math.min( - Math.min((double)maxTake - this.takeCarry, this.allocation), - rate * (diff*1.0E-9) + return (long)Math.floor( + Math.min((double)maxTake, this.allocation + this.takeCarry) ); - - return (long)Math.floor(this.takeCarry + take); } - // rate in units/s, and time in ns - public long takeAllocation(final long time, final double rate, final long maxTake) { - if (maxTake < 1L) { + public long takeAllocation(final long maxTake) { + if (maxTake < 0L) { return 0L; } + // start with the amount we have already taken double ret = this.takeCarry; - final long diff = Math.min(this.maxGranularity, time - this.lastTakeUpdate); - this.lastTakeUpdate = time; - - // note: abs(takeCarry) <= 1.0 - final double take = Math.min( - Math.min((double)maxTake - this.takeCarry, this.allocation), - rate * (diff*1.0E-9) - ); - ret += take; - this.allocation -= take; + // attempt to subtract the rest from the current allocation + final double takeFromAllocation = Math.min((double)maxTake - ret, this.allocation); + this.allocation -= takeFromAllocation; + ret += takeFromAllocation; + // leave the fraction we could not take for next call final long retInteger = (long)Math.floor(ret); this.takeCarry = ret - (double)retInteger; return retInteger; } + + public void returnUnused(final long unused) { + if (unused <= 0L) { + return; + } + // note: expect allocation + take carry + value < maxAllocation + 1 + final double newAllocation = Math.min(this.maxAllocation + 1.0, (this.allocation + this.takeCarry) + (double)unused); + this.allocation = Math.min(newAllocation - 1.0, this.maxAllocation); // allocation > 0.0 as unused >= 1 + this.takeCarry = 0.9999999999999; // close enough really... + } + + @Override + public String toString() { + return "AllocatingRateLimiter{" + + "rate=" + this.rate + + ", intervalNS=" + this.intervalNS + + ", maxAllocation=" + this.maxAllocation + + ", takeCarry=" + this.takeCarry + + ", allocation=" + this.allocation + + ", lastAllocationUpdate=" + this.lastAllocationUpdate + + '}'; + } } diff --git a/paper-server/src/main/java/ca/spottedleaf/moonrise/common/misc/StaggeredRateLimiter.java b/paper-server/src/main/java/ca/spottedleaf/moonrise/common/misc/StaggeredRateLimiter.java new file mode 100644 index 000000000000..f879545530b6 --- /dev/null +++ b/paper-server/src/main/java/ca/spottedleaf/moonrise/common/misc/StaggeredRateLimiter.java @@ -0,0 +1,152 @@ +package ca.spottedleaf.moonrise.common.misc; + +import ca.spottedleaf.concurrentutil.util.TimeUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +public final class StaggeredRateLimiter { + + private final Limiter[] limiters; + private double rate; + + private StaggeredRateLimiter(final Limiter[] limiters, final double rate) { + this.limiters = Objects.requireNonNull(limiters); + + if (limiters.length == 0) { + throw new IllegalArgumentException("Must define at least one limiter"); + } + if (rate < 0.0) { + throw new IllegalArgumentException("Initial rate must be >= 0.0"); + } + + for (final Limiter limiter : limiters) { + if (limiter.rateFactor <= 0.0) { + throw new IllegalArgumentException("Rate factor must be > 0.0"); + } + } + + this.reset(rate); + } + + public static StaggeredRateLimiter.Builder builder() { + return new Builder(); + } + + public void tick(final long timeNow) { + this.tick(timeNow, this.rate, 0.0); + } + + public void tick(final long timeNow, final double newRate, final double allocFactor) { + if (newRate != this.rate) { + this.reset(newRate, allocFactor); + } + + for (final Limiter limiter : this.limiters) { + limiter.tick(timeNow); + } + } + + private long previewAllocation(final long maxTake) { + long min = Long.MAX_VALUE; + + for (final Limiter limiter : this.limiters) { + min = Math.min(min, limiter.limiter.previewAllocation(maxTake)); + } + + return min; + } + + public long takeAllocation(final long maxTake) { + final long take = this.previewAllocation(maxTake); + + for (final Limiter limiter : this.limiters) { + // do we need to attempt take + 1 to fill carry? + final long toTake; + if (take == limiter.limiter.previewAllocation(take + 1)) { + // we must attempt to take + 1 to fill carry + toTake = take + 1; + } else { + // no: we are limited by another limiter + // (i.e takeAllocation(take + 1) == take + 1 + toTake = take; + } + + if (take != limiter.limiter.takeAllocation(toTake)) { + throw new IllegalStateException(); + } + } + + return take; + } + + public void returnUnused(final long unused) { + for (final Limiter limiter : this.limiters) { + limiter.limiter.returnUnused(unused); + } + } + + public void reset(final double allocFactor) { + this.reset(this.rate, allocFactor); + } + + public void reset(final double rate, final double allocFactor) { + this.rate = rate; + + for (final Limiter limiter : this.limiters) { + limiter.adjustRate(rate, allocFactor); + } + } + + private static record Limiter(double rateFactor, AllocatingRateLimiter limiter) { + public void tick(final long timeNow) { + this.limiter.tickAllocation(timeNow); + } + + public void adjustRate(final double rate, final double allocFactor) { + final double adjustedRate = this.rateFactor * rate; + + final double allocation = allocFactor * AllocatingRateLimiter.getMaxAllocation(adjustedRate, this.limiter.getIntervalNS()); + + this.limiter.reset( + adjustedRate, this.limiter.getIntervalNS(), + allocation, this.limiter.getLastAllocationUpdate() + ); + } + } + + public static final class Builder { + + private Builder() {} + + private final long timeNow = System.nanoTime(); + private List limiters = new ArrayList<>(); + private double initialRate; + + public Builder setRate(final double rate) { + this.initialRate = rate; + return this; + } + + public Builder add(final long interval, final TimeUnit intervalUnit, + final double rateFactor) { + this.limiters.add( + new Limiter( + rateFactor, + new AllocatingRateLimiter(0.0, intervalUnit.toNanos(interval), 0.0, this.timeNow) + ) + ); + return this; + } + + public StaggeredRateLimiter build() { + // want lower intervals first + this.limiters.sort((final Limiter l1, final Limiter l2) -> { + return TimeUtil.compareTimes(l1.limiter.getIntervalNS(), l2.limiter.getIntervalNS()); + }); + + return new StaggeredRateLimiter(this.limiters.toArray(new Limiter[0]), this.initialRate); + } + } +} diff --git a/paper-server/src/main/java/ca/spottedleaf/moonrise/common/util/CoordinateUtils.java b/paper-server/src/main/java/ca/spottedleaf/moonrise/common/util/CoordinateUtils.java index bb5b9c9cb0c7..47d2f0cd0161 100644 --- a/paper-server/src/main/java/ca/spottedleaf/moonrise/common/util/CoordinateUtils.java +++ b/paper-server/src/main/java/ca/spottedleaf/moonrise/common/util/CoordinateUtils.java @@ -17,11 +17,11 @@ public static long getChunkKey(final BlockPos pos) { public static long getChunkKey(final Entity entity) { final ChunkPos pos = entity.chunkPosition(); - return ((long)pos.z << 32) | (pos.x & 0xFFFFFFFFL); + return ((long)pos.z() << 32) | (pos.x() & 0xFFFFFFFFL); } public static long getChunkKey(final ChunkPos pos) { - return ((long)pos.z << 32) | (pos.x & 0xFFFFFFFFL); + return ((long)pos.z() << 32) | (pos.x() & 0xFFFFFFFFL); } public static long getChunkKey(final SectionPos pos) { @@ -71,9 +71,9 @@ public static long getChunkSectionKey(final SectionPos pos) { } public static long getChunkSectionKey(final ChunkPos pos, final int y) { - return ((pos.x & SECTION_X_MASK) << SECTION_X_SHIFT) + return ((pos.x() & SECTION_X_MASK) << SECTION_X_SHIFT) | ((y & SECTION_Y_MASK) << SECTION_Y_SHIFT) - | ((pos.z & SECTION_Z_MASK) << SECTION_Z_SHIFT); + | ((pos.z() & SECTION_Z_MASK) << SECTION_Z_SHIFT); } public static long getChunkSectionKey(final BlockPos pos) { diff --git a/paper-server/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java b/paper-server/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java index b7c7de9797d3..b9f788693b0c 100644 --- a/paper-server/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java +++ b/paper-server/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java @@ -5,6 +5,7 @@ import ca.spottedleaf.moonrise.common.PlatformHooks; import com.mojang.logging.LogUtils; import org.slf4j.Logger; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -16,21 +17,24 @@ public final class MoonriseCommon { public static final long WORKER_QUEUE_HOLD_TIME = (long)(20.0e6); // 20ms public static final BalancedPrioritisedThreadPool WORKER_POOL = new BalancedPrioritisedThreadPool( WORKER_QUEUE_HOLD_TIME, - new Consumer<>() { - private final AtomicInteger idGenerator = new AtomicInteger(); - - @Override - public void accept(Thread thread) { - thread.setDaemon(true); - thread.setName(PlatformHooks.get().getBrand() + " Common Worker #" + this.idGenerator.getAndIncrement()); - thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { - @Override - public void uncaughtException(final Thread thread, final Throwable throwable) { - LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable); - } - }); - } + new ThreadFactory() { + private final AtomicInteger idGenerator = new AtomicInteger(); + + @Override + public Thread newThread(final Runnable run) { + final Thread thread = new Thread(run, PlatformHooks.get().getBrand() + " Common Worker #" + this.idGenerator.getAndIncrement()); + + thread.setDaemon(true); + thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(final Thread thread, final Throwable throwable) { + LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable); + } + }); + + return thread; } + } ); public static final BalancedPrioritisedThreadPool.OrderedStreamGroup CLIENT_GROUP = MoonriseCommon.WORKER_POOL.createOrderedStreamGroup(); public static final BalancedPrioritisedThreadPool.OrderedStreamGroup SERVER_GROUP = MoonriseCommon.WORKER_POOL.createOrderedStreamGroup(); @@ -61,19 +65,22 @@ public static void adjustWorkerThreads(final int configWorkerThreads, final int public static final long IO_QUEUE_HOLD_TIME = (long)(25.0e6); // 25ms public static final BalancedPrioritisedThreadPool IO_POOL = new BalancedPrioritisedThreadPool( IO_QUEUE_HOLD_TIME, - new Consumer<>() { + new ThreadFactory() { private final AtomicInteger idGenerator = new AtomicInteger(); @Override - public void accept(final Thread thread) { + public Thread newThread(final Runnable run) { + final Thread thread = new Thread(run, PlatformHooks.get().getBrand() + " I/O Worker #" + this.idGenerator.getAndIncrement()); + thread.setDaemon(true); - thread.setName(PlatformHooks.get().getBrand() + " I/O Worker #" + this.idGenerator.getAndIncrement()); thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(final Thread thread, final Throwable throwable) { LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable); } }); + + return thread; } } ); diff --git a/paper-server/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java b/paper-server/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java index c8583cb0bc5a..b3c7107689f0 100644 --- a/paper-server/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java +++ b/paper-server/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java @@ -3,7 +3,6 @@ import com.destroystokyo.paper.entity.RangedEntity; import com.google.common.base.CaseFormat; import io.papermc.paper.entity.SchoolableFish; -import io.papermc.paper.util.ObfHelper; import it.unimi.dsi.fastutil.ints.Int2BooleanFunction; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; @@ -249,9 +248,6 @@ public static GoalKey getKey(Class goalClass) Class type = getGenericType(goalClass); String name = goalClass.getName(); - if (io.papermc.paper.util.MappingEnvironment.reobf()) { - name = ObfHelper.INSTANCE.deobfClassName(name); - } Class holderClass = getTopLevelClass(goalClass); name = getPathName(type, holderClass, name); diff --git a/paper-server/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java b/paper-server/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java index 605a4a83d0a0..252378f766ca 100644 --- a/paper-server/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java +++ b/paper-server/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java @@ -50,7 +50,7 @@ public static void logSyncLoad(final Level world, final int chunkX, final int ch ++valueInMap.times; - valueInMap.coordinateTimes.compute(ChunkPos.asLong(chunkX, chunkZ), (Long keyInMap1, Integer valueInMap1) -> { + valueInMap.coordinateTimes.compute(ChunkPos.pack(chunkX, chunkZ), (Long keyInMap1, Integer valueInMap1) -> { return valueInMap1 == null ? Integer.valueOf(1) : Integer.valueOf(valueInMap1.intValue() + 1); }); @@ -103,8 +103,7 @@ public static JsonObject serialize() { for (Long2IntMap.Entry coordinate : pair.getSecond().coordinateTimes.long2IntEntrySet()) { final long key = coordinate.getLongKey(); final int times = coordinate.getIntValue(); - final ChunkPos chunkPos = new ChunkPos(key); - coordinates.add("(" + chunkPos.x + "," + chunkPos.z + "): " + times); + coordinates.add("(" + ChunkPos.getX(key) + "," + ChunkPos.getZ(key) + "): " + times); } stacktrace.add("coordinates", coordinates); diff --git a/paper-server/src/main/java/com/destroystokyo/paper/network/PaperNetworkClient.java b/paper-server/src/main/java/com/destroystokyo/paper/network/PaperNetworkClient.java index 0d2fcedf15a9..025fe74877c3 100644 --- a/paper-server/src/main/java/com/destroystokyo/paper/network/PaperNetworkClient.java +++ b/paper-server/src/main/java/com/destroystokyo/paper/network/PaperNetworkClient.java @@ -7,26 +7,26 @@ public class PaperNetworkClient implements NetworkClient { - private final Connection networkManager; + private final Connection connection; - PaperNetworkClient(Connection networkManager) { - this.networkManager = networkManager; + PaperNetworkClient(Connection connection) { + this.connection = connection; } @Override public InetSocketAddress getAddress() { - return (InetSocketAddress) this.networkManager.getRemoteAddress(); + return (InetSocketAddress) this.connection.getRemoteAddress(); } @Override public int getProtocolVersion() { - return this.networkManager.protocolVersion; + return this.connection.protocolVersion; } @Nullable @Override public InetSocketAddress getVirtualHost() { - return this.networkManager.virtualHost; + return this.connection.virtualHost; } public static InetSocketAddress prepareVirtualHost(String host, int port) { diff --git a/paper-server/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java b/paper-server/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java index d926ad804355..931211b57134 100644 --- a/paper-server/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java +++ b/paper-server/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java @@ -4,8 +4,8 @@ class PaperStatusClient extends PaperNetworkClient implements StatusClient { - PaperStatusClient(Connection networkManager) { - super(networkManager); + PaperStatusClient(Connection connection) { + super(connection); } } diff --git a/paper-server/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java b/paper-server/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java index b40b79beb311..5e89bad7231d 100644 --- a/paper-server/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java +++ b/paper-server/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java @@ -20,8 +20,8 @@ public final class StandardPaperServerListPingEventImpl extends PaperServerListP private List originalSample; - private StandardPaperServerListPingEventImpl(MinecraftServer server, Connection networkManager, ServerStatus ping) { - super(server, new PaperStatusClient(networkManager), ping.version().map(ServerStatus.Version::protocol).orElse(-1), server.server.getServerIcon()); + private StandardPaperServerListPingEventImpl(MinecraftServer server, Connection connection, ServerStatus ping) { + super(server, new PaperStatusClient(connection), ping.version().map(ServerStatus.Version::protocol).orElse(-1), server.server.getServerIcon()); this.originalSample = ping.players().map(ServerStatus.Players::sample).orElse(null); // GH-1473 - pre-tick race condition NPE } @@ -63,13 +63,13 @@ private List getPlayerSampleHandle() { return profiles; } - public static void processRequest(MinecraftServer server, Connection networkManager) { - StandardPaperServerListPingEventImpl event = new StandardPaperServerListPingEventImpl(server, networkManager, server.getStatus()); + public static void processRequest(MinecraftServer server, Connection connection) { + StandardPaperServerListPingEventImpl event = new StandardPaperServerListPingEventImpl(server, connection, server.getStatus()); server.server.getPluginManager().callEvent(event); // Close connection immediately if event is cancelled if (event.isCancelled()) { - networkManager.disconnect((Component) null); + connection.disconnect((Component) null); return; } @@ -99,7 +99,7 @@ public static void processRequest(MinecraftServer server, Connection networkMana final ServerStatus ping = new ServerStatus(description, players, Optional.of(version), favicon, server.enforceSecureProfile()); // Send response - networkManager.send(new ClientboundStatusResponsePacket(ping)); + connection.send(new ClientboundStatusResponsePacket(ping)); } } diff --git a/paper-server/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java b/paper-server/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java index b4ce727bd292..6d986b9f3b8b 100644 --- a/paper-server/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java +++ b/paper-server/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java @@ -9,6 +9,7 @@ import com.mojang.authlib.properties.Property; import com.mojang.authlib.properties.PropertyMap; import io.papermc.paper.profile.MutablePropertyMap; +import net.minecraft.util.ExtraCodecs; import net.minecraft.util.Util; import net.minecraft.core.UUIDUtil; import net.minecraft.server.MinecraftServer; @@ -77,7 +78,7 @@ public void setProperty(ProfileProperty property) { PropertyMap properties = profile.properties(); properties.removeAll(name); - Preconditions.checkArgument(properties.size() < 16, "Cannot add more than 16 properties to a profile"); + Preconditions.checkArgument(properties.size() < ExtraCodecs.MAX_PROPERTIES, "Cannot add more than %s properties to a profile", ExtraCodecs.MAX_PROPERTIES); properties.put(name, new Property(name, property.getValue(), property.getSignature())); } @@ -406,7 +407,7 @@ public int hashCode() { public String toString() { return "CraftPlayerProfile [uniqueId=" + getId() + ", name=" + getName() + - ", properties=" + org.bukkit.craftbukkit.profile.CraftPlayerProfile.toString(this.profile.properties()) + + ", properties=" + this.profile.properties() + "]"; } diff --git a/paper-server/src/main/java/io/papermc/paper/adventure/AdventureCodecs.java b/paper-server/src/main/java/io/papermc/paper/adventure/AdventureCodecs.java index e6a4d4b958bc..90f6bec5dfef 100644 --- a/paper-server/src/main/java/io/papermc/paper/adventure/AdventureCodecs.java +++ b/paper-server/src/main/java/io/papermc/paper/adventure/AdventureCodecs.java @@ -7,6 +7,7 @@ import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; +import com.mojang.serialization.JavaOps; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import io.papermc.paper.dialog.Dialog; @@ -47,7 +48,8 @@ import net.kyori.adventure.text.object.PlayerHeadObjectContents; import net.kyori.adventure.text.object.SpriteObjectContents; import net.kyori.adventure.util.Index; -import net.minecraft.commands.arguments.selector.SelectorPattern; +import net.minecraft.commands.arguments.selector.EntitySelector; +import net.minecraft.core.Holder; import net.minecraft.core.UUIDUtil; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; @@ -59,10 +61,11 @@ import net.minecraft.network.chat.contents.TranslatableContents; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.util.CompilableString; import net.minecraft.util.ExtraCodecs; import net.minecraft.util.StringRepresentable; import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.component.ResolvableProfile; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -195,13 +198,12 @@ public String getSerializedName() { ).apply(instance, (key, uuid, component) -> HoverEvent.showEntity(key, uuid, component.orElse(null)))); public static final MapCodec> SHOW_ITEM_CODEC = net.minecraft.network.chat.HoverEvent.ShowItem.CODEC.xmap(internal -> { - @Subst("key") final String typeKey = internal.item().getItemHolder().unwrapKey().orElseThrow().identifier().toString(); - return HoverEvent.showItem(Key.key(typeKey), internal.item().getCount(), PaperAdventure.asAdventure(internal.item().getComponentsPatch())); + @Subst("key") final String typeKey = internal.item().typeHolder().unwrapKey().orElseThrow().identifier().toString(); + return HoverEvent.showItem(Key.key(typeKey), internal.item().count(), PaperAdventure.asAdventure(internal.item().components())); }, adventure -> { - final Item itemType = BuiltInRegistries.ITEM.getValue(PaperAdventure.asVanilla(adventure.value().item())); + final Holder itemType = BuiltInRegistries.ITEM.get(PaperAdventure.asVanilla(adventure.value().item())).orElseThrow(); final Map dataComponentsMap = adventure.value().dataComponents(); - final ItemStack stack = new ItemStack(BuiltInRegistries.ITEM.wrapAsHolder(itemType), adventure.value().count(), PaperAdventure.asVanilla(dataComponentsMap)); - return new net.minecraft.network.chat.HoverEvent.ShowItem(stack); + return new net.minecraft.network.chat.HoverEvent.ShowItem(new ItemStackTemplate(itemType, adventure.value().count(), PaperAdventure.asVanilla(dataComponentsMap))); }); static final HoverEventType SHOW_ENTITY_HOVER_EVENT_TYPE = new HoverEventType<>(SHOW_ENTITY_CODEC, "show_entity"); @@ -396,9 +398,9 @@ static Function> nullableGetter(final Function SCORE_COMPONENT_INNER_MAP_CODEC = ScoreContents.INNER_CODEC.xmap( - s -> Component.score(s.name().map(SelectorPattern::pattern, identity()), s.objective()), - s -> new ScoreContents(SelectorPattern.parse(s.name()).>map(Either::left).result().orElse(Either.right(s.name())), s.objective()) - ); // TODO we might want to ask adventure for a nice way we can avoid parsing and flattening the SelectorPattern on every conversion. + s -> Component.score(s.name().map(CompilableString::source, identity()), s.objective()), + s -> new ScoreContents(EntitySelector.COMPILABLE_CODEC.parse(JavaOps.INSTANCE, s.name())., String>>map(Either::left).result().orElse(Either.right(s.name())), s.objective()) + ); // TODO we might want to ask adventure for a nice way we can avoid parsing and flattening the selector string on every conversion. static final MapCodec SCORE_COMPONENT_MAP_CODEC = SCORE_COMPONENT_INNER_MAP_CODEC.fieldOf("score"); static final MapCodec SELECTOR_COMPONENT_MAP_CODEC = mapCodec((instance) -> { return instance.group( diff --git a/paper-server/src/main/java/io/papermc/paper/adventure/BossBarImplementationImpl.java b/paper-server/src/main/java/io/papermc/paper/adventure/BossBarImplementationImpl.java index 23bd6d2d8fed..4d5f001b2eeb 100644 --- a/paper-server/src/main/java/io/papermc/paper/adventure/BossBarImplementationImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/adventure/BossBarImplementationImpl.java @@ -27,6 +27,7 @@ public BossBarImplementationImpl(final BossBar bar) { public void playerShow(final CraftPlayer player) { if (this.vanilla == null) { this.vanilla = new ServerBossEvent( + net.minecraft.util.Mth.createInsecureUUID(net.minecraft.util.RandomSource.create()), PaperAdventure.asVanilla(this.bar.name()), PaperAdventure.asVanilla(this.bar.color()), PaperAdventure.asVanilla(this.bar.overlay()) diff --git a/paper-server/src/main/java/io/papermc/paper/adventure/ChatProcessor.java b/paper-server/src/main/java/io/papermc/paper/adventure/ChatProcessor.java index 965ddeb8f575..f1ef191a9ca3 100644 --- a/paper-server/src/main/java/io/papermc/paper/adventure/ChatProcessor.java +++ b/paper-server/src/main/java/io/papermc/paper/adventure/ChatProcessor.java @@ -244,7 +244,7 @@ public void sendFormatChangedViewerAware(CraftPlayer player, Component displayNa @Override public void sendMessageChanged(CraftPlayer player, net.minecraft.network.chat.Component renderedMessage, Set viewers, ChatType.Bound chatType) { - this.broadcastToViewers(viewers, chatType, $ -> renderedMessage); + this.broadcastToViewers(viewers, chatType, _ -> renderedMessage); } @Override diff --git a/paper-server/src/main/java/io/papermc/paper/adventure/PaperAdventure.java b/paper-server/src/main/java/io/papermc/paper/adventure/PaperAdventure.java index 8cd1ea370dd4..d0f87ef856d9 100644 --- a/paper-server/src/main/java/io/papermc/paper/adventure/PaperAdventure.java +++ b/paper-server/src/main/java/io/papermc/paper/adventure/PaperAdventure.java @@ -47,6 +47,7 @@ import net.minecraft.nbt.Tag; import net.minecraft.nbt.TagParser; import net.minecraft.network.chat.ComponentUtils; +import net.minecraft.network.chat.ResolutionContext; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; import net.minecraft.network.protocol.game.ClientboundSoundPacket; @@ -264,7 +265,8 @@ public static Component resolveWithContext(final @NotNull Component component, f css.bypassSelectorPermissions = true; } try { - return asAdventure(ComponentUtils.updateForEntity(css, asVanilla(component), scoreboardSubject == null ? null : ((CraftEntity) scoreboardSubject).getHandle(), 0)); + final ResolutionContext resoutionContext = ResolutionContext.builder().withSource(css).withEntityOverride(scoreboardSubject == null ? null : ((CraftEntity) scoreboardSubject).getHandle()).build(); + return asAdventure(ComponentUtils.resolve(resoutionContext, asVanilla(component), 0)); } catch (final CommandSyntaxException e) { throw new IOException(e); } finally { diff --git a/paper-server/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/paper-server/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java index b03b2d098c20..582937bbb8b5 100644 --- a/paper-server/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java +++ b/paper-server/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java @@ -193,8 +193,8 @@ public void modifyBlocks(ClientboundLevelChunkWithLightPacket chunkPacket, Chunk } LevelChunk chunk = chunkPacketInfo.getChunk(); - int x = chunk.getPos().x; - int z = chunk.getPos().z; + int x = chunk.getPos().x(); + int z = chunk.getPos().z(); Level level = chunk.getLevel(); ((ChunkPacketInfoAntiXray) chunkPacketInfo).setNearbyChunks(level.getChunkIfLoaded(x - 1, z), level.getChunkIfLoaded(x + 1, z), level.getChunkIfLoaded(x, z - 1), level.getChunkIfLoaded(x, z + 1)); executor.execute((Runnable) chunkPacketInfo); diff --git a/paper-server/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java b/paper-server/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java index febd441df97c..df34aee12961 100644 --- a/paper-server/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java +++ b/paper-server/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java @@ -21,10 +21,6 @@ protected PaperFluidData(final FluidState state) { this.state = state; } - /** - * Provides the internal server representation of this fluid data. - * @return the fluid state. - */ public FluidState getState() { return this.state; } @@ -48,7 +44,7 @@ public FluidState getState() { Preconditions.checkArgument(location.getWorld() != null, "Cannot compute flow direction on world-less location"); return CraftVector.toBukkit(this.state.getFlow( ((CraftWorld) location.getWorld()).getHandle(), - CraftLocation.toBlockPosition(location) + CraftLocation.toBlockPos(location) )); } @@ -60,7 +56,7 @@ public int getLevel() { @Override public float computeHeight(@NotNull final Location location) { Preconditions.checkArgument(location.getWorld() != null, "Cannot compute height on world-less location"); - return this.state.getHeight(((CraftWorld) location.getWorld()).getHandle(), CraftLocation.toBlockPosition(location)); + return this.state.getHeight(((CraftWorld) location.getWorld()).getHandle(), CraftLocation.toBlockPos(location)); } @Override diff --git a/paper-server/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java b/paper-server/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java index bff395667f69..486fbfd5ef8c 100644 --- a/paper-server/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java @@ -1,7 +1,6 @@ package io.papermc.paper.command.brigadier.argument; import com.destroystokyo.paper.profile.CraftPlayerProfile; -import com.google.common.collect.Collections2; import com.google.common.collect.ForwardingSet; import com.google.common.collect.Lists; import com.google.common.collect.Range; @@ -37,7 +36,6 @@ import io.papermc.paper.registry.TypedKey; import io.papermc.paper.util.MCUtil; import java.util.Collection; -import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Set; @@ -61,12 +59,12 @@ import net.minecraft.commands.arguments.GameProfileArgument; import net.minecraft.commands.arguments.HeightmapTypeArgument; import net.minecraft.commands.arguments.HexColorArgument; +import net.minecraft.commands.arguments.IdentifierArgument; import net.minecraft.commands.arguments.MessageArgument; import net.minecraft.commands.arguments.ObjectiveCriteriaArgument; import net.minecraft.commands.arguments.RangeArgument; import net.minecraft.commands.arguments.ResourceArgument; import net.minecraft.commands.arguments.ResourceKeyArgument; -import net.minecraft.commands.arguments.IdentifierArgument; import net.minecraft.commands.arguments.ScoreboardSlotArgument; import net.minecraft.commands.arguments.StyleArgument; import net.minecraft.commands.arguments.TemplateMirrorArgument; @@ -158,9 +156,9 @@ public ArgumentType players() { public ArgumentType playerProfiles() { return this.wrap(GameProfileArgument.gameProfile(), result -> { if (result instanceof GameProfileArgument.SelectorResult) { - return sourceStack -> Collections.unmodifiableCollection(Collections2.transform(result.getNames((CommandSourceStack) sourceStack), CraftPlayerProfile::new)); + return sourceStack -> MCUtil.transformUnmodifiable(result.getNames((CommandSourceStack) sourceStack), CraftPlayerProfile::new); } else { - return sourceStack -> Collections.unmodifiableCollection(Collections2.transform(result.getNames((CommandSourceStack) sourceStack), CraftPlayerProfile::new)); + return sourceStack -> MCUtil.transformUnmodifiable(result.getNames((CommandSourceStack) sourceStack), CraftPlayerProfile::new); } }); } @@ -189,7 +187,7 @@ public ArgumentType blockInWorldPredicate() { result -> (block, loadChunk) -> { final BlockInWorld blockInWorld = new BlockInWorld( ((CraftWorld) block.getWorld()).getHandle(), - CraftLocation.toBlockPosition(block.getLocation()), + CraftLocation.toBlockPos(block.getLocation()), loadChunk ); // Get state lazy loads the state, will remain null if chunk is unloaded. @@ -250,7 +248,7 @@ protected Set delegate() { return this.wrap(SwizzleArgument.swizzle(), (result) -> { final EnumSet bukkitAxes = EnumSet.noneOf(Axis.class); for (final Direction.Axis nmsAxis : result) { - bukkitAxes.add(CraftBlockData.toBukkit(nmsAxis, Axis.class)); + bukkitAxes.add(CraftBlockData.fromVanilla(nmsAxis, Axis.class)); } return new AxisSetImpl(bukkitAxes); }); @@ -270,7 +268,7 @@ public ArgumentType blockState() { @Override public ArgumentType itemStack() { return this.wrap(ItemArgument.item(PaperCommands.INSTANCE.getBuildContext()), (result) -> { - return CraftItemStack.asBukkitCopy(result.createItemStack(1, true)); + return CraftItemStack.asBukkitCopy(result.createItemStack(1)); }); } diff --git a/paper-server/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java b/paper-server/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java index 4c3d5712c677..dc84b7c3746f 100644 --- a/paper-server/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java +++ b/paper-server/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java @@ -15,13 +15,10 @@ import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.JoinConfiguration; import net.kyori.adventure.text.TextComponent; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentType; -import net.minecraft.core.component.TypedDataComponent; -import net.minecraft.core.registries.Registries; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.SnbtPrinterTagVisitor; @@ -29,13 +26,13 @@ import net.minecraft.resources.RegistryOps; import net.minecraft.world.item.ItemStack; import org.bukkit.command.CommandSender; -import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.framework.qual.DefaultQualifier; +import static java.util.Objects.requireNonNull; import static net.kyori.adventure.text.Component.join; import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.textOfChildren; @@ -56,71 +53,77 @@ public boolean execute(final CommandSender sender, final String subCommand, fina return true; } - @SuppressWarnings({"unchecked", "OptionalAssignedToNull", "rawtypes"}) + @SuppressWarnings({"unchecked", "rawtypes"}) private void doDumpItem(final CommandSender sender, final boolean includeAllComponents) { if (!(sender instanceof final Player player)) { sender.sendMessage("Only players can use this command"); return; } - final ItemStack itemStack = CraftItemStack.asNMSCopy(player.getInventory().getItemInMainHand()); - final TextComponent.Builder visualOutput = Component.text(); - final StringBuilder itemCommandBuilder = new StringBuilder(); - final String itemName = itemStack.getItemHolder().unwrapKey().orElseThrow().identifier().toString(); - itemCommandBuilder.append(itemName); - visualOutput.append(text(itemName, YELLOW)); // item type - final Set> referencedComponentTypes = Collections.newSetFromMap(new IdentityHashMap<>()); - final DataComponentPatch patch = itemStack.getComponentsPatch(); - referencedComponentTypes.addAll(patch.entrySet().stream().map(Map.Entry::getKey).toList()); - final DataComponentMap prototype = itemStack.getItem().components(); + final TextComponent.Builder output = Component.text(); + final StringBuilder itemToCopy = new StringBuilder(); + final ItemStack item = CraftItemStack.asNMSCopy(player.getInventory().getItemInMainHand()); + final String itemName = item.typeHolder().unwrapKey().orElseThrow().identifier().toString(); + itemToCopy.append(itemName); + output.append(text(itemName, YELLOW)); // item type + + final Set> remainingComponents = Collections.newSetFromMap(new IdentityHashMap<>()); + final DataComponentPatch patch = item.getComponentsPatch(); + remainingComponents.addAll(patch.entrySet().stream().map(Map.Entry::getKey).toList()); + final DataComponentMap prototype = item.getPrototype(); if (includeAllComponents) { - referencedComponentTypes.addAll(prototype.keySet()); + remainingComponents.addAll(prototype.keySet()); } + remainingComponents.removeIf(DataComponentType::isTransient); - final RegistryAccess.Frozen access = ((CraftServer) sender.getServer()).getServer().registryAccess(); - final RegistryOps ops = access.createSerializationContext(NbtOps.INSTANCE); - final Registry> registry = access.lookupOrThrow(Registries.DATA_COMPONENT_TYPE); - final List componentComponents = new ArrayList<>(); - final List commandComponents = new ArrayList<>(); - for (final DataComponentType type : referencedComponentTypes) { - final String path = registry.getResourceKey(type).orElseThrow().identifier().getPath(); - final @Nullable Optional patchedValue = patch.get(type); - final @Nullable TypedDataComponent prototypeValue = prototype.getTyped(type); - if (patchedValue != null) { + final RegistryOps ops = CraftRegistry.getMinecraftRegistry().createSerializationContext(NbtOps.INSTANCE); + final List writtenComponents = new ArrayList<>(); + final List componentsToCopy = new ArrayList<>(); + for (final Map.Entry, Optional> entry : patch.entrySet()) { // patch + final DataComponentType type = entry.getKey(); + if (remainingComponents.remove(type)) { + final String path = requireNonNull(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(type)).getPath(); + final Optional patchedValue = entry.getValue(); if (patchedValue.isEmpty()) { - componentComponents.add(text().append(text('!', RED), text(path, AQUA))); - commandComponents.add("!" + path); + writtenComponents.add(text().append(text('!', RED), text(path, AQUA))); + componentsToCopy.add("!" + path); } else { final Tag serialized = (Tag) ((DataComponentType) type).codecOrThrow().encodeStart(ops, patchedValue.get()).getOrThrow(); - writeComponentValue(componentComponents::add, commandComponents::add, path, serialized); + writeComponentValue(writtenComponents::add, componentsToCopy::add, path, serialized); } - } else if (includeAllComponents && prototypeValue != null) { - final Tag serialized = prototypeValue.encodeValue(ops).getOrThrow(); - writeComponentValue(componentComponents::add, commandComponents::add, path, serialized); } } - if (!componentComponents.isEmpty()) { - visualOutput.append( + + if (includeAllComponents) { + for (final DataComponentType type : remainingComponents) { // prototype + final String path = requireNonNull(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(type)).getPath(); + final Tag serialized = requireNonNull(prototype.getTyped(type)).encodeValue(ops).getOrThrow(); + writeComponentValue(writtenComponents::add, componentsToCopy::add, path, serialized); + } + } + + if (!writtenComponents.isEmpty()) { + output.append( text("[", color(0x8910CE)), - join(JoinConfiguration.separator(text(",", GRAY)), componentComponents), + join(JoinConfiguration.separator(text(",", GRAY)), writtenComponents), text("]", color(0x8910CE)) ); - itemCommandBuilder + itemToCopy .append("[") - .append(String.join(",", commandComponents)) + .append(String.join(",", componentsToCopy)) .append("]"); } - player.sendMessage(visualOutput.build().compact()); + player.sendMessage(output.build().compact()); final Component copyMsg = text("Click to copy item definition to clipboard for use with /give", GRAY, ITALIC); - sender.sendMessage(copyMsg.clickEvent(copyToClipboard(itemCommandBuilder.toString()))); + sender.sendMessage(copyMsg.clickEvent(copyToClipboard(itemToCopy.toString()))); } - private static void writeComponentValue(final Consumer visualOutput, final Consumer commandOutput, final String path, final Tag serialized) { - visualOutput.accept(textOfChildren( + private static void writeComponentValue(final Consumer output, final Consumer copyOutput, final String path, final Tag serialized) { + output.accept(textOfChildren( text(path, color(0xFF7FD7)), text("=", WHITE), PaperAdventure.asAdventure(NbtUtils.toPrettyComponent(serialized)) )); - commandOutput.accept(path + "=" + new SnbtPrinterTagVisitor("", 0, new ArrayList<>()).visit(serialized)); + copyOutput.accept(path + "=" + new SnbtPrinterTagVisitor("", 0, new ArrayList<>()).visit(serialized)); } @Override diff --git a/paper-server/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java b/paper-server/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java index e3e21acb82b5..c5ae414cdf4e 100644 --- a/paper-server/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java +++ b/paper-server/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java @@ -125,9 +125,9 @@ private void listEntities(final CommandSender sender, final String[] args) { info.getRight().entrySet().stream() .sorted((a, b) -> !a.getValue().equals(b.getValue()) ? b.getValue() - a.getValue() : a.getKey().toString().compareTo(b.getKey().toString())) .limit(10).forEach(e -> { - final int x = (e.getKey().x << 4) + 8; - final int z = (e.getKey().z << 4) + 8; - final Component message = text(" " + e.getValue() + ": " + e.getKey().x + ", " + e.getKey().z + (chunkProviderServer.isPositionTicking(e.getKey().toLong()) ? " (Ticking)" : " (Non-Ticking)")) + final int x = (e.getKey().x() << 4) + 8; + final int z = (e.getKey().z() << 4) + 8; + final Component message = text(" " + e.getValue() + ": " + e.getKey().x() + ", " + e.getKey().z() + (chunkProviderServer.isPositionTicking(e.getKey().pack()) ? " (Ticking)" : " (Non-Ticking)")) .hoverEvent(HoverEvent.showText(text("Click to teleport to chunk", GREEN))) .clickEvent(ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND, "/minecraft:execute as @s in " + world.getWorld().getKey() + " run tp " + x + " " + (world.getWorld().getHighestBlockYAt(x, z, HeightMap.MOTION_BLOCKING) + 1) + " " + z)); sender.sendMessage(message); diff --git a/paper-server/src/main/java/io/papermc/paper/configuration/Configurations.java b/paper-server/src/main/java/io/papermc/paper/configuration/Configurations.java index 889051d2d6e6..001daf9f0018 100644 --- a/paper-server/src/main/java/io/papermc/paper/configuration/Configurations.java +++ b/paper-server/src/main/java/io/papermc/paper/configuration/Configurations.java @@ -276,7 +276,7 @@ private UnaryOperator applyObjectMapperFactory(final Objec } public Path getWorldConfigFile(ServerLevel level) { - return level.levelStorageAccess.levelDirectory.path().resolve(this.worldConfigFileName); + return level.getServer().storageSource.getDimensionPath(level.dimension()).resolve(this.worldConfigFileName); } public static class ContextMap { diff --git a/paper-server/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/paper-server/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java index e0491ef5cfc0..93027a0e7e59 100644 --- a/paper-server/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java +++ b/paper-server/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java @@ -347,7 +347,7 @@ public void reloadConfigs(MinecraftServer server) { } private static ContextMap createWorldContextMap(ServerLevel level) { - return createWorldContextMap(level.levelStorageAccess.levelDirectory.path(), level.serverLevelData.getLevelName(), level.dimension().identifier(), level.spigotConfig, level.registryAccess(), level.getGameRules()); + return createWorldContextMap(level.getServer().storageSource.getDimensionPath(level.dimension()), level.bukkitName, level.dimension().identifier(), level.spigotConfig, level.registryAccess(), level.getGameRules()); } public static ContextMap createWorldContextMap(final Path dir, final String levelName, final Identifier worldKey, final SpigotWorldConfig spigotConfig, final RegistryAccess registryAccess, final GameRules gameRules) { diff --git a/paper-server/src/main/java/io/papermc/paper/configuration/serializer/ServerboundPacketClassSerializer.java b/paper-server/src/main/java/io/papermc/paper/configuration/serializer/ServerboundPacketClassSerializer.java index 3dd96af2ffea..52195607af4c 100644 --- a/paper-server/src/main/java/io/papermc/paper/configuration/serializer/ServerboundPacketClassSerializer.java +++ b/paper-server/src/main/java/io/papermc/paper/configuration/serializer/ServerboundPacketClassSerializer.java @@ -73,7 +73,7 @@ public final class ServerboundPacketClassSerializer extends ScalarSerializer extends ReadablePlayerCookieConnectionImpl implements PlayerCommonConnection { - protected final T handle; + protected final T packetListener; - public PaperCommonConnection(final T serverConfigurationPacketListenerImpl) { - super(serverConfigurationPacketListenerImpl.connection); - this.handle = serverConfigurationPacketListenerImpl; + public PaperCommonConnection(final T packetListener) { + super(packetListener.connection); + this.packetListener = packetListener; } @Override public void sendReportDetails(final Map details) { - this.handle.send(new ClientboundCustomReportDetailsPacket(details)); + this.packetListener.send(new ClientboundCustomReportDetailsPacket(details)); } @Override public void sendLinks(final ServerLinks links) { - this.handle.send(new ClientboundServerLinksPacket(((CraftServerLinks) links).getServerLinks().untrust())); + this.packetListener.send(new ClientboundServerLinksPacket(((CraftServerLinks) links).getServerLinks().untrust())); } @Override public void transfer(final String host, final int port) { - this.handle.send(new ClientboundTransferPacket(host, port)); + this.packetListener.send(new ClientboundTransferPacket(host, port)); } @Override @@ -72,32 +72,32 @@ public T getClientOption(ClientOption type) { @Override public void disconnect(final Component component) { - this.handle.disconnect(PaperAdventure.asVanilla(component), DisconnectionReason.UNKNOWN); + this.packetListener.disconnect(PaperAdventure.asVanilla(component), DisconnectionReason.UNKNOWN); } @Override public boolean isTransferred() { - return this.handle.isTransferred(); + return this.packetListener.isTransferred(); } @Override public SocketAddress getAddress() { - return this.handle.connection.channel.remoteAddress(); + return this.packetListener.connection.channel.remoteAddress(); } @Override public InetSocketAddress getClientAddress() { - return (InetSocketAddress) this.handle.connection.getRemoteAddress(); + return (InetSocketAddress) this.packetListener.connection.getRemoteAddress(); } @Override public @Nullable InetSocketAddress getVirtualHost() { - return this.handle.connection.virtualHost; + return this.packetListener.connection.virtualHost; } @Override public @Nullable InetSocketAddress getHAProxyAddress() { - return this.handle.connection.haProxyAddress instanceof final InetSocketAddress inetSocketAddress ? inetSocketAddress : null; + return this.packetListener.connection.haProxyAddress instanceof final InetSocketAddress inetSocketAddress ? inetSocketAddress : null; } @Override @@ -105,9 +105,8 @@ public void storeCookie(final NamespacedKey key, final byte[] value) { Preconditions.checkArgument(key != null, "Cookie key cannot be null"); Preconditions.checkArgument(value != null, "Cookie value cannot be null"); Preconditions.checkArgument(value.length <= 5120, "Cookie value too large, must be smaller than 5120 bytes"); - Preconditions.checkState(this.canStoreCookie(), "Can only store cookie in CONFIGURATION or PLAY protocol."); - this.handle.send(new ClientboundStoreCookiePacket(CraftNamespacedKey.toMinecraft(key), value)); + this.packetListener.send(new ClientboundStoreCookiePacket(CraftNamespacedKey.toMinecraft(key), value)); } public abstract ClientInformation getClientInformation(); diff --git a/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerConfigurationConnection.java b/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerConfigurationConnection.java index c91f926da39d..49ea407cd912 100644 --- a/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerConfigurationConnection.java +++ b/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerConfigurationConnection.java @@ -47,7 +47,7 @@ public PaperPlayerConfigurationConnection(final ServerConfigurationPacketListene @Override public ClientInformation getClientInformation() { - return this.handle.clientInformation; + return this.packetListener.clientInformation; } @Override @@ -61,39 +61,39 @@ public void sendResourcePacks(final ResourcePackRequest request) { final ResourcePackInfo pack = iter.next(); packs.add(new ClientboundResourcePackPushPacket(pack.id(), pack.uri().toASCIIString(), pack.hash(), request.required(), iter.hasNext() ? Optional.empty() : Optional.ofNullable(prompt))); if (request.callback() != ResourcePackCallback.noOp()) { - this.handle.packCallbacks.put(pack.id(), request.callback()); // just override if there is a previously existing callback + this.packetListener.packCallbacks.put(pack.id(), request.callback()); // just override if there is a previously existing callback } } - packs.forEach(this.handle::send); + packs.forEach(this.packetListener::send); } @Override public void removeResourcePacks(final UUID id, final UUID... others) { - net.kyori.adventure.util.MonkeyBars.nonEmptyArrayToList(pack -> new ClientboundResourcePackPopPacket(Optional.of(pack)), id, others).forEach(this.handle::send); + net.kyori.adventure.util.MonkeyBars.nonEmptyArrayToList(pack -> new ClientboundResourcePackPopPacket(Optional.of(pack)), id, others).forEach(this.packetListener::send); } @Override public void clearResourcePacks() { - this.handle.send(new ClientboundResourcePackPopPacket(Optional.empty())); + this.packetListener.send(new ClientboundResourcePackPopPacket(Optional.empty())); } @Override public void showDialog(final DialogLike dialog) { - this.handle.send(new ClientboundShowDialogPacket(PaperDialog.bukkitToMinecraftHolder((Dialog) dialog))); + this.packetListener.send(new ClientboundShowDialogPacket(PaperDialog.bukkitToMinecraftHolder((Dialog) dialog))); } @Override public void closeDialog() { - this.handle.send(ClientboundClearDialogPacket.INSTANCE); + this.packetListener.send(ClientboundClearDialogPacket.INSTANCE); } @Override public Pointers pointers() { if (this.adventurePointers == null) { this.adventurePointers = Pointers.builder() - .withDynamic(Identity.NAME, () -> this.handle.getOwner().name()) - .withDynamic(Identity.UUID, () -> this.handle.getOwner().id()) - .withDynamic(Identity.LOCALE, () -> Objects.requireNonNullElse(Translator.parseLocale(this.handle.clientInformation.language()), Locale.US)) + .withDynamic(Identity.NAME, () -> this.packetListener.getOwner().name()) + .withDynamic(Identity.UUID, () -> this.packetListener.getOwner().id()) + .withDynamic(Identity.LOCALE, () -> Objects.requireNonNullElse(Translator.parseLocale(this.packetListener.clientInformation.language()), Locale.US)) .build(); } @@ -107,40 +107,40 @@ public Audience getAudience() { @Override public PlayerProfile getProfile() { - return CraftPlayerProfile.asBukkitCopy(this.handle.getOwner()); + return CraftPlayerProfile.asBukkitCopy(this.packetListener.getOwner()); } @Override public void clearChat() { - this.handle.send(ClientboundResetChatPacket.INSTANCE); + this.packetListener.send(ClientboundResetChatPacket.INSTANCE); } @Override public void completeReconfiguration() { - final ConfigurationTask task = this.handle.currentTask; + final ConfigurationTask task = this.packetListener.currentTask; if (task != null) { // This means that the player is going through the normal configuration process, or is already returning to the game phase. // Be safe and just ignore, as many plugins may call this. return; } - this.handle.returnToWorld(); + this.packetListener.returnToWorld(); } @Override public Set channels() { - return this.handle.pluginMessagerChannels; + return this.packetListener.pluginMessagerChannels; } @Override public void sendPluginMessage(final Plugin source, final String channel, final byte[] message) { - StandardMessenger.validatePluginMessage(this.handle.cserver.getMessenger(), source, channel, message); + StandardMessenger.validatePluginMessage(this.packetListener.cserver.getMessenger(), source, channel, message); if (this.channels().contains(channel)) { @SuppressWarnings("deprecation") // "not an API method" does not apply to us Identifier id = Identifier.parse(StandardMessenger.validateAndCorrectChannel(channel)); ClientboundCustomPayloadPacket packet = new ClientboundCustomPayloadPacket(new DiscardedPayload(id, message)); - this.handle.send(packet); + this.packetListener.send(packet); } } @@ -151,6 +151,6 @@ public Set getListeningPluginChannels() { @Override public boolean isConnected() { - return this.handle.connection.isConnected(); + return this.packetListener.connection.isConnected(); } } diff --git a/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerGameConnection.java b/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerGameConnection.java index 1a06f8ffc45e..ac252f2f43f1 100644 --- a/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerGameConnection.java +++ b/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerGameConnection.java @@ -1,10 +1,10 @@ package io.papermc.paper.connection; +import java.util.Set; import net.minecraft.server.level.ClientInformation; import net.minecraft.server.network.ServerGamePacketListenerImpl; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; -import java.util.Set; public class PaperPlayerGameConnection extends PaperCommonConnection implements PlayerGameConnection { @@ -14,20 +14,20 @@ public PaperPlayerGameConnection(final ServerGamePacketListenerImpl serverConfig @Override public ClientInformation getClientInformation() { - return this.handle.player.clientInformation(); + return this.packetListener.player.clientInformation(); } @Override public void reenterConfiguration() { - if (HorriblePlayerLoginEventHack.warnReenterConfiguration(this.handle.connection)) { + if (HorriblePlayerLoginEventHack.warnReenterConfiguration(this.packetListener.connection)) { return; } - this.handle.switchToConfig(); + this.packetListener.switchToConfig(); } @Override public Player getPlayer() { - return this.handle.getCraftPlayer(); + return this.packetListener.getCraftPlayer(); } @Override diff --git a/paper-server/src/main/java/io/papermc/paper/connection/PluginMessageBridgeImpl.java b/paper-server/src/main/java/io/papermc/paper/connection/PluginMessageBridgeImpl.java index 798ebc2be5f5..db1f63a3a6cd 100644 --- a/paper-server/src/main/java/io/papermc/paper/connection/PluginMessageBridgeImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/connection/PluginMessageBridgeImpl.java @@ -1,13 +1,13 @@ package io.papermc.paper.connection; import com.google.common.base.Preconditions; +import java.util.Set; import org.bukkit.Bukkit; import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.event.player.PlayerRegisterChannelEvent; import org.bukkit.event.player.PlayerUnregisterChannelEvent; import org.bukkit.plugin.messaging.StandardMessenger; import org.jspecify.annotations.NullMarked; -import java.util.Set; @NullMarked public interface PluginMessageBridgeImpl { diff --git a/paper-server/src/main/java/io/papermc/paper/connection/ReadablePlayerCookieConnectionImpl.java b/paper-server/src/main/java/io/papermc/paper/connection/ReadablePlayerCookieConnectionImpl.java index 226e63bd7749..eeb64a8b950f 100644 --- a/paper-server/src/main/java/io/papermc/paper/connection/ReadablePlayerCookieConnectionImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/connection/ReadablePlayerCookieConnectionImpl.java @@ -28,18 +28,14 @@ public CompletableFuture retrieveCookie(final NamespacedKey key) { Preconditions.checkArgument(key != null, "Cookie key cannot be null"); CompletableFuture future = new CompletableFuture<>(); - Identifier resourceLocation = CraftNamespacedKey.toMinecraft(key); - this.requestedCookies.put(resourceLocation, new CookieFuture(resourceLocation, future)); + Identifier id = CraftNamespacedKey.toMinecraft(key); + this.requestedCookies.put(id, new CookieFuture(id, future)); - this.connection.send(new ClientboundCookieRequestPacket(resourceLocation)); + this.connection.send(new ClientboundCookieRequestPacket(id)); return future; } - public boolean canStoreCookie() { - return true; - } - public boolean handleCookieResponse(ServerboundCookieResponsePacket packet) { CookieFuture future = this.requestedCookies.get(packet.key()); if (future != null) { diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java index 15a96abb2ac7..94573a05b97f 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java @@ -12,9 +12,9 @@ public record DataComponentAdapter( Function vanillaToApi, boolean codecValidation ) { - static final Function API_TO_UNIT_CONVERTER = $ -> Unit.INSTANCE; + static final Function API_TO_UNIT_CONVERTER = _ -> Unit.INSTANCE; - static final Function API_TO_UNIMPLEMENTED_CONVERTER = $ -> { + static final Function API_TO_UNIMPLEMENTED_CONVERTER = _ -> { throw new UnsupportedOperationException("Cannot convert an API value to an unimplemented type"); }; diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java index f6783d5f9279..328cc1a7655c 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java @@ -45,7 +45,9 @@ import io.papermc.paper.datacomponent.item.PaperWeapon; import io.papermc.paper.datacomponent.item.PaperWritableBookContent; import io.papermc.paper.datacomponent.item.PaperWrittenBookContent; -import io.papermc.paper.registry.PaperRegistries; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.data.util.Conversions; +import io.papermc.paper.registry.set.PaperRegistrySets; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @@ -55,15 +57,12 @@ import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.util.Unit; -import net.minecraft.world.item.EitherHolder; import net.minecraft.world.item.Rarity; import net.minecraft.world.item.component.InstrumentComponent; import net.minecraft.world.item.component.MapPostProcessing; -import net.minecraft.world.item.component.ProvidesTrimMaterial; import org.bukkit.DyeColor; import org.bukkit.craftbukkit.CraftArt; import org.bukkit.craftbukkit.CraftMusicInstrument; -import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.damage.CraftDamageType; import org.bukkit.craftbukkit.entity.CraftCat; import org.bukkit.craftbukkit.entity.CraftChicken; @@ -90,11 +89,11 @@ public final class DataComponentAdapters { - static final Function UNIT_TO_API_CONVERTER = $ -> { + static final Function UNIT_TO_API_CONVERTER = _ -> { throw new UnsupportedOperationException("Cannot convert the Unit type to an API value"); }; - static final Function UNIMPLEMENTED_TO_API_CONVERTER = $ -> { + static final Function UNIMPLEMENTED_TO_API_CONVERTER = _ -> { throw new UnsupportedOperationException("Cannot convert the an unimplemented type to an API value"); }; @@ -109,7 +108,7 @@ public static void bootstrap() { registerIdentity(DataComponents.POTION_DURATION_SCALE); register(DataComponents.CUSTOM_NAME, PaperAdventure::asAdventure, PaperAdventure::asVanilla); registerIdentity(DataComponents.MINIMUM_ATTACK_CHARGE); - register(DataComponents.DAMAGE_TYPE, nms -> CraftDamageType.minecraftHolderToBukkit(nms.unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new EitherHolder<>(CraftDamageType.bukkitToMinecraftHolder(api))); + register(DataComponents.DAMAGE_TYPE, CraftDamageType::minecraftHolderToBukkit, CraftDamageType::bukkitToMinecraftHolder); register(DataComponents.ITEM_NAME, PaperAdventure::asAdventure, PaperAdventure::asVanilla); register(DataComponents.ITEM_MODEL, PaperAdventure::asAdventure, PaperAdventure::asVanilla); register(DataComponents.LORE, PaperItemLore::new); @@ -136,6 +135,7 @@ public static void bootstrap() { register(DataComponents.TOOLTIP_STYLE, PaperAdventure::asAdventure, PaperAdventure::asVanilla); register(DataComponents.DEATH_PROTECTION, PaperDeathProtection::new); register(DataComponents.STORED_ENCHANTMENTS, PaperItemEnchantments::new); + register(DataComponents.DYE, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); register(DataComponents.DYED_COLOR, PaperDyedItemColor::new); register(DataComponents.MAP_COLOR, PaperMapItemColor::new); register(DataComponents.MAP_ID, PaperMapId::new); @@ -152,11 +152,11 @@ public static void bootstrap() { // entity data // bucket entity data // block entity data - register(DataComponents.INSTRUMENT, nms -> CraftMusicInstrument.minecraftHolderToBukkit(nms.instrument().unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new InstrumentComponent(CraftMusicInstrument.bukkitToMinecraftHolder(api))); - register(DataComponents.PROVIDES_TRIM_MATERIAL, nms -> CraftTrimMaterial.minecraftHolderToBukkit(nms.material().unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new ProvidesTrimMaterial(CraftTrimMaterial.bukkitToMinecraftHolder(api))); + register(DataComponents.INSTRUMENT, nms -> CraftMusicInstrument.minecraftHolderToBukkit(nms.instrument()), api -> new InstrumentComponent(CraftMusicInstrument.bukkitToMinecraftHolder(api))); + register(DataComponents.PROVIDES_TRIM_MATERIAL, CraftTrimMaterial::minecraftHolderToBukkit, CraftTrimMaterial::bukkitToMinecraftHolder); register(DataComponents.OMINOUS_BOTTLE_AMPLIFIER, PaperOminousBottleAmplifier::new); register(DataComponents.JUKEBOX_PLAYABLE, PaperJukeboxPlayable::new); - register(DataComponents.PROVIDES_BANNER_PATTERNS, PaperRegistries::fromNms, PaperRegistries::toNms); + register(DataComponents.PROVIDES_BANNER_PATTERNS, set -> PaperRegistrySets.convertToApi(RegistryKey.BANNER_PATTERN, set), set -> PaperRegistrySets.convertToNms(Registries.BANNER_PATTERN, Conversions.global().lookup(), set)); register( DataComponents.RECIPES, nms -> transformUnmodifiable(nms, PaperAdventure::asAdventureKey), @@ -183,6 +183,7 @@ public static void bootstrap() { register(DataComponents.KINETIC_WEAPON, PaperKineticWeapon::new); register(DataComponents.ATTACK_RANGE, PaperAttackRange::new); register(DataComponents.SWING_ANIMATION, PaperSwingAnimation::new); + // registerIdentity(DataComponents.ADDITIONAL_TRADE_COST); register(DataComponents.VILLAGER_VARIANT, CraftVillager.CraftType::minecraftHolderToBukkit, CraftVillager.CraftType::bukkitToMinecraftHolder); register(DataComponents.WOLF_VARIANT, CraftWolf.CraftVariant::minecraftHolderToBukkit, CraftWolf.CraftVariant::bukkitToMinecraftHolder); register(DataComponents.WOLF_COLLAR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); @@ -196,15 +197,19 @@ public static void bootstrap() { register(DataComponents.MOOSHROOM_VARIANT, nms -> MushroomCow.Variant.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.cow.MushroomCow.Variant.values()[api.ordinal()]); register(DataComponents.RABBIT_VARIANT, nms -> Rabbit.Type.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.rabbit.Rabbit.Variant.byId(api.ordinal())); register(DataComponents.PIG_VARIANT, CraftPig.CraftVariant::minecraftHolderToBukkit, CraftPig.CraftVariant::bukkitToMinecraftHolder); + register(DataComponents.PIG_SOUND_VARIANT, CraftPig.CraftSoundVariant::minecraftHolderToBukkit, CraftPig.CraftSoundVariant::bukkitToMinecraftHolder); register(DataComponents.COW_VARIANT, CraftCow.CraftVariant::minecraftHolderToBukkit, CraftCow.CraftVariant::bukkitToMinecraftHolder); - register(DataComponents.CHICKEN_VARIANT, nms -> CraftChicken.CraftVariant.minecraftHolderToBukkit(nms.unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new EitherHolder<>(CraftChicken.CraftVariant.bukkitToMinecraftHolder(api))); + register(DataComponents.COW_SOUND_VARIANT, CraftCow.CraftSoundVariant::minecraftHolderToBukkit, CraftCow.CraftSoundVariant::bukkitToMinecraftHolder); + register(DataComponents.CHICKEN_VARIANT, CraftChicken.CraftVariant::minecraftHolderToBukkit, CraftChicken.CraftVariant::bukkitToMinecraftHolder); + register(DataComponents.CHICKEN_SOUND_VARIANT, CraftChicken.CraftSoundVariant::minecraftHolderToBukkit, CraftChicken.CraftSoundVariant::bukkitToMinecraftHolder); register(DataComponents.FROG_VARIANT, CraftFrog.CraftVariant::minecraftHolderToBukkit, CraftFrog.CraftVariant::bukkitToMinecraftHolder); - register(DataComponents.ZOMBIE_NAUTILUS_VARIANT, nms -> CraftZombieNautilus.CraftVariant.minecraftHolderToBukkit(nms.unwrap(CraftRegistry.getMinecraftRegistry()).orElseThrow()), api -> new EitherHolder<>(CraftZombieNautilus.CraftVariant.bukkitToMinecraftHolder(api))); + register(DataComponents.ZOMBIE_NAUTILUS_VARIANT, CraftZombieNautilus.CraftVariant::minecraftHolderToBukkit, CraftZombieNautilus.CraftVariant::bukkitToMinecraftHolder); register(DataComponents.HORSE_VARIANT, nms -> Horse.Color.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.equine.Variant.byId(api.ordinal())); register(DataComponents.PAINTING_VARIANT, CraftArt::minecraftHolderToBukkit, CraftArt::bukkitToMinecraftHolder); register(DataComponents.LLAMA_VARIANT, nms -> Llama.Color.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.equine.Llama.Variant.byId(api.ordinal())); register(DataComponents.AXOLOTL_VARIANT, nms -> Axolotl.Variant.values()[nms.ordinal()], api -> net.minecraft.world.entity.animal.axolotl.Axolotl.Variant.byId(api.ordinal())); register(DataComponents.CAT_VARIANT, CraftCat.CraftType::minecraftHolderToBukkit, CraftCat.CraftType::bukkitToMinecraftHolder); + register(DataComponents.CAT_SOUND_VARIANT, CraftCat.CraftSoundVariant::minecraftHolderToBukkit, CraftCat.CraftSoundVariant::bukkitToMinecraftHolder); register(DataComponents.CAT_COLLAR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); register(DataComponents.SHEEP_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); register(DataComponents.SHULKER_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData())); diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java index f8ba50404d24..cf10806dc61e 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java @@ -2,11 +2,9 @@ import com.destroystokyo.paper.profile.PlayerProfile; import com.google.common.base.Preconditions; -import io.papermc.paper.registry.PaperRegistries; import io.papermc.paper.registry.data.util.Conversions; import io.papermc.paper.registry.set.PaperRegistrySets; import io.papermc.paper.registry.set.RegistryKeySet; -import io.papermc.paper.registry.tag.TagKey; import io.papermc.paper.text.Filtered; import net.kyori.adventure.key.Key; import net.kyori.adventure.util.TriState; @@ -185,7 +183,7 @@ public UseRemainder useRemainder(final ItemStack stack) { Preconditions.checkArgument(stack != null, "Item cannot be null"); Preconditions.checkArgument(!stack.isEmpty(), "Remaining item cannot be empty!"); return new PaperUseRemainder( - new net.minecraft.world.item.component.UseRemainder(CraftItemStack.asNMSCopy(stack)) + new net.minecraft.world.item.component.UseRemainder(CraftItemStack.asTemplate(stack)) ); } @@ -201,8 +199,8 @@ public UseCooldown.Builder useCooldown(final float seconds) { } @Override - public DamageResistant damageResistant(final TagKey types) { - return new PaperDamageResistant(new net.minecraft.world.item.component.DamageResistant(PaperRegistries.toNms(types))); + public DamageResistant damageResistant(final RegistryKeySet types) { + return new PaperDamageResistant(new net.minecraft.world.item.component.DamageResistant(PaperRegistrySets.convertToNms(Registries.DAMAGE_TYPE, Conversions.global().lookup(), types))); } @Override diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperAttackRange.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperAttackRange.java index f721973602bf..6120ca6216e2 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperAttackRange.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperAttackRange.java @@ -14,22 +14,22 @@ public net.minecraft.world.item.component.AttackRange getHandle() { @Override public float minReach() { - return this.impl.minRange(); + return this.impl.minReach(); } @Override public float maxReach() { - return this.impl.maxRange(); + return this.impl.maxReach(); } @Override public float minCreativeReach() { - return this.impl.minCreativeRange(); + return this.impl.minCreativeReach(); } @Override public float maxCreativeReach() { - return this.impl.maxCreativeRange(); + return this.impl.maxCreativeReach(); } @Override @@ -44,10 +44,10 @@ public float mobFactor() { static final class BuilderImpl implements AttackRange.Builder { - private float minReach = net.minecraft.world.item.component.AttackRange.CODEC_DEFAULT.minRange(); - private float maxReach = net.minecraft.world.item.component.AttackRange.CODEC_DEFAULT.maxRange(); - private float minCreativeReach = net.minecraft.world.item.component.AttackRange.CODEC_DEFAULT.minCreativeRange(); - private float maxCreativeReach = net.minecraft.world.item.component.AttackRange.CODEC_DEFAULT.maxCreativeRange(); + private float minReach = net.minecraft.world.item.component.AttackRange.CODEC_DEFAULT.minReach(); + private float maxReach = net.minecraft.world.item.component.AttackRange.CODEC_DEFAULT.maxReach(); + private float minCreativeReach = net.minecraft.world.item.component.AttackRange.CODEC_DEFAULT.minCreativeReach(); + private float maxCreativeReach = net.minecraft.world.item.component.AttackRange.CODEC_DEFAULT.maxCreativeReach(); private float hitboxMargin = net.minecraft.world.item.component.AttackRange.CODEC_DEFAULT.hitboxMargin(); private float mobFactor = net.minecraft.world.item.component.AttackRange.CODEC_DEFAULT.mobFactor(); diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlockItemDataProperties.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlockItemDataProperties.java index 5757e16c5948..9968838b8818 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlockItemDataProperties.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlockItemDataProperties.java @@ -19,13 +19,13 @@ public record PaperBlockItemDataProperties( public BlockData createBlockData(final BlockType blockType) { final Block block = CraftBlockType.bukkitToMinecraftNew(blockType); final BlockState defaultState = block.defaultBlockState(); - return this.impl.apply(defaultState).createCraftBlockData(); + return this.impl.apply(defaultState).asBlockData(); } @Override public BlockData applyTo(final BlockData blockData) { final BlockState state = ((CraftBlockData) blockData).getState(); - return this.impl.apply(state).createCraftBlockData(); + return this.impl.apply(state).asBlockData(); } @Override diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlocksAttacks.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlocksAttacks.java index 71c927abdebc..651d95394fd7 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlocksAttacks.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBlocksAttacks.java @@ -6,12 +6,15 @@ import io.papermc.paper.datacomponent.item.blocksattacks.ItemDamageFunction; import io.papermc.paper.datacomponent.item.blocksattacks.PaperDamageReduction; import io.papermc.paper.datacomponent.item.blocksattacks.PaperItemDamageFunction; -import io.papermc.paper.registry.PaperRegistries; -import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.data.util.Conversions; +import io.papermc.paper.registry.set.PaperRegistrySets; +import io.papermc.paper.registry.set.RegistryKeySet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import java.util.Optional; import net.kyori.adventure.key.Key; +import net.minecraft.core.registries.Registries; import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.damage.DamageType; import org.jspecify.annotations.Nullable; @@ -46,9 +49,8 @@ public ItemDamageFunction itemDamage() { } @Override - public @Nullable TagKey bypassedBy() { - final Optional> tagKey = this.impl.bypassedBy().map(PaperRegistries::fromNms); - return tagKey.orElse(null); + public @Nullable RegistryKeySet bypassedBy() { + return this.impl.bypassedBy().map(holders -> PaperRegistrySets.convertToApi(RegistryKey.DAMAGE_TYPE, holders)).orElse(null); } @Override @@ -67,7 +69,7 @@ static final class BuilderImpl implements Builder { private float disableCooldownScale = 1.0F; private List damageReductions = new ObjectArrayList<>(); private ItemDamageFunction itemDamage = new PaperItemDamageFunction(net.minecraft.world.item.component.BlocksAttacks.ItemDamageFunction.DEFAULT); - private @Nullable TagKey bypassedBy; + private @Nullable RegistryKeySet bypassedBy; private @Nullable Key blockSound; private @Nullable Key disableSound; @@ -104,7 +106,7 @@ public Builder itemDamage(final ItemDamageFunction function) { } @Override - public Builder bypassedBy(@Nullable final TagKey bypassedBy) { + public Builder bypassedBy(final @Nullable RegistryKeySet bypassedBy) { this.bypassedBy = bypassedBy; return this; } @@ -128,7 +130,7 @@ public BlocksAttacks build() { this.disableCooldownScale, this.damageReductions.stream().map(damageReduction -> ((PaperDamageReduction) damageReduction).internal()).toList(), ((PaperItemDamageFunction) this.itemDamage).internal(), - Optional.ofNullable(this.bypassedBy).map(PaperRegistries::toNms), + Optional.ofNullable(this.bypassedBy).map(holders -> PaperRegistrySets.convertToNms(Registries.DAMAGE_TYPE, Conversions.global().lookup(), holders)), Optional.ofNullable(this.blockSound).map(PaperAdventure::resolveSound), Optional.ofNullable(this.disableSound).map(PaperAdventure::resolveSound) )); diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBundleContents.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBundleContents.java index ba95ce77dbdd..2f55228064e8 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBundleContents.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperBundleContents.java @@ -1,7 +1,6 @@ package io.papermc.paper.datacomponent.item; import com.google.common.base.Preconditions; -import io.papermc.paper.util.MCUtil; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import org.bukkit.craftbukkit.inventory.CraftItemStack; @@ -19,18 +18,18 @@ public net.minecraft.world.item.component.BundleContents getHandle() { @Override public List contents() { - return MCUtil.transformUnmodifiable((List) this.impl.items(), CraftItemStack::asBukkitCopy); + return this.impl.itemCopyStream().map(CraftItemStack::asBukkitCopy).toList(); } static final class BuilderImpl implements BundleContents.Builder { - private final List items = new ObjectArrayList<>(); + private final List items = new ObjectArrayList<>(); @Override public BundleContents.Builder add(final ItemStack stack) { Preconditions.checkArgument(stack != null, "stack cannot be null"); Preconditions.checkArgument(!stack.isEmpty(), "stack cannot be empty"); - this.items.add(CraftItemStack.asNMSCopy(stack)); + this.items.add(CraftItemStack.asTemplate(stack)); return this; } diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperChargedProjectiles.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperChargedProjectiles.java index 2129dd67fd02..3972d8097e13 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperChargedProjectiles.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperChargedProjectiles.java @@ -19,7 +19,7 @@ public net.minecraft.world.item.component.ChargedProjectiles getHandle() { @Override public List projectiles() { - return MCUtil.transformUnmodifiable(this.impl.getItems() /*makes copies internally*/, CraftItemStack::asCraftMirror); + return MCUtil.transformUnmodifiable(this.impl.itemCopies() /*makes copies internally*/, CraftItemStack::asCraftMirror); } static final class BuilderImpl implements ChargedProjectiles.Builder { @@ -45,7 +45,7 @@ public ChargedProjectiles build() { if (this.items.isEmpty()) { return new PaperChargedProjectiles(net.minecraft.world.item.component.ChargedProjectiles.EMPTY); } - return new PaperChargedProjectiles(net.minecraft.world.item.component.ChargedProjectiles.of(this.items)); + return new PaperChargedProjectiles(net.minecraft.world.item.component.ChargedProjectiles.ofNonEmpty(this.items)); } } } diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperDamageResistant.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperDamageResistant.java index adc986c8b3d6..8f828e204bde 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperDamageResistant.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperDamageResistant.java @@ -1,7 +1,8 @@ package io.papermc.paper.datacomponent.item; -import io.papermc.paper.registry.PaperRegistries; -import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.set.PaperRegistrySets; +import io.papermc.paper.registry.set.RegistryKeySet; import org.bukkit.craftbukkit.util.Handleable; import org.bukkit.damage.DamageType; @@ -15,7 +16,7 @@ public net.minecraft.world.item.component.DamageResistant getHandle() { } @Override - public TagKey types() { - return PaperRegistries.fromNms(this.impl.types()); + public RegistryKeySet types() { + return PaperRegistrySets.convertToApi(RegistryKey.DAMAGE_TYPE, this.impl.types()); } } diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java index aa10ee49b810..146ea7aba6ae 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java @@ -11,8 +11,8 @@ import net.minecraft.core.HolderSet; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.item.equipment.EquipmentAsset; diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemContainerContents.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemContainerContents.java index fe86d636f6e1..e424d8050e09 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemContainerContents.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperItemContainerContents.java @@ -19,7 +19,7 @@ public net.minecraft.world.item.component.ItemContainerContents getHandle() { @Override public List contents() { - return MCUtil.transformUnmodifiable(this.impl.items, CraftItemStack::asBukkitCopy); + return MCUtil.transformUnmodifiable(this.impl.items, optional -> optional.map(CraftItemStack::asBukkitCopy).orElse(null)); } static final class BuilderImpl implements ItemContainerContents.Builder { diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperJukeboxPlayable.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperJukeboxPlayable.java index b80f1420703e..7c8fc2934307 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperJukeboxPlayable.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperJukeboxPlayable.java @@ -1,9 +1,7 @@ package io.papermc.paper.datacomponent.item; -import net.minecraft.world.item.EitherHolder; import org.bukkit.JukeboxSong; import org.bukkit.craftbukkit.CraftJukeboxSong; -import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.util.Handleable; public record PaperJukeboxPlayable( @@ -17,10 +15,7 @@ public net.minecraft.world.item.JukeboxPlayable getHandle() { @Override public JukeboxSong jukeboxSong() { - return this.impl.song() - .unwrap(CraftRegistry.getMinecraftRegistry()) - .map(CraftJukeboxSong::minecraftHolderToBukkit) - .orElseThrow(); + return CraftJukeboxSong.minecraftHolderToBukkit(this.impl.song()); } static final class BuilderImpl implements JukeboxPlayable.Builder { @@ -40,7 +35,7 @@ public JukeboxPlayable.Builder jukeboxSong(final JukeboxSong song) { @Override public JukeboxPlayable build() { return new PaperJukeboxPlayable(new net.minecraft.world.item.JukeboxPlayable( - new EitherHolder<>(CraftJukeboxSong.bukkitToMinecraftHolder(this.song)) + CraftJukeboxSong.bukkitToMinecraftHolder(this.song) )); } } diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperResolvableProfile.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperResolvableProfile.java index 6a88eeda16e4..ecafbf6ea8b2 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperResolvableProfile.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/PaperResolvableProfile.java @@ -21,6 +21,7 @@ import net.kyori.adventure.text.object.PlayerHeadObjectContents; import net.minecraft.core.ClientAsset; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.ExtraCodecs; import net.minecraft.util.StringUtil; import net.minecraft.world.entity.player.PlayerModelType; import net.minecraft.world.entity.player.PlayerSkin; @@ -196,7 +197,7 @@ public ResolvableProfile.Builder addProperty(final ProfileProperty property) { final Property newProperty = new Property(property.getName(), property.getValue(), property.getSignature()); if (!this.propertyMap.containsEntry(property.getName(), newProperty)) { // underlying map is a multimap that doesn't allow duplicate key-value pair final int newSize = this.propertyMap.size() + 1; - Preconditions.checkArgument(newSize <= 16, "Cannot have more than 16 properties, was %s", newSize); + Preconditions.checkArgument(newSize <= ExtraCodecs.MAX_PROPERTIES, "Cannot have more than %s properties, was %s", ExtraCodecs.MAX_PROPERTIES, newSize); } this.propertyMap.put(property.getName(), newProperty); diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/attribute/PaperAttributeModifierDisplay.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/attribute/PaperAttributeModifierDisplay.java index 6b861fd930d6..aefe74ee717d 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/attribute/PaperAttributeModifierDisplay.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/attribute/PaperAttributeModifierDisplay.java @@ -8,8 +8,8 @@ public sealed interface PaperAttributeModifierDisplay permits PaperAttributeModi static AttributeModifierDisplay fromVanilla(ItemAttributeModifiers.Display display) { return switch (display) { - case ItemAttributeModifiers.Display.Default $ -> Default.INSTANCE; - case ItemAttributeModifiers.Display.Hidden $ -> Hidden.INSTANCE; + case ItemAttributeModifiers.Display.Default _ -> Default.INSTANCE; + case ItemAttributeModifiers.Display.Hidden _ -> Hidden.INSTANCE; case ItemAttributeModifiers.Display.OverrideText override -> new OverrideText(override); default -> throw new UnsupportedOperationException("Don't know how to convert " + display.getClass()); }; diff --git a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffect.java b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffect.java index a92a4a90e6f9..fe309c7f526e 100644 --- a/paper-server/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffect.java +++ b/paper-server/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffect.java @@ -14,7 +14,7 @@ public interface PaperConsumableEffect { static io.papermc.paper.datacomponent.item.consumable.ConsumeEffect fromVanilla(net.minecraft.world.item.consume_effects.ConsumeEffect consumable) { return switch (consumable) { case ApplyStatusEffectsConsumeEffect effect -> new PaperApplyStatusEffects(effect); - case ClearAllStatusEffectsConsumeEffect $ -> PaperClearAllStatusEffects.INSTANCE; + case ClearAllStatusEffectsConsumeEffect _ -> PaperClearAllStatusEffects.INSTANCE; case PlaySoundConsumeEffect effect -> new PaperPlaySound(effect); case RemoveStatusEffectsConsumeEffect effect -> new PaperRemoveStatusEffects(effect); case TeleportRandomlyConsumeEffect effect -> new PaperTeleportRandomly(effect); diff --git a/paper-server/src/main/java/io/papermc/paper/datapack/PaperDatapackManager.java b/paper-server/src/main/java/io/papermc/paper/datapack/PaperDatapackManager.java index ceb58966a3fd..c695d1012338 100644 --- a/paper-server/src/main/java/io/papermc/paper/datapack/PaperDatapackManager.java +++ b/paper-server/src/main/java/io/papermc/paper/datapack/PaperDatapackManager.java @@ -1,8 +1,7 @@ package io.papermc.paper.datapack; -import com.google.common.collect.Collections2; +import io.papermc.paper.util.MCUtil; import java.util.Collection; -import java.util.Collections; import java.util.function.Predicate; import net.minecraft.server.packs.repository.Pack; import net.minecraft.server.packs.repository.PackRepository; @@ -36,20 +35,15 @@ public void refreshPacks() { @Override public Collection getPacks() { final Collection enabledPacks = this.repository.getSelectedPacks(); - return this.transformPacks(this.repository.getAvailablePacks(), enabledPacks::contains); + return transformPacks(this.repository.getAvailablePacks(), enabledPacks::contains); } @Override public Collection getEnabledPacks() { - return this.transformPacks(this.repository.getSelectedPacks(), pack -> true); + return transformPacks(this.repository.getSelectedPacks(), _ -> true); } - private Collection transformPacks(final Collection packs, final Predicate enabled) { - return Collections.unmodifiableCollection( - Collections2.transform( - packs, - pack -> new PaperDatapack(pack, enabled.test(pack)) - ) - ); + private static Collection transformPacks(final Collection packs, final Predicate enabled) { + return MCUtil.transformUnmodifiable(packs, pack -> new PaperDatapack(pack, enabled.test(pack))); } } diff --git a/paper-server/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java b/paper-server/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java index 3200aef1ba5d..5741a60faba7 100644 --- a/paper-server/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java +++ b/paper-server/src/main/java/io/papermc/paper/plugin/PluginInitializerManager.java @@ -7,14 +7,11 @@ import io.papermc.paper.plugin.provider.PluginProvider; import io.papermc.paper.plugin.provider.type.paper.PaperPluginParent; import io.papermc.paper.plugin.provider.type.spigot.SpigotPluginProvider; -import io.papermc.paper.pluginremap.PluginRemapper; import java.util.Set; import java.util.TreeSet; -import java.util.function.Function; import joptsimple.OptionSet; import net.minecraft.server.dedicated.DedicatedServer; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.java.LibraryLoader; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -30,15 +27,10 @@ public class PluginInitializerManager { private static PluginInitializerManager impl; private final Path pluginDirectory; private final Path updateDirectory; - public final io.papermc.paper.pluginremap.@org.checkerframework.checker.nullness.qual.MonotonicNonNull PluginRemapper pluginRemapper; // Paper PluginInitializerManager(final Path pluginDirectory, final Path updateDirectory) { this.pluginDirectory = pluginDirectory; this.updateDirectory = updateDirectory; - this.pluginRemapper = Boolean.getBoolean("paper.disablePluginRemapping") - ? null - : PluginRemapper.create(pluginDirectory); - LibraryLoader.REMAPPER = this.pluginRemapper == null ? Function.identity() : this.pluginRemapper::remapLibraries; } private static PluginInitializerManager parse(@NotNull final OptionSet minecraftOptionSet) throws Exception { @@ -107,7 +99,6 @@ public static void load(OptionSet optionSet) throws Exception { LOGGER.info("Initializing plugins..."); // We have to load the bukkit configuration inorder to get the update folder location. io.papermc.paper.plugin.PluginInitializerManager pluginSystem = io.papermc.paper.plugin.PluginInitializerManager.init(optionSet); - if (pluginSystem.pluginRemapper != null) pluginSystem.pluginRemapper.loadingPlugins(); // Register the default plugin directory io.papermc.paper.plugin.util.EntrypointUtil.registerProvidersFromSource(io.papermc.paper.plugin.provider.source.DirectoryProviderSource.INSTANCE, pluginSystem.pluginDirectoryPath()); diff --git a/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java b/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java index 1240c061c121..f7e2c815e34a 100644 --- a/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java +++ b/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java @@ -1,6 +1,5 @@ package io.papermc.paper.plugin.entrypoint.classloader; -import io.papermc.paper.pluginremap.reflect.ReflectionRemapper; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; @@ -16,9 +15,6 @@ import java.util.jar.Attributes; import java.util.jar.Manifest; import org.checkerframework.checker.nullness.qual.Nullable; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; import static java.util.Objects.requireNonNullElse; @@ -45,16 +41,7 @@ public BytecodeModifyingURLClassLoader( final URL[] urls, final ClassLoader parent ) { - this(urls, parent, bytes -> { - final ClassReader classReader = new ClassReader(bytes); - final ClassWriter classWriter = new ClassWriter(classReader, 0); - final ClassVisitor visitor = ReflectionRemapper.visitor(classWriter); - if (visitor == classWriter) { - return bytes; - } - classReader.accept(visitor, 0); - return classWriter.toByteArray(); - }); + this(urls, parent, bytes -> bytes); } @Override @@ -157,7 +144,7 @@ private boolean isSealed(String name, Manifest man) { Manifest man = null; if (url.getProtocol().equals("jar")) { try { - final Object computedManifest = this.manifests.computeIfAbsent(jarName(url), $ -> { + final Object computedManifest = this.manifests.computeIfAbsent(jarName(url), _ -> { try { final Manifest m = ((JarURLConnection) url.openConnection()).getManifest(); return requireNonNullElse(m, MISSING_MANIFEST); diff --git a/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperClassloaderBytecodeModifier.java b/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperClassloaderBytecodeModifier.java index 0e734c07dbe8..f9a2c55a354c 100644 --- a/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperClassloaderBytecodeModifier.java +++ b/paper-server/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperClassloaderBytecodeModifier.java @@ -7,6 +7,6 @@ public class PaperClassloaderBytecodeModifier implements ClassloaderBytecodeModi @Override public byte[] modify(PluginMeta configuration, byte[] bytecode) { - return io.papermc.paper.pluginremap.reflect.ReflectionRemapper.processClass(bytecode); + return bytecode; } } diff --git a/paper-server/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java b/paper-server/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java index ce808520d639..9146f18a6a8e 100644 --- a/paper-server/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java +++ b/paper-server/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java @@ -43,7 +43,7 @@ public boolean blocksPluginReloading() { } public void callEvent(final LifecycleEventType eventType, final E event) { - this.callEvent(eventType, event, $ -> true); + this.callEvent(eventType, event, _ -> true); } public void callEvent(final LifecycleEventType eventType, final E event, final Predicate ownerPredicate) { diff --git a/paper-server/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java b/paper-server/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java index 21a0a4e29c0e..4bef544df4dc 100644 --- a/paper-server/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java +++ b/paper-server/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java @@ -1,13 +1,10 @@ package io.papermc.paper.plugin.loader; -import io.papermc.paper.plugin.PluginInitializerManager; import io.papermc.paper.plugin.bootstrap.PluginProviderContext; -import io.papermc.paper.plugin.entrypoint.classloader.BytecodeModifyingURLClassLoader; import io.papermc.paper.plugin.entrypoint.classloader.PaperPluginClassLoader; import io.papermc.paper.plugin.loader.library.ClassPathLibrary; import io.papermc.paper.plugin.loader.library.PaperLibraryStore; import io.papermc.paper.plugin.provider.configuration.PaperPluginMeta; -import io.papermc.paper.util.MappingEnvironment; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; @@ -41,7 +38,7 @@ public PaperClasspathBuilder(PluginProviderContext context) { } public PaperPluginClassLoader buildClassLoader(Logger logger, Path source, JarFile jarFile, PaperPluginMeta configuration) { - List paths = this.buildLibraryPaths(true); + List paths = this.buildLibraryPaths(); URL[] urls = new URL[paths.size()]; for (int i = 0; i < paths.size(); i++) { Path path = paths.get(i); @@ -53,25 +50,19 @@ public PaperPluginClassLoader buildClassLoader(Logger logger, Path source, JarFi } try { - final URLClassLoader libraryLoader = MappingEnvironment.DISABLE_PLUGIN_REMAPPING - ? new URLClassLoader(urls, this.getClass().getClassLoader()) - : new BytecodeModifyingURLClassLoader(urls, this.getClass().getClassLoader()); + final URLClassLoader libraryLoader = new URLClassLoader(urls, this.getClass().getClassLoader()); return new PaperPluginClassLoader(logger, source, jarFile, configuration, this.getClass().getClassLoader(), libraryLoader); } catch (IOException exception) { throw new RuntimeException(exception); } } - public List buildLibraryPaths(final boolean remap) { + public List buildLibraryPaths() { PaperLibraryStore paperLibraryStore = new PaperLibraryStore(); for (ClassPathLibrary library : this.libraries) { library.register(paperLibraryStore); } - List paths = paperLibraryStore.getPaths(); - if (remap && PluginInitializerManager.instance().pluginRemapper != null) { - paths = PluginInitializerManager.instance().pluginRemapper.remapLibraries(paths); - } - return paths; + return paperLibraryStore.getPaths(); } } diff --git a/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/DirectoryProviderSource.java b/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/DirectoryProviderSource.java index 00dbd17a2378..b6dcbd88ea31 100644 --- a/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/DirectoryProviderSource.java +++ b/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/DirectoryProviderSource.java @@ -18,7 +18,7 @@ public class DirectoryProviderSource implements ProviderSource> public static final DirectoryProviderSource INSTANCE = new DirectoryProviderSource(true); public static final DirectoryProviderSource INSTANCE_NO_CREATE = new DirectoryProviderSource(false); - private static final FileProviderSource FILE_PROVIDER_SOURCE = new FileProviderSource("Directory '%s'"::formatted, false); // Paper - Remap plugins + private static final FileProviderSource FILE_PROVIDER_SOURCE = new FileProviderSource("Directory '%s'"::formatted); private static final Logger LOGGER = LogUtils.getClassLogger(); private final boolean createDirectory; @@ -48,11 +48,6 @@ public List prepareContext(Path context) throws IOException { LOGGER.error("Error preparing plugin context: " + e.getMessage(), e); } }); - // Paper start - Remap plugins - if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) { - return io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.rewritePluginDirectory(files); - } - // Paper end - Remap plugins return files; } diff --git a/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/FileProviderSource.java b/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/FileProviderSource.java index a0b84535a9d3..9661f033bbb2 100644 --- a/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/FileProviderSource.java +++ b/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/FileProviderSource.java @@ -28,15 +28,9 @@ public class FileProviderSource implements ProviderSource { private static final Logger LOGGER = LogUtils.getClassLogger(); private final Function contextChecker; - private final boolean applyRemap; - - public FileProviderSource(Function contextChecker, boolean applyRemap) { - this.contextChecker = contextChecker; - this.applyRemap = applyRemap; - } public FileProviderSource(Function contextChecker) { - this(contextChecker, true); + this.contextChecker = contextChecker; } @Override @@ -60,11 +54,6 @@ public Path prepareContext(Path context) throws IOException { } catch (Exception exception) { throw new RuntimeException(source + " failed to update!", exception); } - // Paper start - Remap plugins - if (this.applyRemap && io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) { - context = io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.rewritePlugin(context); - } - // Paper end - Remap plugins return context; } diff --git a/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/PluginFlagProviderSource.java b/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/PluginFlagProviderSource.java index c2b60c745135..ac55ae0e3011 100644 --- a/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/PluginFlagProviderSource.java +++ b/paper-server/src/main/java/io/papermc/paper/plugin/provider/source/PluginFlagProviderSource.java @@ -14,7 +14,7 @@ public class PluginFlagProviderSource implements ProviderSource, List> { public static final PluginFlagProviderSource INSTANCE = new PluginFlagProviderSource(); - private static final FileProviderSource FILE_PROVIDER_SOURCE = new FileProviderSource("File '%s' specified through 'add-plugin' argument"::formatted, false); + private static final FileProviderSource FILE_PROVIDER_SOURCE = new FileProviderSource("File '%s' specified through 'add-plugin' argument"::formatted); private static final Logger LOGGER = LogUtils.getClassLogger(); @Override @@ -27,11 +27,6 @@ public List prepareContext(List context) { LOGGER.error("Error preparing plugin context: " + e.getMessage(), e); } } - // Paper start - Remap plugins - if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null && !files.isEmpty()) { - return io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.rewriteExtraPlugins(files); - } - // Paper end - Remap plugins return files; } diff --git a/paper-server/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java b/paper-server/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java index 9edf79dffd28..5c40c3d0ea8e 100644 --- a/paper-server/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java +++ b/paper-server/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java @@ -2,20 +2,17 @@ import com.destroystokyo.paper.utils.PaperPluginLogger; import io.papermc.paper.plugin.bootstrap.PluginProviderContextImpl; -import io.papermc.paper.plugin.entrypoint.classloader.BytecodeModifyingURLClassLoader; import io.papermc.paper.plugin.entrypoint.classloader.PaperSimplePluginClassLoader; import io.papermc.paper.plugin.loader.PaperClasspathBuilder; import io.papermc.paper.plugin.loader.PluginLoader; import io.papermc.paper.plugin.provider.configuration.serializer.constraints.PluginConfigConstraints; import io.papermc.paper.plugin.provider.type.PluginTypeFactory; import io.papermc.paper.plugin.provider.util.ProviderUtil; -import io.papermc.paper.util.MappingEnvironment; import java.util.List; import java.util.logging.Logger; import net.kyori.adventure.text.logger.slf4j.ComponentLogger; import org.bukkit.plugin.InvalidDescriptionException; import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.java.LibraryLoader; import org.yaml.snakeyaml.error.YAMLException; import java.io.IOException; @@ -27,12 +24,6 @@ class SpigotPluginProviderFactory implements PluginTypeFactory { - static { - if (!MappingEnvironment.DISABLE_PLUGIN_REMAPPING) { - LibraryLoader.LIBRARY_LOADER_FACTORY = BytecodeModifyingURLClassLoader::new; - } - } - @Override public SpigotPluginProvider build(JarFile file, PluginDescriptionFile configuration, Path source) throws InvalidDescriptionException { // Copied from SimplePluginManager#loadPlugins @@ -61,7 +52,7 @@ public SpigotPluginProvider build(JarFile file, PluginDescriptionFile configurat throw new RuntimeException(e); } - paperLibraryPaths = builder.buildLibraryPaths(false); + paperLibraryPaths = builder.buildLibraryPaths(); } else { paperLibraryPaths = null; } diff --git a/paper-server/src/main/java/io/papermc/paper/pluginremap/DebugLogger.java b/paper-server/src/main/java/io/papermc/paper/pluginremap/DebugLogger.java deleted file mode 100644 index 99e658e3a0f0..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/pluginremap/DebugLogger.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.papermc.paper.pluginremap; - -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.function.Consumer; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.framework.qual.DefaultQualifier; - -/** - * {@link PrintWriter}-backed logger implementation for use with {@link net.neoforged.art.api.Renamer} which - * only opens the backing writer and logs messages when the {@link PluginRemapper#DEBUG_LOGGING} system property - * is set to true. - */ -@DefaultQualifier(NonNull.class) -final class DebugLogger implements Consumer, AutoCloseable { - private final @Nullable PrintWriter writer; - - DebugLogger(final Path logFile) { - try { - this.writer = createWriter(logFile); - } catch (final IOException ex) { - throw new RuntimeException("Failed to initialize DebugLogger for file '" + logFile + "'", ex); - } - } - - @Override - public void accept(final String line) { - this.useWriter(writer -> writer.println(line)); - } - - @Override - public void close() { - this.useWriter(PrintWriter::close); - } - - private void useWriter(final Consumer op) { - final @Nullable PrintWriter writer = this.writer; - if (writer != null) { - op.accept(writer); - } - } - - Consumer debug() { - return line -> this.accept("[debug]: " + line); - } - - static DebugLogger forOutputFile(final Path outputFile) { - return new DebugLogger(outputFile.resolveSibling(outputFile.getFileName() + ".log")); - } - - private static @Nullable PrintWriter createWriter(final Path logFile) throws IOException { - if (!PluginRemapper.DEBUG_LOGGING) { - return null; - } - if (!Files.exists(logFile.getParent())) { - Files.createDirectories(logFile.getParent()); - } - return new PrintWriter(logFile.toFile()); - } -} diff --git a/paper-server/src/main/java/io/papermc/paper/pluginremap/InsertManifestAttribute.java b/paper-server/src/main/java/io/papermc/paper/pluginremap/InsertManifestAttribute.java deleted file mode 100644 index d738b31f0005..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/pluginremap/InsertManifestAttribute.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.papermc.paper.pluginremap; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.jar.Attributes; -import java.util.jar.Manifest; -import net.neoforged.art.api.Transformer; - -final class InsertManifestAttribute implements Transformer { - static final String PAPERWEIGHT_NAMESPACE_MANIFEST_KEY = "paperweight-mappings-namespace"; - static final String MOJANG_NAMESPACE = "mojang"; - static final String MOJANG_PLUS_YARN_NAMESPACE = "mojang+yarn"; - static final String SPIGOT_NAMESPACE = "spigot"; - static final Set KNOWN_NAMESPACES = Set.of(MOJANG_NAMESPACE, MOJANG_PLUS_YARN_NAMESPACE, SPIGOT_NAMESPACE); - - private final String mainAttributesKey; - private final String namespace; - private final boolean createIfMissing; - private volatile boolean visitedManifest = false; - - static Transformer addNamespaceManifestAttribute(final String namespace) { - return new InsertManifestAttribute(PAPERWEIGHT_NAMESPACE_MANIFEST_KEY, namespace, true); - } - - InsertManifestAttribute( - final String mainAttributesKey, - final String namespace, - final boolean createIfMissing - ) { - this.mainAttributesKey = mainAttributesKey; - this.namespace = namespace; - this.createIfMissing = createIfMissing; - } - - @Override - public ManifestEntry process(final ManifestEntry entry) { - this.visitedManifest = true; - try { - final Manifest manifest = new Manifest(new ByteArrayInputStream(entry.getData())); - manifest.getMainAttributes().putValue(this.mainAttributesKey, this.namespace); - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - manifest.write(out); - return ManifestEntry.create(Entry.STABLE_TIMESTAMP, out.toByteArray()); - } catch (final IOException e) { - throw new RuntimeException("Failed to modify manifest", e); - } - } - - @Override - public Collection getExtras() { - if (!this.visitedManifest && this.createIfMissing) { - final Manifest manifest = new Manifest(); - manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); - manifest.getMainAttributes().putValue(this.mainAttributesKey, this.namespace); - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - try { - manifest.write(out); - } catch (final IOException e) { - throw new RuntimeException("Failed to write manifest", e); - } - return List.of(ManifestEntry.create(Entry.STABLE_TIMESTAMP, out.toByteArray())); - } - return Transformer.super.getExtras(); - } -} diff --git a/paper-server/src/main/java/io/papermc/paper/pluginremap/PluginRemapper.java b/paper-server/src/main/java/io/papermc/paper/pluginremap/PluginRemapper.java deleted file mode 100644 index fbad4a2242aa..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/pluginremap/PluginRemapper.java +++ /dev/null @@ -1,442 +0,0 @@ -package io.papermc.paper.pluginremap; - -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.mojang.logging.LogUtils; -import io.papermc.paper.plugin.provider.type.PluginFileType; -import io.papermc.paper.util.AtomicFiles; -import io.papermc.paper.util.MappingEnvironment; -import io.papermc.paper.util.concurrent.ScalingThreadPool; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.jar.Manifest; -import java.util.stream.Stream; -import net.minecraft.DefaultUncaughtExceptionHandlerWithName; -import net.minecraft.util.ExceptionCollector; -import net.neoforged.art.api.Renamer; -import net.neoforged.art.api.SignatureStripperConfig; -import net.neoforged.art.api.Transformer; -import net.neoforged.srgutils.IMappingFile; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.framework.qual.DefaultQualifier; -import org.slf4j.Logger; - -import static io.papermc.paper.pluginremap.InsertManifestAttribute.addNamespaceManifestAttribute; - -@DefaultQualifier(NonNull.class) -public final class PluginRemapper { - public static final boolean DEBUG_LOGGING = Boolean.getBoolean("Paper.PluginRemapperDebug"); - private static final String PAPER_REMAPPED = ".paper-remapped"; - private static final String UNKNOWN_ORIGIN = "unknown-origin"; - private static final String LIBRARIES = "libraries"; - private static final String EXTRA_PLUGINS = "extra-plugins"; - private static final String REMAP_CLASSPATH = "remap-classpath"; - private static final String REVERSED_MAPPINGS = "mappings/reversed"; - private static final Logger LOGGER = LogUtils.getClassLogger(); - - private final ExecutorService threadPool; - private final ReobfServer reobf; - private final RemappedPluginIndex remappedPlugins; - private final RemappedPluginIndex extraPlugins; - private final UnknownOriginRemappedPluginIndex unknownOrigin; - private final UnknownOriginRemappedPluginIndex libraries; - private @Nullable CompletableFuture reversedMappings; - - public PluginRemapper(final Path pluginsDir) { - this.threadPool = createThreadPool(); - final CompletableFuture mappings = CompletableFuture.supplyAsync(PluginRemapper::loadReobfMappings, this.threadPool); - final Path remappedPlugins = pluginsDir.resolve(PAPER_REMAPPED); - this.reversedMappings = this.reversedMappingsFuture(() -> mappings, remappedPlugins, this.threadPool); - this.reobf = new ReobfServer(remappedPlugins.resolve(REMAP_CLASSPATH), mappings, this.threadPool); - this.remappedPlugins = new RemappedPluginIndex(remappedPlugins, false); - this.extraPlugins = new RemappedPluginIndex(this.remappedPlugins.dir().resolve(EXTRA_PLUGINS), true); - this.unknownOrigin = new UnknownOriginRemappedPluginIndex(this.remappedPlugins.dir().resolve(UNKNOWN_ORIGIN)); - this.libraries = new UnknownOriginRemappedPluginIndex(this.remappedPlugins.dir().resolve(LIBRARIES)); - } - - public static @Nullable PluginRemapper create(final Path pluginsDir) { - if (MappingEnvironment.reobf() || !MappingEnvironment.hasMappings()) { - return null; - } - - try { - return new PluginRemapper(pluginsDir); - } catch (final Exception e) { - throw new RuntimeException("Failed to create PluginRemapper, try deleting the '" + pluginsDir.resolve(PAPER_REMAPPED) + "' directory", e); - } - } - - public void shutdown() { - this.threadPool.shutdown(); - this.save(true); - boolean didShutdown; - try { - didShutdown = this.threadPool.awaitTermination(3L, TimeUnit.SECONDS); - } catch (final InterruptedException ex) { - didShutdown = false; - } - if (!didShutdown) { - this.threadPool.shutdownNow(); - } - } - - public void save(final boolean clean) { - this.remappedPlugins.write(); - this.extraPlugins.write(); - this.unknownOrigin.write(clean); - this.libraries.write(clean); - } - - // Called on startup and reload - public void loadingPlugins() { - if (this.reversedMappings == null) { - this.reversedMappings = this.reversedMappingsFuture( - () -> CompletableFuture.supplyAsync(PluginRemapper::loadReobfMappings, this.threadPool), - this.remappedPlugins.dir(), - this.threadPool - ); - } - } - - // Called after all plugins enabled during startup/reload - public void pluginsEnabled() { - this.reversedMappings = null; - this.save(false); - } - - public List remapLibraries(final List libraries) { - final List> tasks = new ArrayList<>(); - for (final Path lib : libraries) { - if (!lib.getFileName().toString().endsWith(".jar")) { - if (DEBUG_LOGGING) { - LOGGER.info("Library '{}' is not a jar.", lib); - } - tasks.add(CompletableFuture.completedFuture(lib)); - continue; - } - final @Nullable Path cached = this.libraries.getIfPresent(lib); - if (cached != null) { - if (DEBUG_LOGGING) { - LOGGER.info("Library '{}' has not changed since last remap.", lib); - } - tasks.add(CompletableFuture.completedFuture(cached)); - continue; - } - tasks.add(this.remapLibrary(this.libraries, lib)); - } - return waitForAll(tasks); - } - - public Path rewritePlugin(final Path plugin) { - // Already remapped - if (plugin.getParent().equals(this.remappedPlugins.dir()) - || plugin.getParent().equals(this.extraPlugins.dir())) { - return plugin; - } - - final @Nullable Path cached = this.unknownOrigin.getIfPresent(plugin); - if (cached != null) { - if (DEBUG_LOGGING) { - LOGGER.info("Plugin '{}' has not changed since last remap.", plugin); - } - return cached; - } - - return this.remapPlugin(this.unknownOrigin, plugin).join(); - } - - public List rewriteExtraPlugins(final List plugins) { - final @Nullable List allCached = this.extraPlugins.getAllIfPresent(plugins); - if (allCached != null) { - if (DEBUG_LOGGING) { - LOGGER.info("All extra plugins have a remapped variant cached."); - } - return allCached; - } - - final List> tasks = new ArrayList<>(); - for (final Path file : plugins) { - final @Nullable Path cached = this.extraPlugins.getIfPresent(file); - if (cached != null) { - if (DEBUG_LOGGING) { - LOGGER.info("Extra plugin '{}' has not changed since last remap.", file); - } - tasks.add(CompletableFuture.completedFuture(cached)); - continue; - } - tasks.add(this.remapPlugin(this.extraPlugins, file)); - } - return waitForAll(tasks); - } - - public List rewritePluginDirectory(final List jars) { - final @Nullable List remappedJars = this.remappedPlugins.getAllIfPresent(jars); - if (remappedJars != null) { - if (DEBUG_LOGGING) { - LOGGER.info("All plugins have a remapped variant cached."); - } - return remappedJars; - } - - final List> tasks = new ArrayList<>(); - for (final Path file : jars) { - final @Nullable Path existingFile = this.remappedPlugins.getIfPresent(file); - if (existingFile != null) { - if (DEBUG_LOGGING) { - LOGGER.info("Plugin '{}' has not changed since last remap.", file); - } - tasks.add(CompletableFuture.completedFuture(existingFile)); - continue; - } - - tasks.add(this.remapPlugin(this.remappedPlugins, file)); - } - return waitForAll(tasks); - } - - private static IMappingFile reverse(final IMappingFile mappings) { - if (DEBUG_LOGGING) { - LOGGER.info("Reversing mappings..."); - } - final long start = System.currentTimeMillis(); - final IMappingFile reversed = mappings.reverse(); - if (DEBUG_LOGGING) { - LOGGER.info("Done reversing mappings in {}ms.", System.currentTimeMillis() - start); - } - return reversed; - } - - private CompletableFuture reversedMappingsFuture( - final Supplier> mappingsFuture, - final Path remappedPlugins, - final Executor executor - ) { - return CompletableFuture.supplyAsync(() -> { - try { - final String mappingsHash = MappingEnvironment.mappingsHash(); - final String fName = mappingsHash + ".tiny"; - final Path reversedMappings1 = remappedPlugins.resolve(REVERSED_MAPPINGS); - final Path file = reversedMappings1.resolve(fName); - if (Files.isDirectory(reversedMappings1)) { - if (Files.isRegularFile(file)) { - return CompletableFuture.completedFuture( - loadMappings("Reversed", Files.newInputStream(file)) - ); - } else { - for (final Path oldFile : list(reversedMappings1, Files::isRegularFile)) { - Files.delete(oldFile); - } - } - } else { - Files.createDirectories(reversedMappings1); - } - return mappingsFuture.get().thenApply(loadedMappings -> { - final IMappingFile reversed = reverse(loadedMappings); - try { - AtomicFiles.atomicWrite(file, writeTo -> { - reversed.write(writeTo, IMappingFile.Format.TINY, false); - }); - } catch (final IOException e) { - throw new RuntimeException("Failed to write reversed mappings", e); - } - return reversed; - }); - } catch (final IOException e) { - throw new RuntimeException("Failed to load reversed mappings", e); - } - }, executor).thenCompose(f -> f); - } - - private CompletableFuture remapPlugin( - final RemappedPluginIndex index, - final Path inputFile - ) { - return this.remap(index, inputFile, false); - } - - private CompletableFuture remapLibrary( - final RemappedPluginIndex index, - final Path inputFile - ) { - return this.remap(index, inputFile, true); - } - - /** - * Returns the remapped file if remapping was necessary, otherwise null. - * - * @param index remapped plugin index - * @param inputFile input file - * @return remapped file, or inputFile if no remapping was necessary - */ - private CompletableFuture remap( - final RemappedPluginIndex index, - final Path inputFile, - final boolean library - ) { - final Path destination = index.input(inputFile); - - try (final FileSystem fs = FileSystems.newFileSystem(inputFile, new HashMap<>())) { - // Leave dummy files if no remapping is required, so that we can check if they exist without copying the whole file - final Path manifestPath = fs.getPath("META-INF/MANIFEST.MF"); - final @Nullable String ns; - if (Files.exists(manifestPath)) { - final Manifest manifest; - try (final InputStream in = new BufferedInputStream(Files.newInputStream(manifestPath))) { - manifest = new Manifest(in); - } - ns = manifest.getMainAttributes().getValue(InsertManifestAttribute.PAPERWEIGHT_NAMESPACE_MANIFEST_KEY); - } else { - ns = null; - } - - if (ns != null && !InsertManifestAttribute.KNOWN_NAMESPACES.contains(ns)) { - throw new RuntimeException("Failed to remap plugin " + inputFile + " with unknown mapping namespace '" + ns + "'"); - } - - final boolean mojangMappedManifest = ns != null && (ns.equals(InsertManifestAttribute.MOJANG_NAMESPACE) || ns.equals(InsertManifestAttribute.MOJANG_PLUS_YARN_NAMESPACE)); - if (library) { - if (mojangMappedManifest) { - if (DEBUG_LOGGING) { - LOGGER.info("Library '{}' is already Mojang mapped.", inputFile); - } - index.skip(inputFile); - return CompletableFuture.completedFuture(inputFile); - } else if (ns == null) { - if (DEBUG_LOGGING) { - LOGGER.info("Library '{}' does not specify a mappings namespace (not remapping).", inputFile); - } - index.skip(inputFile); - return CompletableFuture.completedFuture(inputFile); - } - } else { - if (mojangMappedManifest) { - if (DEBUG_LOGGING) { - LOGGER.info("Plugin '{}' is already Mojang mapped.", inputFile); - } - index.skip(inputFile); - return CompletableFuture.completedFuture(inputFile); - } else if (ns == null && Files.exists(fs.getPath(PluginFileType.PAPER_PLUGIN_YML))) { - if (DEBUG_LOGGING) { - LOGGER.info("Plugin '{}' is a Paper plugin with no namespace specified.", inputFile); - } - index.skip(inputFile); - return CompletableFuture.completedFuture(inputFile); - } - } - } catch (final IOException ex) { - return CompletableFuture.failedFuture(new RuntimeException("Failed to open plugin jar " + inputFile, ex)); - } - - return this.reobf.remapped().thenApplyAsync(reobfServer -> { - LOGGER.info("Remapping {} '{}'...", library ? "library" : "plugin", inputFile); - final long start = System.currentTimeMillis(); - try (final DebugLogger logger = DebugLogger.forOutputFile(destination)) { - try (final Renamer renamer = Renamer.builder() - .add(Transformer.renamerFactory(this.mappings(), false)) - .add(addNamespaceManifestAttribute(InsertManifestAttribute.MOJANG_PLUS_YARN_NAMESPACE)) - .add(Transformer.signatureStripperFactory(SignatureStripperConfig.ALL)) - .lib(reobfServer.toFile()) - .threads(1) - .logger(logger) - .debug(logger.debug()) - .build()) { - renamer.run(inputFile.toFile(), destination.toFile()); - } - } catch (final Exception ex) { - throw new RuntimeException("Failed to remap plugin jar '" + inputFile + "'", ex); - } - LOGGER.info("Done remapping {} '{}' in {}ms.", library ? "library" : "plugin", inputFile, System.currentTimeMillis() - start); - return destination; - }, this.threadPool); - } - - private IMappingFile mappings() { - final @Nullable CompletableFuture mappings = this.reversedMappings; - if (mappings == null) { - return this.reversedMappingsFuture( - () -> CompletableFuture.supplyAsync(PluginRemapper::loadReobfMappings, Runnable::run), - this.remappedPlugins.dir(), - Runnable::run - ).join(); - } - return mappings.join(); - } - - private static IMappingFile loadReobfMappings() { - return loadMappings("Reobf", MappingEnvironment.mappingsStream()); - } - - private static IMappingFile loadMappings(final String name, final InputStream stream) { - try (stream) { - if (DEBUG_LOGGING) { - LOGGER.info("Loading {} mappings...", name); - } - final long start = System.currentTimeMillis(); - final IMappingFile load = IMappingFile.load(stream); - if (DEBUG_LOGGING) { - LOGGER.info("Done loading {} mappings in {}ms.", name, System.currentTimeMillis() - start); - } - return load; - } catch (final IOException ex) { - throw new RuntimeException("Failed to load " + name + " mappings", ex); - } - } - - static List list(final Path dir, final Predicate filter) { - try (final Stream stream = Files.list(dir)) { - return stream.filter(filter).toList(); - } catch (final IOException ex) { - throw new RuntimeException("Failed to list directory '" + dir + "'", ex); - } - } - - private static List waitForAll(final List> tasks) { - final ExceptionCollector collector = new ExceptionCollector<>(); - final List ret = new ArrayList<>(); - for (final CompletableFuture task : tasks) { - try { - ret.add(task.join()); - } catch (final CompletionException ex) { - collector.add(ex); - } - } - try { - collector.throwIfPresent(); - } catch (final Exception ex) { - // Don't hard fail during bootstrap/plugin loading. The plugin(s) in question will be skipped - LOGGER.error("Encountered exception remapping plugins", ex); - } - return ret; - } - - private static ThreadPoolExecutor createThreadPool() { - return new ThreadPoolExecutor( - 0, - 4, - 5L, - TimeUnit.SECONDS, - ScalingThreadPool.createUnboundedQueue(), - new ThreadFactoryBuilder() - .setNameFormat("Paper Plugin Remapper Thread - %1$d") - .setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(LOGGER)) - .build(), - ScalingThreadPool.defaultReEnqueuePolicy() - ); - } -} diff --git a/paper-server/src/main/java/io/papermc/paper/pluginremap/RemappedPluginIndex.java b/paper-server/src/main/java/io/papermc/paper/pluginremap/RemappedPluginIndex.java deleted file mode 100644 index 1a2e8902c060..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/pluginremap/RemappedPluginIndex.java +++ /dev/null @@ -1,217 +0,0 @@ -package io.papermc.paper.pluginremap; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.mojang.logging.LogUtils; -import io.papermc.paper.util.Hashing; -import io.papermc.paper.util.MappingEnvironment; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.framework.qual.DefaultQualifier; -import org.slf4j.Logger; -import org.spongepowered.configurate.loader.AtomicFiles; - -@DefaultQualifier(NonNull.class) -class RemappedPluginIndex { - private static final Logger LOGGER = LogUtils.getLogger(); - private static final Gson GSON = new GsonBuilder() - .setPrettyPrinting() - .create(); - private static final String INDEX_FILE_NAME = "index.json"; - - protected final State state; - private final Path dir; - private final Path indexFile; - private final boolean handleDuplicateFileNames; - - // todo maybe hash remapped variants to ensure they haven't changed? probably unneeded - static final class State { - final Map hashes = new HashMap<>(); - final Set skippedHashes = new HashSet<>(); - private final String mappingsHash = MappingEnvironment.mappingsHash(); - } - - RemappedPluginIndex(final Path dir, final boolean handleDuplicateFileNames) { - this.dir = dir; - this.handleDuplicateFileNames = handleDuplicateFileNames; - if (!Files.exists(this.dir)) { - try { - Files.createDirectories(this.dir); - } catch (final IOException ex) { - throw new UncheckedIOException(ex); - } - } - - this.indexFile = dir.resolve(INDEX_FILE_NAME); - if (Files.isRegularFile(this.indexFile)) { - this.state = this.readIndex(); - } else { - this.state = new State(); - } - } - - private State readIndex() { - final State state; - try (final BufferedReader reader = Files.newBufferedReader(this.indexFile)) { - state = GSON.fromJson(reader, State.class); - } catch (final Exception ex) { - throw new RuntimeException("Failed to read index file '" + this.indexFile + "'", ex); - } - - // If mappings have changed, delete all cached files and create a new index - if (!state.mappingsHash.equals(MappingEnvironment.mappingsHash())) { - for (final String fileName : state.hashes.values()) { - final Path path = this.dir.resolve(fileName); - try { - Files.deleteIfExists(path); - } catch (final IOException ex) { - throw new UncheckedIOException("Failed to delete no longer needed file '" + path + "'", ex); - } - } - return new State(); - } - return state; - } - - Path dir() { - return this.dir; - } - - /** - * Returns a list of cached paths if all of the input paths are present in the cache. - * The returned list may contain paths from different directories. - * - * @param paths plugin jar paths to check - * @return null if any of the paths are not present in the cache, otherwise a list of the cached paths - */ - @Nullable List getAllIfPresent(final List paths) { - final Map hashCache = new HashMap<>(); - final Function inputFileHash = path -> hashCache.computeIfAbsent(path, Hashing::sha256); - - // Delete cached entries we no longer need - final Iterator> iterator = this.state.hashes.entrySet().iterator(); - while (iterator.hasNext()) { - final Map.Entry entry = iterator.next(); - final String inputHash = entry.getKey(); - final String fileName = entry.getValue(); - if (paths.stream().anyMatch(path -> inputFileHash.apply(path).equals(inputHash))) { - // Hash is used, keep it - continue; - } - - iterator.remove(); - final Path filePath = this.dir.resolve(fileName); - try { - Files.deleteIfExists(filePath); - } catch (final IOException ex) { - throw new UncheckedIOException("Failed to delete no longer needed file '" + filePath + "'", ex); - } - } - - // Also clear hashes of skipped files - this.state.skippedHashes.removeIf(hash -> paths.stream().noneMatch(path -> inputFileHash.apply(path).equals(hash))); - - final List ret = new ArrayList<>(); - for (final Path path : paths) { - final String inputHash = inputFileHash.apply(path); - if (this.state.skippedHashes.contains(inputHash)) { - // Add the original path - ret.add(path); - continue; - } - - final @Nullable Path cached = this.getIfPresent(inputHash); - if (cached == null) { - // Missing the remapped file - return null; - } - ret.add(cached); - } - return ret; - } - - private String createCachedFileName(final Path in) { - if (this.handleDuplicateFileNames) { - final String fileName = in.getFileName().toString(); - final int i = fileName.lastIndexOf(".jar"); - return fileName.substring(0, i) + "-" + System.currentTimeMillis() + ".jar"; - } - return in.getFileName().toString(); - } - - /** - * Returns the given path if the file was previously skipped for being remapped, otherwise the cached path or null. - * - * @param in input file - * @return {@code in} if already remapped, the cached path if present, otherwise null - */ - @Nullable Path getIfPresent(final Path in) { - final String inHash = Hashing.sha256(in); - if (this.state.skippedHashes.contains(inHash)) { - return in; - } - return this.getIfPresent(inHash); - } - - /** - * Returns the cached path if a remapped file is present for the given hash, otherwise null. - * - * @param inHash hash of the input file - * @return the cached path if present, otherwise null - * @see #getIfPresent(Path) - */ - protected @Nullable Path getIfPresent(final String inHash) { - final @Nullable String fileName = this.state.hashes.get(inHash); - if (fileName == null) { - return null; - } - - final Path path = this.dir.resolve(fileName); - if (Files.exists(path)) { - return path; - } - return null; - } - - Path input(final Path in) { - return this.input(in, Hashing.sha256(in)); - } - - /** - * Marks the given file as skipped for remapping. - * - * @param in input file - */ - void skip(final Path in) { - this.state.skippedHashes.add(Hashing.sha256(in)); - } - - protected Path input(final Path in, final String hashString) { - final String name = this.createCachedFileName(in); - this.state.hashes.put(hashString, name); - return this.dir.resolve(name); - } - - void write() { - try (final BufferedWriter writer = AtomicFiles.atomicBufferedWriter(this.indexFile, StandardCharsets.UTF_8)) { - GSON.toJson(this.state, writer); - } catch (final IOException ex) { - LOGGER.warn("Failed to write index file '{}'", this.indexFile, ex); - } - } -} diff --git a/paper-server/src/main/java/io/papermc/paper/pluginremap/ReobfServer.java b/paper-server/src/main/java/io/papermc/paper/pluginremap/ReobfServer.java deleted file mode 100644 index aa5bf7ae042f..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/pluginremap/ReobfServer.java +++ /dev/null @@ -1,92 +0,0 @@ -package io.papermc.paper.pluginremap; - -import com.mojang.logging.LogUtils; -import io.papermc.paper.util.AtomicFiles; -import io.papermc.paper.util.MappingEnvironment; -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import net.neoforged.art.api.Renamer; -import net.neoforged.art.api.Transformer; -import net.neoforged.art.internal.RenamerImpl; -import net.neoforged.srgutils.IMappingFile; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.framework.qual.DefaultQualifier; -import org.slf4j.Logger; - -import static io.papermc.paper.pluginremap.InsertManifestAttribute.addNamespaceManifestAttribute; - -@DefaultQualifier(NonNull.class) -final class ReobfServer { - private static final Logger LOGGER = LogUtils.getClassLogger(); - - private final Path remapClasspathDir; - private final CompletableFuture load; - - ReobfServer(final Path remapClasspathDir, final CompletableFuture mappings, final Executor executor) { - this.remapClasspathDir = remapClasspathDir; - if (this.mappingsChanged()) { - this.load = mappings.thenAcceptAsync(this::remap, executor); - } else { - if (PluginRemapper.DEBUG_LOGGING) { - LOGGER.info("Have cached reobf server for current mappings."); - } - this.load = CompletableFuture.completedFuture(null); - } - } - - CompletableFuture remapped() { - return this.load.thenApply($ -> this.remappedPath()); - } - - private Path remappedPath() { - return this.remapClasspathDir.resolve(MappingEnvironment.mappingsHash() + ".jar"); - } - - private boolean mappingsChanged() { - return !Files.exists(this.remappedPath()); - } - - private void remap(final IMappingFile mappings) { - try { - if (!Files.exists(this.remapClasspathDir)) { - Files.createDirectories(this.remapClasspathDir); - } - for (final Path file : PluginRemapper.list(this.remapClasspathDir, Files::isRegularFile)) { - Files.delete(file); - } - } catch (final IOException ex) { - throw new RuntimeException(ex); - } - - LOGGER.info("Remapping server..."); - final long startRemap = System.currentTimeMillis(); - try (final DebugLogger log = DebugLogger.forOutputFile(this.remappedPath())) { - AtomicFiles.atomicWrite(this.remappedPath(), writeTo -> { - try (final RenamerImpl renamer = (RenamerImpl) Renamer.builder() - .logger(log) - .debug(log.debug()) - .threads(1) - .add(Transformer.renamerFactory(mappings, false)) - .add(addNamespaceManifestAttribute(InsertManifestAttribute.SPIGOT_NAMESPACE)) - .build()) { - renamer.run(serverJar().toFile(), writeTo.toFile(), true); - } - }); - } catch (final Exception ex) { - throw new RuntimeException("Failed to remap server jar", ex); - } - LOGGER.info("Done remapping server in {}ms.", System.currentTimeMillis() - startRemap); - } - - private static Path serverJar() { - try { - return Path.of(ReobfServer.class.getProtectionDomain().getCodeSource().getLocation().toURI()); - } catch (final URISyntaxException ex) { - throw new RuntimeException(ex); - } - } -} diff --git a/paper-server/src/main/java/io/papermc/paper/pluginremap/UnknownOriginRemappedPluginIndex.java b/paper-server/src/main/java/io/papermc/paper/pluginremap/UnknownOriginRemappedPluginIndex.java deleted file mode 100644 index ad53aab4fee1..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/pluginremap/UnknownOriginRemappedPluginIndex.java +++ /dev/null @@ -1,72 +0,0 @@ -package io.papermc.paper.pluginremap; - -import com.mojang.logging.LogUtils; -import io.papermc.paper.util.Hashing; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.framework.qual.DefaultQualifier; -import org.slf4j.Logger; - -@DefaultQualifier(NonNull.class) -final class UnknownOriginRemappedPluginIndex extends RemappedPluginIndex { - private static final Logger LOGGER = LogUtils.getLogger(); - - private final Set used = new HashSet<>(); - - UnknownOriginRemappedPluginIndex(final Path dir) { - super(dir, true); - } - - @Override - @Nullable Path getIfPresent(final Path in) { - final String hash = Hashing.sha256(in); - if (this.state.skippedHashes.contains(hash)) { - return in; - } - - final @Nullable Path path = super.getIfPresent(hash); - if (path != null) { - this.used.add(hash); - } - return path; - } - - @Override - Path input(final Path in) { - final String hash = Hashing.sha256(in); - this.used.add(hash); - return super.input(in, hash); - } - - void write(final boolean clean) { - if (!clean) { - super.write(); - return; - } - - final Iterator> it = this.state.hashes.entrySet().iterator(); - while (it.hasNext()) { - final Map.Entry next = it.next(); - if (this.used.contains(next.getKey())) { - continue; - } - - // Remove unused mapped file - it.remove(); - final Path file = this.dir().resolve(next.getValue()); - try { - Files.deleteIfExists(file); - } catch (final IOException ex) { - LOGGER.warn("Failed to delete no longer needed cached jar '{}'", file, ex); - } - } - super.write(); - } -} diff --git a/paper-server/src/main/java/io/papermc/paper/pluginremap/reflect/PaperReflection.java b/paper-server/src/main/java/io/papermc/paper/pluginremap/reflect/PaperReflection.java deleted file mode 100644 index 92bc8e4933ff..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/pluginremap/reflect/PaperReflection.java +++ /dev/null @@ -1,211 +0,0 @@ -package io.papermc.paper.pluginremap.reflect; - -import com.mojang.logging.LogUtils; -import io.papermc.paper.util.MappingEnvironment; -import io.papermc.paper.util.ObfHelper; -import io.papermc.reflectionrewriter.runtime.AbstractDefaultRulesReflectionProxy; -import io.papermc.reflectionrewriter.runtime.DefineClassReflectionProxy; -import java.lang.invoke.MethodHandles; -import java.nio.ByteBuffer; -import java.security.CodeSource; -import java.security.ProtectionDomain; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.framework.qual.DefaultQualifier; -import org.slf4j.Logger; - -// todo proper inheritance handling -@SuppressWarnings("unused") -@DefaultQualifier(NonNull.class) -public final class PaperReflection extends AbstractDefaultRulesReflectionProxy implements DefineClassReflectionProxy { - // concat to avoid being rewritten by shadow - private static final Logger LOGGER = LogUtils.getLogger(); - private static final String CB_PACKAGE_PREFIX = "org.bukkit.".concat("craftbukkit."); - private static final String LEGACY_CB_PACKAGE_PREFIX = "org.bukkit.".concat("craftbukkit.") + MappingEnvironment.LEGACY_CB_VERSION + "."; - - private final DefineClassReflectionProxy defineClassProxy; - private final Map mappingsByMojangName; - private final Map mappingsByObfName; - // Reflection does not care about method return values, so this map removes the return value descriptor from the key - private final Map> strippedMethodMappings; - - PaperReflection() { - this.defineClassProxy = DefineClassReflectionProxy.create(PaperReflection::processClass); - if (!MappingEnvironment.hasMappings()) { - this.mappingsByMojangName = Map.of(); - this.mappingsByObfName = Map.of(); - this.strippedMethodMappings = Map.of(); - return; - } - final ObfHelper obfHelper = ObfHelper.INSTANCE; - this.mappingsByMojangName = Objects.requireNonNull(obfHelper.mappingsByMojangName(), "mappingsByMojangName"); - this.mappingsByObfName = Objects.requireNonNull(obfHelper.mappingsByObfName(), "mappingsByObfName"); - this.strippedMethodMappings = this.mappingsByMojangName.entrySet().stream().collect(Collectors.toUnmodifiableMap( - Map.Entry::getKey, - entry -> entry.getValue().strippedMethods() - )); - } - - @Override - protected String mapClassName(final String name) { - final ObfHelper.@Nullable ClassMapping mapping = this.mappingsByObfName.get(name); - return mapping != null ? mapping.mojangName() : removeCraftBukkitRelocation(name); - } - - @Override - protected String mapDeclaredMethodName(final Class clazz, final String name, final Class @Nullable ... parameterTypes) { - final @Nullable Map mapping = this.strippedMethodMappings.get(clazz.getName()); - if (mapping == null) { - return name; - } - return mapping.getOrDefault(strippedMethodKey(name, parameterTypes), name); - } - - @Override - protected String mapMethodName(final Class clazz, final String name, final Class @Nullable ... parameterTypes) { - final @Nullable String mapped = this.findMappedMethodName(clazz, name, parameterTypes); - return mapped != null ? mapped : name; - } - - @Override - protected String mapDeclaredFieldName(final Class clazz, final String name) { - final ObfHelper.@Nullable ClassMapping mapping = this.mappingsByMojangName.get(clazz.getName()); - if (mapping == null) { - return name; - } - return mapping.fieldsByObf().getOrDefault(name, name); - } - - @Override - protected String mapFieldName(final Class clazz, final String name) { - final @Nullable String mapped = this.findMappedFieldName(clazz, name); - return mapped != null ? mapped : name; - } - - private @Nullable String findMappedMethodName(final Class clazz, final String name, final Class @Nullable ... parameterTypes) { - final Map map = this.strippedMethodMappings.get(clazz.getName()); - @Nullable String mapped = null; - if (map != null) { - mapped = map.get(strippedMethodKey(name, parameterTypes)); - if (mapped != null) { - return mapped; - } - } - // JVM checks super before interfaces - final Class superClass = clazz.getSuperclass(); - if (superClass != null) { - mapped = this.findMappedMethodName(superClass, name, parameterTypes); - } - if (mapped == null) { - for (final Class i : clazz.getInterfaces()) { - mapped = this.findMappedMethodName(i, name, parameterTypes); - if (mapped != null) { - break; - } - } - } - return mapped; - } - - private @Nullable String findMappedFieldName(final Class clazz, final String name) { - final ObfHelper.ClassMapping mapping = this.mappingsByMojangName.get(clazz.getName()); - @Nullable String mapped = null; - if (mapping != null) { - mapped = mapping.fieldsByObf().get(name); - if (mapped != null) { - return mapped; - } - } - // The JVM checks super before interfaces - final Class superClass = clazz.getSuperclass(); - if (superClass != null) { - mapped = this.findMappedFieldName(superClass, name); - } - if (mapped == null) { - for (final Class i : clazz.getInterfaces()) { - mapped = this.findMappedFieldName(i, name); - if (mapped != null) { - break; - } - } - } - return mapped; - } - - private static String strippedMethodKey(final String methodName, final Class @Nullable ... parameterTypes) { - return methodName + parameterDescriptor(parameterTypes); - } - - private static String parameterDescriptor(final Class @Nullable ... parameterTypes) { - if (parameterTypes == null) { - // Null parameterTypes is treated as an empty array - return "()"; - } - final StringBuilder builder = new StringBuilder(); - builder.append('('); - for (final Class parameterType : parameterTypes) { - builder.append(parameterType.descriptorString()); - } - builder.append(')'); - return builder.toString(); - } - - private static String removeCraftBukkitRelocation(final String name) { - if (MappingEnvironment.hasMappings()) { - // Relocation is applied in reobf, and when mappings are present they handle the relocation - return name; - } - if (name.startsWith(LEGACY_CB_PACKAGE_PREFIX)) { - return CB_PACKAGE_PREFIX + name.substring(LEGACY_CB_PACKAGE_PREFIX.length()); - } - return name; - } - - @Override - public Class defineClass(final Object loader, final byte[] b, final int off, final int len) throws ClassFormatError { - return this.defineClassProxy.defineClass(loader, b, off, len); - } - - @Override - public Class defineClass(final Object loader, final String name, final byte[] b, final int off, final int len) throws ClassFormatError { - return this.defineClassProxy.defineClass(loader, name, b, off, len); - } - - @Override - public Class defineClass(final Object loader, final @Nullable String name, final byte[] b, final int off, final int len, final @Nullable ProtectionDomain protectionDomain) throws ClassFormatError { - return this.defineClassProxy.defineClass(loader, name, b, off, len, protectionDomain); - } - - @Override - public Class defineClass(final Object loader, final String name, final ByteBuffer b, final ProtectionDomain protectionDomain) throws ClassFormatError { - return this.defineClassProxy.defineClass(loader, name, b, protectionDomain); - } - - @Override - public Class defineClass(final Object secureLoader, final String name, final byte[] b, final int off, final int len, final CodeSource cs) { - return this.defineClassProxy.defineClass(secureLoader, name, b, off, len, cs); - } - - @Override - public Class defineClass(final Object secureLoader, final String name, final ByteBuffer b, final CodeSource cs) { - return this.defineClassProxy.defineClass(secureLoader, name, b, cs); - } - - @Override - public Class defineClass(final MethodHandles.Lookup lookup, final byte[] bytes) throws IllegalAccessException { - return this.defineClassProxy.defineClass(lookup, bytes); - } - - // todo apply bytecode remap here as well - private static byte[] processClass(final byte[] bytes) { - try { - return ReflectionRemapper.processClass(bytes); - } catch (final Exception ex) { - LOGGER.warn("Failed to process class bytes", ex); - return bytes; - } - } -} diff --git a/paper-server/src/main/java/io/papermc/paper/pluginremap/reflect/ReflectionRemapper.java b/paper-server/src/main/java/io/papermc/paper/pluginremap/reflect/ReflectionRemapper.java deleted file mode 100644 index a3045afbc0cc..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/pluginremap/reflect/ReflectionRemapper.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.papermc.paper.pluginremap.reflect; - -import io.papermc.asm.ClassInfoProvider; -import io.papermc.asm.RewriteRuleVisitorFactory; -import io.papermc.paper.util.MappingEnvironment; -import io.papermc.reflectionrewriter.BaseReflectionRules; -import io.papermc.reflectionrewriter.DefineClassRule; -import io.papermc.reflectionrewriter.proxygenerator.ProxyGenerator; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Method; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.framework.qual.DefaultQualifier; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; - -@DefaultQualifier(NonNull.class) -public final class ReflectionRemapper { - private static final String PAPER_REFLECTION_HOLDER = "io.papermc.paper.pluginremap.reflect.PaperReflectionHolder"; - private static final String PAPER_REFLECTION_HOLDER_DESC = PAPER_REFLECTION_HOLDER.replace('.', '/'); - private static final RewriteRuleVisitorFactory VISITOR_FACTORY = RewriteRuleVisitorFactory.create( - Opcodes.ASM9, - chain -> chain.then(new BaseReflectionRules(PAPER_REFLECTION_HOLDER).rules()) - .then(DefineClassRule.create(PAPER_REFLECTION_HOLDER_DESC, true)), - ClassInfoProvider.basic() - ); - - static { - if (!MappingEnvironment.reobf()) { - setupProxy(); - } - } - - private ReflectionRemapper() { - } - - public static ClassVisitor visitor(final ClassVisitor parent) { - if (MappingEnvironment.reobf() || MappingEnvironment.DISABLE_PLUGIN_REMAPPING) { - return parent; - } - return VISITOR_FACTORY.createVisitor(parent); - } - - public static byte[] processClass(final byte[] bytes) { - if (MappingEnvironment.DISABLE_PLUGIN_REMAPPING) { - return bytes; - } - final ClassReader classReader = new ClassReader(bytes); - final ClassWriter classWriter = new ClassWriter(classReader, 0); - classReader.accept(ReflectionRemapper.visitor(classWriter), 0); - return classWriter.toByteArray(); - } - - private static void setupProxy() { - try { - final byte[] bytes = ProxyGenerator.generateProxy(PaperReflection.class, PAPER_REFLECTION_HOLDER_DESC); - final MethodHandles.Lookup lookup = MethodHandles.lookup(); - final Class generated = lookup.defineClass(bytes); - final Method init = generated.getDeclaredMethod("init", PaperReflection.class); - init.invoke(null, new PaperReflection()); - } catch (final ReflectiveOperationException ex) { - throw new RuntimeException(ex); - } - } -} diff --git a/paper-server/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilderImpl.java b/paper-server/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilderImpl.java index 178e58292358..cb8d66555358 100644 --- a/paper-server/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilderImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilderImpl.java @@ -20,7 +20,7 @@ public class PositionedRayTraceConfigurationBuilderImpl implements PositionedRay public OptionalDouble maxDistance = OptionalDouble.empty(); public FluidCollisionMode fluidCollisionMode = FluidCollisionMode.NEVER; public BlockCollisionMode blockCollisionMode = BlockCollisionMode.OUTLINE; - public double raySize = 0.0D; + public double raySize = 0.0; public @Nullable Predicate entityFilter; public @Nullable Predicate blockFilter; public EnumSet targets = EnumSet.noneOf(RayTraceTarget.class); diff --git a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java index 121babcfdda5..291b47d91a4b 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java @@ -136,10 +136,14 @@ public final class PaperRegistries { start(Registries.PAINTING_VARIANT, RegistryKey.PAINTING_VARIANT).craft(Art.class, CraftArt::new).writable(PaperPaintingVariantRegistryEntry.PaperBuilder::new).delayed(), start(Registries.INSTRUMENT, RegistryKey.INSTRUMENT).craft(MusicInstrument.class, CraftMusicInstrument::new, true).writable(PaperInstrumentRegistryEntry.PaperBuilder::new).delayed(), start(Registries.CAT_VARIANT, RegistryKey.CAT_VARIANT).craft(Cat.Type.class, CraftCat.CraftType::new).writable(PaperCatTypeRegistryEntry.PaperBuilder::new).delayed(), + start(Registries.CAT_SOUND_VARIANT, RegistryKey.CAT_SOUND_VARIANT).craft(Cat.SoundVariant.class, CraftCat.CraftSoundVariant::new).build(), start(Registries.FROG_VARIANT, RegistryKey.FROG_VARIANT).craft(Frog.Variant.class, CraftFrog.CraftVariant::new).writable(PaperFrogVariantRegistryEntry.PaperBuilder::new).delayed(), start(Registries.CHICKEN_VARIANT, RegistryKey.CHICKEN_VARIANT).craft(Chicken.Variant.class, CraftChicken.CraftVariant::new).writable(PaperChickenVariantRegistryEntry.PaperBuilder::new), + start(Registries.CHICKEN_SOUND_VARIANT, RegistryKey.CHICKEN_SOUND_VARIANT).craft(Chicken.SoundVariant.class, CraftChicken.CraftSoundVariant::new).build(), start(Registries.COW_VARIANT, RegistryKey.COW_VARIANT).craft(Cow.Variant.class, CraftCow.CraftVariant::new).writable(PaperCowVariantRegistryEntry.PaperBuilder::new), + start(Registries.COW_SOUND_VARIANT, RegistryKey.COW_SOUND_VARIANT).craft(Cow.SoundVariant.class, CraftCow.CraftSoundVariant::new).build(), start(Registries.PIG_VARIANT, RegistryKey.PIG_VARIANT).craft(Pig.Variant.class, CraftPig.CraftVariant::new).writable(PaperPigVariantRegistryEntry.PaperBuilder::new), + start(Registries.PIG_SOUND_VARIANT, RegistryKey.PIG_SOUND_VARIANT).craft(Pig.SoundVariant.class, CraftPig.CraftSoundVariant::new).build(), start(Registries.ZOMBIE_NAUTILUS_VARIANT, RegistryKey.ZOMBIE_NAUTILUS_VARIANT).craft(ZombieNautilus.Variant.class, CraftZombieNautilus.CraftVariant::new).writable(PaperZombieNautilusVariantRegistryEntry.PaperBuilder::new), start(Registries.DIALOG, RegistryKey.DIALOG).craft(Dialog.class, PaperDialog::new, true).writable(PaperDialogRegistryEntry.PaperBuilder::new), diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperCatTypeRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperCatTypeRegistryEntry.java index 7b73a4a8e549..a1d314f722d9 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperCatTypeRegistryEntry.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperCatTypeRegistryEntry.java @@ -15,6 +15,7 @@ public class PaperCatTypeRegistryEntry implements CatTypeRegistryEntry { protected ClientAsset.@Nullable ResourceTexture clientTextureAsset; + protected ClientAsset.@Nullable ResourceTexture babyClientTextureAsset; protected SpawnPrioritySelectors spawnConditions; protected final Conversions conversions; @@ -29,7 +30,8 @@ public PaperCatTypeRegistryEntry( return; } - this.clientTextureAsset = internal.assetInfo(); + this.clientTextureAsset = internal.assetInfo(false); + this.babyClientTextureAsset = internal.assetInfo(true); this.spawnConditions = internal.spawnConditions(); } @@ -38,6 +40,11 @@ public ClientTextureAsset clientTextureAsset() { return this.conversions.asBukkit(asConfigured(this.clientTextureAsset, "clientTextureAsset")); } + @Override + public ClientTextureAsset babyClientTextureAsset() { + return this.conversions.asBukkit(asConfigured(this.babyClientTextureAsset, "babyClientTextureAsset")); + } + public static final class PaperBuilder extends PaperCatTypeRegistryEntry implements Builder, PaperRegistryBuilder { public PaperBuilder(final Conversions conversions, final @Nullable CatVariant internal) { @@ -50,10 +57,17 @@ public Builder clientTextureAsset(final ClientTextureAsset clientTextureAsset) { return this; } + @Override + public Builder babyClientTextureAsset(final ClientTextureAsset babyClientTextureAsset) { + this.babyClientTextureAsset = this.conversions.asVanilla(asArgument(babyClientTextureAsset, "babyClientTextureAsset")); + return this; + } + @Override public CatVariant build() { return new CatVariant( asConfigured(this.clientTextureAsset, "clientTextureAsset"), + asConfigured(this.babyClientTextureAsset, "babyClientTextureAsset"), asConfigured(this.spawnConditions, "spawnConditions") ); } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperChickenVariantRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperChickenVariantRegistryEntry.java index 31b931326467..4809bff29b99 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperChickenVariantRegistryEntry.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperChickenVariantRegistryEntry.java @@ -17,6 +17,7 @@ public class PaperChickenVariantRegistryEntry implements ChickenVariantRegistryE protected ChickenVariant.@Nullable ModelType model; protected ClientAsset.@Nullable ResourceTexture clientTextureAsset; + protected ClientAsset.@Nullable ResourceTexture babyClientTextureAsset; protected SpawnPrioritySelectors spawnConditions; protected final Conversions conversions; @@ -32,6 +33,7 @@ public PaperChickenVariantRegistryEntry( } this.clientTextureAsset = internal.modelAndTexture().asset(); + this.babyClientTextureAsset = internal.babyTexture(); this.model = internal.modelAndTexture().model(); this.spawnConditions = internal.spawnConditions(); } @@ -41,6 +43,11 @@ public ClientTextureAsset clientTextureAsset() { return this.conversions.asBukkit(asConfigured(this.clientTextureAsset, "clientTextureAsset")); } + @Override + public ClientTextureAsset babyClientTextureAsset() { + return this.conversions.asBukkit(asConfigured(this.babyClientTextureAsset, "babyClientTextureAsset")); + } + @Override public Model model() { return switch (asConfigured(this.model, "model")) { @@ -61,6 +68,12 @@ public Builder clientTextureAsset(final ClientTextureAsset clientTextureAsset) { return this; } + @Override + public Builder babyClientTextureAsset(final ClientTextureAsset babyClientTextureAsset) { + this.babyClientTextureAsset = this.conversions.asVanilla(asArgument(babyClientTextureAsset, "babyClientTextureAsset")); + return this; + } + @Override public Builder model(final Model model) { this.model = switch (asArgument(model, "model")) { @@ -74,6 +87,7 @@ public Builder model(final Model model) { public ChickenVariant build() { return new ChickenVariant( new ModelAndTexture<>(asConfigured(this.model, "model"), asConfigured(this.clientTextureAsset, "clientTextureAsset")), + asConfigured(this.babyClientTextureAsset, "babyClientTextureAsset"), asConfigured(this.spawnConditions, "spawnConditions") ); } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperCowVariantRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperCowVariantRegistryEntry.java index cd3249196b26..8d526716ea43 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperCowVariantRegistryEntry.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperCowVariantRegistryEntry.java @@ -17,6 +17,7 @@ public class PaperCowVariantRegistryEntry implements CowVariantRegistryEntry { protected CowVariant.@Nullable ModelType model = null; protected ClientAsset.@Nullable ResourceTexture clientTextureAsset = null; + protected ClientAsset.@Nullable ResourceTexture babyClientTextureAsset = null; protected SpawnPrioritySelectors spawnConditions; protected final Conversions conversions; @@ -32,6 +33,7 @@ public PaperCowVariantRegistryEntry( } this.clientTextureAsset = internal.modelAndTexture().asset(); + this.babyClientTextureAsset = internal.babyTexture(); this.model = internal.modelAndTexture().model(); this.spawnConditions = internal.spawnConditions(); } @@ -41,6 +43,11 @@ public ClientTextureAsset clientTextureAsset() { return this.conversions.asBukkit(asConfigured(this.clientTextureAsset, "clientTextureAsset")); } + @Override + public ClientTextureAsset babyClientTextureAsset() { + return this.conversions.asBukkit(asConfigured(this.babyClientTextureAsset, "babyClientTextureAsset")); + } + @Override public Model model() { return switch (asConfigured(this.model, "model")) { @@ -62,6 +69,12 @@ public Builder clientTextureAsset(final ClientTextureAsset clientTextureAsset) { return this; } + @Override + public Builder babyClientTextureAsset(final ClientTextureAsset babyClientTextureAsset) { + this.babyClientTextureAsset = this.conversions.asVanilla(asArgument(babyClientTextureAsset, "babyClientTextureAsset")); + return this; + } + @Override public Builder model(final Model model) { this.model = switch (asArgument(model, "model")) { @@ -76,6 +89,7 @@ public Builder model(final Model model) { public CowVariant build() { return new CowVariant( new ModelAndTexture<>(asConfigured(this.model, "model"), asConfigured(this.clientTextureAsset, "clientTextureAsset")), + asConfigured(this.babyClientTextureAsset, "babyClientTextureAsset"), this.spawnConditions ); } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperPigVariantRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperPigVariantRegistryEntry.java index 3f8af47de16b..fe9e5889b819 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperPigVariantRegistryEntry.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperPigVariantRegistryEntry.java @@ -17,6 +17,7 @@ public class PaperPigVariantRegistryEntry implements PigVariantRegistryEntry { protected PigVariant.@Nullable ModelType model; protected ClientAsset.@Nullable ResourceTexture clientTextureAsset; + protected ClientAsset.@Nullable ResourceTexture babyClientTextureAsset; protected SpawnPrioritySelectors spawnConditions; protected final Conversions conversions; @@ -32,6 +33,7 @@ public PaperPigVariantRegistryEntry( } this.clientTextureAsset = internal.modelAndTexture().asset(); + this.babyClientTextureAsset = internal.babyTexture(); this.model = internal.modelAndTexture().model(); this.spawnConditions = internal.spawnConditions(); } @@ -41,6 +43,11 @@ public ClientTextureAsset clientTextureAsset() { return this.conversions.asBukkit(asConfigured(this.clientTextureAsset, "clientTextureAsset")); } + @Override + public ClientTextureAsset babyClientTextureAsset() { + return this.conversions.asBukkit(asConfigured(this.babyClientTextureAsset, "babyClientTextureAsset")); + } + @Override public Model model() { return switch (asConfigured(this.model, "model")) { @@ -61,6 +68,12 @@ public Builder clientTextureAsset(final ClientTextureAsset clientTextureAsset) { return this; } + @Override + public Builder babyClientTextureAsset(final ClientTextureAsset babyClientTextureAsset) { + this.babyClientTextureAsset = this.conversions.asVanilla(asArgument(babyClientTextureAsset, "babyClientTextureAsset")); + return this; + } + @Override public Builder model(final Model model) { this.model = switch (asArgument(model, "model")) { @@ -74,6 +87,7 @@ public Builder model(final Model model) { public PigVariant build() { return new PigVariant( new ModelAndTexture<>(asConfigured(this.model, "model"), asConfigured(this.clientTextureAsset, "clientTextureAsset")), + asConfigured(this.babyClientTextureAsset, "babyClientTextureAsset"), asConfigured(this.spawnConditions, "spawnConditions") ); } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperWolfVariantRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperWolfVariantRegistryEntry.java index cb7a976b4ad1..4c33b3ecd8a2 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperWolfVariantRegistryEntry.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperWolfVariantRegistryEntry.java @@ -17,6 +17,9 @@ public class PaperWolfVariantRegistryEntry implements WolfVariantRegistryEntry { protected ClientAsset.@Nullable ResourceTexture angryClientTextureAsset; protected ClientAsset.@Nullable ResourceTexture wildClientTextureAsset; protected ClientAsset.@Nullable ResourceTexture tameClientTextureAsset; + protected ClientAsset.@Nullable ResourceTexture babyAngryClientTextureAsset; + protected ClientAsset.@Nullable ResourceTexture babyWildClientTextureAsset; + protected ClientAsset.@Nullable ResourceTexture babyTameClientTextureAsset; protected SpawnPrioritySelectors spawnConditions; protected final Conversions conversions; @@ -31,9 +34,12 @@ public PaperWolfVariantRegistryEntry( return; } - this.angryClientTextureAsset = internal.assetInfo().angry(); - this.wildClientTextureAsset = internal.assetInfo().wild(); - this.tameClientTextureAsset = internal.assetInfo().tame(); + this.angryClientTextureAsset = internal.adultInfo().angry(); + this.wildClientTextureAsset = internal.adultInfo().wild(); + this.tameClientTextureAsset = internal.adultInfo().tame(); + this.babyAngryClientTextureAsset = internal.babyInfo().angry(); + this.babyWildClientTextureAsset = internal.babyInfo().wild(); + this.babyTameClientTextureAsset = internal.babyInfo().tame(); this.spawnConditions = internal.spawnConditions(); } @@ -52,6 +58,21 @@ public ClientTextureAsset tameClientTextureAsset() { return this.conversions.asBukkit(asConfigured(this.tameClientTextureAsset, "tameClientTextureAsset")); } + @Override + public ClientTextureAsset babyAngryClientTextureAsset() { + return this.conversions.asBukkit(asConfigured(this.babyAngryClientTextureAsset, "babyAngryClientTextureAsset")); + } + + @Override + public ClientTextureAsset babyWildClientTextureAsset() { + return this.conversions.asBukkit(asConfigured(this.babyWildClientTextureAsset, "babyWildClientTextureAsset")); + } + + @Override + public ClientTextureAsset babyTameClientTextureAsset() { + return this.conversions.asBukkit(asConfigured(this.babyTameClientTextureAsset, "babyTameClientTextureAsset")); + } + public static final class PaperBuilder extends PaperWolfVariantRegistryEntry implements Builder, PaperRegistryBuilder { public PaperBuilder(final Conversions conversions, final @Nullable WolfVariant internal) { @@ -76,6 +97,24 @@ public Builder tameClientTextureAsset(final ClientTextureAsset tameClientTexture return this; } + @Override + public Builder babyAngryClientTextureAsset(final ClientTextureAsset babyAngryClientTextureAsset) { + this.babyAngryClientTextureAsset = this.conversions.asVanilla(asArgument(babyAngryClientTextureAsset, "babyAngryClientTextureAsset")); + return this; + } + + @Override + public Builder babyWildClientTextureAsset(final ClientTextureAsset babyWildClientTextureAsset) { + this.babyWildClientTextureAsset = this.conversions.asVanilla(asArgument(babyWildClientTextureAsset, "babyWildClientTextureAsset")); + return this; + } + + @Override + public Builder babyTameClientTextureAsset(final ClientTextureAsset babyTameClientTextureAsset) { + this.babyTameClientTextureAsset = this.conversions.asVanilla(asArgument(babyTameClientTextureAsset, "babyTameClientTextureAsset")); + return this; + } + @Override public WolfVariant build() { return new WolfVariant( @@ -84,6 +123,11 @@ public WolfVariant build() { asConfigured(this.tameClientTextureAsset, "tameClientTextureAsset"), asConfigured(this.angryClientTextureAsset, "angryClientTextureAsset") ), + new WolfVariant.AssetInfo( + asConfigured(this.babyWildClientTextureAsset, "babyWildClientTextureAsset"), + asConfigured(this.babyTameClientTextureAsset, "babyTameClientTextureAsset"), + asConfigured(this.babyAngryClientTextureAsset, "babyAngryClientTextureAsset") + ), asConfigured(this.spawnConditions, "spawnConditions") ); } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/dialog/PaperDialogCodecs.java b/paper-server/src/main/java/io/papermc/paper/registry/data/dialog/PaperDialogCodecs.java index 209ea05ade06..8d2272967ab8 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/dialog/PaperDialogCodecs.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/dialog/PaperDialogCodecs.java @@ -63,8 +63,8 @@ private PaperDialogCodecs() { return registry.freeze(); }); private static final Function> GET_DIALOG_ACTION_TYPE = dialogAction -> switch (dialogAction) { - case DialogAction.CommandTemplateAction $ -> COMMAND_TEMPLATE_ACTION_CODEC; - case DialogAction.CustomClickAction $ -> CUSTOM_ALL_ACTION_CODEC; + case DialogAction.CommandTemplateAction _ -> COMMAND_TEMPLATE_ACTION_CODEC; + case DialogAction.CustomClickAction _ -> CUSTOM_ALL_ACTION_CODEC; case DialogAction.StaticAction action -> STATIC_ACTION_CODECS.get(AdventureCodecs.GET_CLICK_EVENT_TYPE.apply(action.value())); }; private static final Codec DIALOG_ACTION_CODEC = DIALOG_ACTION_TYPES.byNameCodec().dispatch(GET_DIALOG_ACTION_TYPE, Function.identity()); @@ -85,7 +85,7 @@ private PaperDialogCodecs() { ); private static final Codec SIMPLE_PLAIN_MESSAGE_BODY_CODEC = Codec.withAlternative(PLAIN_MESSAGE_BODY_CODEC.codec(), AdventureCodecs.COMPONENT_CODEC, component -> DialogBody.plainMessage(component, PlainMessage.DEFAULT_WIDTH)); private static final MapCodec ITEM_BODY_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - ItemStack.STRICT_CODEC.xmap(CraftItemStack::asBukkitCopy, CraftItemStack::asNMSCopy).fieldOf("item").forGetter(ItemDialogBody::item), + ItemStack.CODEC.xmap(CraftItemStack::asBukkitCopy, CraftItemStack::asNMSCopy).fieldOf("item").forGetter(ItemDialogBody::item), SIMPLE_PLAIN_MESSAGE_BODY_CODEC.optionalFieldOf("description").forGetter(body -> Optional.ofNullable(body.description())), Codec.BOOL.optionalFieldOf("show_decorations", true).forGetter(ItemDialogBody::showDecorations), Codec.BOOL.optionalFieldOf("show_tooltip", true).forGetter(ItemDialogBody::showTooltip), @@ -100,8 +100,8 @@ private PaperDialogCodecs() { return registry.freeze(); }); private static final Function> GET_DIALOG_BODY_TYPE = dialogAction -> switch (dialogAction) { - case PlainMessageDialogBody $ -> PLAIN_MESSAGE_BODY_CODEC; - case ItemDialogBody $ -> ITEM_BODY_CODEC; + case PlainMessageDialogBody _ -> PLAIN_MESSAGE_BODY_CODEC; + case ItemDialogBody _ -> ITEM_BODY_CODEC; }; private static final Codec DIALOG_BODY_CODEC = DIALOG_BODY_TYPES.byNameCodec().dispatch(GET_DIALOG_BODY_TYPE, Function.identity()); private static final Codec> DIALOG_BODY_LIST_CODEC = ExtraCodecs.compactListCodec(DIALOG_BODY_CODEC); @@ -163,10 +163,10 @@ private PaperDialogCodecs() { return registry.freeze(); }); private static final Function> GET_DIALOG_INPUT_TYPE_TYPE = dialogAction -> switch (dialogAction) { - case TextDialogInput $ -> TEXT_DIALOG_INPUT_TYPE_MAP_CODEC; - case SingleOptionDialogInput $ -> SINGLE_OPTION_DIALOG_INPUT_TYPE_MAP_CODEC; - case NumberRangeDialogInput $ -> NUMBER_RANGE_INPUT_MAP_CODEC; - case BooleanDialogInput $ -> BOOLEAN_DIALOG_INPUT_TYPE_MAP_CODEC; + case TextDialogInput _ -> TEXT_DIALOG_INPUT_TYPE_MAP_CODEC; + case SingleOptionDialogInput _ -> SINGLE_OPTION_DIALOG_INPUT_TYPE_MAP_CODEC; + case NumberRangeDialogInput _ -> NUMBER_RANGE_INPUT_MAP_CODEC; + case BooleanDialogInput _ -> BOOLEAN_DIALOG_INPUT_TYPE_MAP_CODEC; }; private static final Codec DIALOG_INPUT_CODEC = DIALOG_INPUT_TYPES.byNameCodec().dispatchMap(GET_DIALOG_INPUT_TYPE_TYPE, Function.identity()).codec(); diff --git a/paper-server/src/main/java/io/papermc/paper/util/MappingEnvironment.java b/paper-server/src/main/java/io/papermc/paper/util/MappingEnvironment.java deleted file mode 100644 index a9363c43d9d4..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/util/MappingEnvironment.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.papermc.paper.util; - -import java.io.InputStream; -import java.util.Objects; -import java.util.jar.Manifest; -import net.minecraft.world.entity.MobCategory; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.framework.qual.DefaultQualifier; - -@DefaultQualifier(NonNull.class) -public final class MappingEnvironment { - public static final boolean DISABLE_PLUGIN_REMAPPING = Boolean.getBoolean("paper.disablePluginRemapping"); - public static final String LEGACY_CB_VERSION = "v1_21_R7"; - private static final @Nullable String MAPPINGS_HASH = readMappingsHash(); - private static final boolean REOBF = checkReobf(); - - private MappingEnvironment() { - } - - public static boolean reobf() { - return REOBF; - } - - public static boolean hasMappings() { - return MAPPINGS_HASH != null; - } - - public static InputStream mappingsStream() { - return Objects.requireNonNull(mappingsStreamIfPresent(), "Missing mappings!"); - } - - public static @Nullable InputStream mappingsStreamIfPresent() { - return MappingEnvironment.class.getClassLoader().getResourceAsStream("META-INF/mappings/reobf.tiny"); - } - - public static String mappingsHash() { - return Objects.requireNonNull(MAPPINGS_HASH, "MAPPINGS_HASH"); - } - - private static @Nullable String readMappingsHash() { - final @Nullable Manifest manifest = JarManifests.manifest(MappingEnvironment.class); - if (manifest != null) { - final Object hash = manifest.getMainAttributes().getValue("Included-Mappings-Hash"); - if (hash != null) { - return hash.toString(); - } - } - - final @Nullable InputStream stream = mappingsStreamIfPresent(); - if (stream == null) { - return null; - } - return Hashing.sha256(stream); - } - - @SuppressWarnings("ConstantConditions") - private static boolean checkReobf() { - final Class clazz = MobCategory.class; - if (clazz.getSimpleName().equals("MobCategory")) { - return false; - } else if (clazz.getSimpleName().equals("EnumCreatureType")) { - return true; - } - throw new IllegalStateException(); - } -} diff --git a/paper-server/src/main/java/io/papermc/paper/util/ObfHelper.java b/paper-server/src/main/java/io/papermc/paper/util/ObfHelper.java deleted file mode 100644 index 6067be951c4c..000000000000 --- a/paper-server/src/main/java/io/papermc/paper/util/ObfHelper.java +++ /dev/null @@ -1,156 +0,0 @@ -package io.papermc.paper.util; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import net.neoforged.srgutils.IMappingFile; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.checkerframework.framework.qual.DefaultQualifier; - -@DefaultQualifier(NonNull.class) -public enum ObfHelper { - INSTANCE; - - private final @Nullable Map mappingsByObfName; - private final @Nullable Map mappingsByMojangName; - - ObfHelper() { - final @Nullable Set maps = loadMappingsIfPresent(); - if (maps != null) { - this.mappingsByObfName = maps.stream().collect(Collectors.toUnmodifiableMap(ClassMapping::obfName, map -> map)); - this.mappingsByMojangName = maps.stream().collect(Collectors.toUnmodifiableMap(ClassMapping::mojangName, map -> map)); - } else { - this.mappingsByObfName = null; - this.mappingsByMojangName = null; - } - } - - public @Nullable Map mappingsByObfName() { - return this.mappingsByObfName; - } - - public @Nullable Map mappingsByMojangName() { - return this.mappingsByMojangName; - } - - /** - * Attempts to get the obf name for a given class by its Mojang name. Will - * return the input string if mappings are not present. - * - * @param fullyQualifiedMojangName fully qualified class name (dotted) - * @return mapped or original fully qualified (dotted) class name - */ - public String reobfClassName(final String fullyQualifiedMojangName) { - if (this.mappingsByMojangName == null) { - return fullyQualifiedMojangName; - } - - final ClassMapping map = this.mappingsByMojangName.get(fullyQualifiedMojangName); - if (map == null) { - return fullyQualifiedMojangName; - } - - return map.obfName(); - } - - /** - * Attempts to get the Mojang name for a given class by its obf name. Will - * return the input string if mappings are not present. - * - * @param fullyQualifiedObfName fully qualified class name (dotted) - * @return mapped or original fully qualified (dotted) class name - */ - public String deobfClassName(final String fullyQualifiedObfName) { - if (this.mappingsByObfName == null) { - return fullyQualifiedObfName; - } - - final ClassMapping map = this.mappingsByObfName.get(fullyQualifiedObfName); - if (map == null) { - return fullyQualifiedObfName; - } - - return map.mojangName(); - } - - private static @Nullable Set loadMappingsIfPresent() { - if (!MappingEnvironment.hasMappings()) { - return null; - } - try (final InputStream mappingsInputStream = MappingEnvironment.mappingsStream()) { - final IMappingFile mappings = IMappingFile.load(mappingsInputStream); // Mappings are mojang->spigot - final Set classes = new HashSet<>(); - - final StringPool pool = new StringPool(); - for (final IMappingFile.IClass cls : mappings.getClasses()) { - final Map methods = new HashMap<>(); - final Map fields = new HashMap<>(); - final Map strippedMethods = new HashMap<>(); - - for (final IMappingFile.IMethod methodMapping : cls.getMethods()) { - methods.put( - pool.string(methodKey( - Objects.requireNonNull(methodMapping.getMapped()), - Objects.requireNonNull(methodMapping.getMappedDescriptor()) - )), - pool.string(Objects.requireNonNull(methodMapping.getOriginal())) - ); - - strippedMethods.put( - pool.string(pool.string(strippedMethodKey( - methodMapping.getMapped(), - methodMapping.getDescriptor() - ))), - pool.string(methodMapping.getOriginal()) - ); - } - for (final IMappingFile.IField field : cls.getFields()) { - fields.put( - pool.string(field.getMapped()), - pool.string(field.getOriginal()) - ); - } - - final ClassMapping map = new ClassMapping( - Objects.requireNonNull(cls.getMapped()).replace('/', '.'), - Objects.requireNonNull(cls.getOriginal()).replace('/', '.'), - Map.copyOf(methods), - Map.copyOf(fields), - Map.copyOf(strippedMethods) - ); - classes.add(map); - } - - return Set.copyOf(classes); - } catch (final IOException ex) { - System.err.println("Failed to load mappings."); - ex.printStackTrace(); - return null; - } - } - - public static String strippedMethodKey(final String methodName, final String methodDescriptor) { - final String methodKey = methodKey(methodName, methodDescriptor); - final int returnDescriptorEnd = methodKey.indexOf(')'); - return methodKey.substring(0, returnDescriptorEnd + 1); - } - - public static String methodKey(final String methodName, final String methodDescriptor) { - return methodName + methodDescriptor; - } - - public record ClassMapping( - String obfName, - String mojangName, - Map methodsByObf, - Map fieldsByObf, - // obf name with mapped desc to mapped name. return value is excluded from desc as reflection doesn't use it - Map strippedMethods - ) {} -} diff --git a/paper-server/src/main/java/io/papermc/paper/util/sanitizer/OversizedItemComponentSanitizer.java b/paper-server/src/main/java/io/papermc/paper/util/sanitizer/OversizedItemComponentSanitizer.java index 59f7e2c36d54..02579074b70a 100644 --- a/paper-server/src/main/java/io/papermc/paper/util/sanitizer/OversizedItemComponentSanitizer.java +++ b/paper-server/src/main/java/io/papermc/paper/util/sanitizer/OversizedItemComponentSanitizer.java @@ -11,7 +11,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.util.Mth; -import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.Items; import net.minecraft.world.item.component.BundleContents; import net.minecraft.world.item.component.ChargedProjectiles; @@ -59,12 +59,7 @@ private static ChargedProjectiles sanitizeChargedProjectiles(final ChargedProjec return projectiles; } - return ChargedProjectiles.of(List.of( - new ItemStack( - projectiles.contains(Items.FIREWORK_ROCKET) - ? Items.FIREWORK_ROCKET - : Items.ARROW - ))); + return ChargedProjectiles.of(new ItemStackTemplate(projectiles.contains(Items.FIREWORK_ROCKET) ? Items.FIREWORK_ROCKET : Items.ARROW)); } private static ItemContainerContents sanitizeItemContainerContents(final ItemContainerContents contents) { @@ -86,16 +81,16 @@ private static BundleContents sanitizeBundleContents(final BundleContents conten // A bundles content weight may be anywhere from 0 to, basically, infinity. // A weight of 1 is the usual maximum case - int sizeUsed = Mth.mulAndTruncate(contents.weight(), 64); + int sizeUsed = Mth.mulAndTruncate(contents.weight().getOrThrow(), 64); // Early out, *most* bundles should not be overfilled above a weight of one. if (sizeUsed <= 64) { - return new BundleContents(List.of(new ItemStack(Items.PAPER, Math.max(1, sizeUsed)))); + return new BundleContents(List.of(new ItemStackTemplate(Items.PAPER, Math.max(1, sizeUsed)))); } - final List sanitizedRepresentation = new ObjectArrayList<>(sizeUsed / 64 + 1); + final List sanitizedRepresentation = new ObjectArrayList<>(sizeUsed / 64 + 1); while (sizeUsed > 0) { final int stackCount = Math.min(64, sizeUsed); - sanitizedRepresentation.add(new ItemStack(Items.PAPER, stackCount)); + sanitizedRepresentation.add(new ItemStackTemplate(Items.PAPER, stackCount)); sizeUsed -= stackCount; } // Now we add a single fake item that uses the same amount of slots as all other items. diff --git a/paper-server/src/main/java/io/papermc/paper/world/PaperWorldLoader.java b/paper-server/src/main/java/io/papermc/paper/world/PaperWorldLoader.java index 51a01f86fee5..cd3e7cc8cf08 100644 --- a/paper-server/src/main/java/io/papermc/paper/world/PaperWorldLoader.java +++ b/paper-server/src/main/java/io/papermc/paper/world/PaperWorldLoader.java @@ -1,223 +1,204 @@ package io.papermc.paper.world; -import com.google.common.io.Files; -import com.mojang.logging.LogUtils; -import com.mojang.serialization.Dynamic; +import io.papermc.paper.world.migration.WorldFolderMigration; +import io.papermc.paper.world.saveddata.PaperLevelOverrides; +import io.papermc.paper.world.saveddata.PaperWorldMetadata; +import io.papermc.paper.world.saveddata.PaperWorldPDC; +import java.io.IOException; +import java.util.Locale; +import java.util.UUID; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.NbtException; -import net.minecraft.nbt.ReportedNbtException; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; import net.minecraft.server.Main; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.datafix.DataFixers; +import net.minecraft.world.level.Level; import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.storage.LevelDataAndDimensions; +import net.minecraft.world.level.storage.LevelResource; import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.LevelSummary; import net.minecraft.world.level.storage.PrimaryLevelData; -import net.minecraft.world.level.validation.ContentValidationException; -import org.apache.commons.io.FileUtils; +import net.minecraft.world.level.storage.SavedDataStorage; +import org.bukkit.NamespacedKey; import org.bukkit.World; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -import org.slf4j.Logger; -import java.io.File; -import java.io.IOException; -import java.util.Locale; -public record PaperWorldLoader(MinecraftServer server, String levelId) { - private static final Logger LOGGER = LogUtils.getClassLogger(); +import static java.util.Objects.requireNonNull; +@NullMarked +public record PaperWorldLoader(MinecraftServer server, String levelId) { public static PaperWorldLoader create(final MinecraftServer server, final String levelId) { return new PaperWorldLoader(server, levelId); } public record WorldLoadingInfo( - int dimension, - String name, - String worldType, + World.Environment environment, ResourceKey stemKey, + ResourceKey dimensionKey, boolean enabled ) {} - private WorldLoadingInfo getWorldInfo( - final String levelId, - final LevelStem stem - ) { + public record LoadedWorldData( + String bukkitName, + UUID uuid, + @Nullable PaperWorldPDC pdc, + PaperLevelOverrides levelOverrides + ) {} + + public record WorldLoadingInfoAndData(WorldLoadingInfo info, LoadedWorldData data) {} + + private @Nullable WorldLoadingInfoAndData getWorldInfoAndData(final LevelStem stem) { + final WorldLoadingInfo info = this.getWorldInfo(stem); + if (!info.enabled()) { + return null; + } + + final String defaultName = defaultWorldName(this.levelId, info.stemKey()); + + try { + WorldFolderMigration.migrateStartupWorld(this.server.storageSource, this.server.registryAccess(), defaultName, info.stemKey(), info.dimensionKey()); + } catch (final IOException ex) { + throw new RuntimeException("Failed to migrate world storage for " + defaultName, ex); + } + + final LoadedWorldData loadedWorldData = loadWorldData( + this.server, + info.dimensionKey(), + defaultName + ); + + return new WorldLoadingInfoAndData(info, loadedWorldData); + } + + public static ResourceKey dimensionKey(final ResourceKey stemKey) { + return ResourceKey.create(Registries.DIMENSION, stemKey.identifier()); + } + + public static ResourceKey dimensionKey(final NamespacedKey key) { + return ResourceKey.create(Registries.DIMENSION, Identifier.fromNamespaceAndPath(key.namespace(), key.value())); + } + + private WorldLoadingInfo getWorldInfo(final LevelStem stem) { final ResourceKey stemKey = this.server.registryAccess().lookupOrThrow(Registries.LEVEL_STEM).getResourceKey(stem).orElseThrow(); - int dimension = 0; + final ResourceKey dimensionKey = dimensionKey(stemKey); boolean enabled = true; + final World.Environment environment; if (stemKey == LevelStem.NETHER) { - dimension = -1; + environment = World.Environment.NETHER; enabled = this.server.server.getAllowNether(); } else if (stemKey == LevelStem.END) { - dimension = 1; + environment = World.Environment.THE_END; enabled = this.server.server.getAllowEnd(); - } else if (stemKey != LevelStem.OVERWORLD) { - dimension = -999; - } - String worldType = dimension == -999 - ? stemKey.identifier().getNamespace() + "_" + stemKey.identifier().getPath() - : World.Environment.getEnvironment(dimension).toString().toLowerCase(Locale.ROOT); - String name = stemKey == LevelStem.OVERWORLD - ? levelId - : levelId + "_" + worldType; - return new WorldLoadingInfo(dimension, name, worldType, stemKey, enabled); - } - - private void migrateWorldFolder(final WorldLoadingInfo info) { - // Migration of old CB world folders... - if (info.dimension() == 0) { - return; + } else if (stemKey == LevelStem.OVERWORLD) { + environment = World.Environment.NORMAL; + } else { + environment = World.Environment.CUSTOM; } - File newWorld = LevelStorageSource.getStorageFolder(new File(info.name()).toPath(), info.stemKey()).toFile(); - File oldWorld = LevelStorageSource.getStorageFolder(new File(this.levelId).toPath(), info.stemKey()).toFile(); - File oldLevelDat = new File(new File(this.levelId), "level.dat"); // The data folders exist on first run as they are created in the PersistentCollection constructor above, but the level.dat won't - - if (!newWorld.isDirectory() && oldWorld.isDirectory() && oldLevelDat.isFile()) { - LOGGER.info("---- Migration of old " + info.worldType() + " folder required ----"); - LOGGER.info("Unfortunately due to the way that Minecraft implemented multiworld support in 1.6, Bukkit requires that you move your " + info.worldType() + " folder to a new location in order to operate correctly."); - LOGGER.info("We will move this folder for you, but it will mean that you need to move it back should you wish to stop using Bukkit in the future."); - LOGGER.info("Attempting to move " + oldWorld + " to " + newWorld + "..."); - - if (newWorld.exists()) { - LOGGER.warn("A file or folder already exists at " + newWorld + "!"); - LOGGER.info("---- Migration of old " + info.worldType() + " folder failed ----"); - } else if (newWorld.getParentFile().mkdirs()) { - if (oldWorld.renameTo(newWorld)) { - LOGGER.info("Success! To restore " + info.worldType() + " in the future, simply move " + newWorld + " to " + oldWorld); - // Migrate world data too. - try { - Files.copy(oldLevelDat, new File(new File(info.name()), "level.dat")); - FileUtils.copyDirectory(new File(new File(this.levelId), "data"), new File(new File(info.name()), "data")); - } catch (IOException exception) { - LOGGER.warn("Unable to migrate world data."); - } - LOGGER.info("---- Migration of old " + info.worldType() + " folder complete ----"); - } else { - LOGGER.warn("Could not move folder " + oldWorld + " to " + newWorld + "!"); - LOGGER.info("---- Migration of old " + info.worldType() + " folder failed ----"); - } - } else { - LOGGER.warn("Could not create path for " + newWorld + "!"); - LOGGER.info("---- Migration of old " + info.worldType() + " folder failed ----"); - } - } + return new WorldLoadingInfo(environment, stemKey, dimensionKey, enabled); } - // Loosely modeled on code in net.minecraft.server.Main - public void loadInitialWorlds() { - for (final LevelStem stem : this.server.registryAccess().lookupOrThrow(Registries.LEVEL_STEM)) { - final WorldLoadingInfo info = this.getWorldInfo(this.levelId, stem); - this.migrateWorldFolder(info); - if (!info.enabled()) { - continue; - } + public static LoadedWorldData loadWorldData( + final MinecraftServer server, + final ResourceKey dimension, + final String defaultName + ) { + final var storageSource = server.storageSource; + final var registryAccess = server.registryAccess(); - LevelStorageSource.LevelStorageAccess levelStorageAccess = this.server.storageSource; - if (info.dimension() != 0) { - try { - levelStorageAccess = LevelStorageSource.createDefault(this.server.server.getWorldContainer().toPath()).validateAndCreateAccess(info.name(), info.stemKey()); - } catch (IOException | ContentValidationException ex) { - throw new RuntimeException(ex); - } - } + final SavedDataStorage tempStorage = new SavedDataStorage(storageSource.getDimensionPath(dimension).resolve(LevelResource.DATA.id()), DataFixers.getDataFixer(), registryAccess); + final PaperWorldMetadata metadata = tempStorage.get(PaperWorldMetadata.TYPE); + final PaperWorldPDC pdc = tempStorage.get(PaperWorldPDC.TYPE); + final PaperLevelOverrides levelOverrides = tempStorage.get(PaperLevelOverrides.TYPE); - final LevelDataResult levelData = getLevelData(levelStorageAccess); - if (levelData.fatalError) { - return; - } + final LoadedWorldData data = new LoadedWorldData( + defaultName, + metadata == null ? UUID.randomUUID() : metadata.uuid(), + pdc, + levelOverrides == null ? PaperLevelOverrides.createFromLiveLevelData((PrimaryLevelData) server.getWorldData()) : levelOverrides + ); - final PrimaryLevelData primaryLevelData; - if (levelData.dataTag == null) { - primaryLevelData = (PrimaryLevelData) Main.createNewWorldData( - ((DedicatedServer) this.server).settings, - this.server.worldLoaderContext, - this.server.worldLoaderContext.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM), - this.server.isDemo(), - this.server.options.has("bonusChest") - ).cookie(); - } else { - primaryLevelData = (PrimaryLevelData) LevelStorageSource.getLevelDataAndDimensions( - levelData.dataTag, - this.server.worldLoaderContext.dataConfiguration(), - this.server.worldLoaderContext.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM), - this.server.worldLoaderContext.datapackWorldgen() - ).worldData(); - } + data.levelOverrides().attach((PrimaryLevelData) server.getWorldData(), dimension); - primaryLevelData.checkName(info.name()); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end) - primaryLevelData.setModdedInfo(this.server.getServerModName(), this.server.getModdedStatus().shouldReportAsModified()); + return data; + } - if (this.server.options.has("forceUpgrade")) { - Main.forceUpgrade( - levelStorageAccess, - primaryLevelData, - DataFixers.getDataFixer(), - this.server.options.has("eraseCache"), - () -> true, - this.server.registryAccess(), - this.server.options.has("recreateRegionFiles") - ); + public void loadInitialWorlds() { + final var levelStemRegistry = this.server.registryAccess().lookupOrThrow(Registries.LEVEL_STEM); + final boolean hasWorldData = this.server.storageSource.hasWorldData(); + final LevelStem overworldStem = requireNonNull(levelStemRegistry.getValue(LevelStem.OVERWORLD), "Overworld stem missing"); + this.loadInitialWorld(overworldStem, hasWorldData); + for (final LevelStem stem : levelStemRegistry) { + if (stem == overworldStem) { + continue; } - - this.server.createLevel(stem, info, levelStorageAccess, primaryLevelData); + this.loadInitialWorld(stem, hasWorldData); } - ((DedicatedServer) this.server).forceDifficulty(); + // ((DedicatedServer) this.server).forceDifficulty(); for (ServerLevel serverLevel : this.server.getAllLevels()) { this.server.prepareLevel(serverLevel); } } - public record LevelDataResult(@Nullable Dynamic dataTag, boolean fatalError) {} + private void loadInitialWorld(final LevelStem stem, final boolean hasWorldData) { + final WorldLoadingInfoAndData loading = this.getWorldInfoAndData(stem); + if (loading == null) { + return; + } - // Based on code in net.minecraft.server.Main - public static LevelDataResult getLevelData( - final LevelStorageSource.LevelStorageAccess levelStorageAccess - ) { - Dynamic dataTag; - if (levelStorageAccess.hasWorldData()) { - LevelSummary summary; - try { - dataTag = levelStorageAccess.getDataTag(); - summary = levelStorageAccess.getSummary(dataTag); - } catch (NbtException | ReportedNbtException | IOException var41) { - LevelStorageSource.LevelDirectory levelDirectory = levelStorageAccess.getLevelDirectory(); - LOGGER.warn("Failed to load world data from {}", levelDirectory.dataFile(), var41); - LOGGER.info("Attempting to use fallback"); - - try { - dataTag = levelStorageAccess.getDataTagFallback(); - summary = levelStorageAccess.getSummary(dataTag); - } catch (NbtException | ReportedNbtException | IOException var40) { - LOGGER.error("Failed to load world data from {}", levelDirectory.oldDataFile(), var40); - LOGGER.error( - "Failed to load world data from {} and {}. World files may be corrupted. Shutting down.", - levelDirectory.dataFile(), - levelDirectory.oldDataFile() - ); - return new LevelDataResult(null, true); - } - - levelStorageAccess.restoreLevelDataFromOld(); - } + final WorldGenSettings worldGenSettings = !hasWorldData + ? this.server.getWorldGenSettings() + : loadWorldGenSettings( + this.server.storageSource, + this.server.worldLoaderContext.datapackWorldgen(), + loading.info().dimensionKey() + ); + final var worldDataAndGenSettings = new LevelDataAndDimensions.WorldDataAndGenSettings(this.server.getWorldData(), worldGenSettings); + + if (loading.info().dimensionKey() == Level.OVERWORLD) { + final var primaryLevelData = ((PrimaryLevelData) this.server.getWorldData()); + primaryLevelData.checkName(loading.data().bukkitName()); + primaryLevelData.setModdedInfo(this.server.getServerModName(), this.server.getModdedStatus().shouldReportAsModified()); + } - if (summary.requiresManualConversion()) { - LOGGER.info("This world must be opened in an older version (like 1.6.4) to be safely converted"); - return new LevelDataResult(null, true); - } + if (this.server.options.has("forceUpgrade")) { + Main.forceUpgrade(this.server.storageSource, DataFixers.getDataFixer(), this.server.options.has("eraseCache"), () -> true, this.server.registryAccess(), this.server.options.has("recreateRegionFiles")); + } - if (!summary.isCompatible()) { - LOGGER.info("This world was created by an incompatible version."); - return new LevelDataResult(null, true); - } - } else { - return new LevelDataResult(null, false); + this.server.createLevel(stem, loading, worldDataAndGenSettings); + } + + public static WorldGenSettings loadWorldGenSettings( + final LevelStorageSource.LevelStorageAccess access, final net.minecraft.core.HolderLookup.Provider registryAccess, final ResourceKey dimension + ) { + return LevelStorageSource.readExistingSavedData(access, dimension, registryAccess, WorldGenSettings.TYPE) + .getOrThrow(err -> new IllegalStateException("Unable to read or access the world gen settings file for dimension " + dimension.identifier() + ". " + err)); + } + + private static String defaultWorldName(final String levelId, final ResourceKey stemKey) { + if (stemKey == LevelStem.OVERWORLD) { + return levelId; } + return levelId + "_" + worldType(stemKey); + } - return new LevelDataResult(dataTag, false); + private static String worldType(final ResourceKey stemKey) { + if (stemKey == LevelStem.NETHER) { + return World.Environment.NETHER.toString().toLowerCase(Locale.ROOT); + } + if (stemKey == LevelStem.END) { + return World.Environment.THE_END.toString().toLowerCase(Locale.ROOT); + } + if (stemKey == LevelStem.OVERWORLD) { + return World.Environment.NORMAL.toString().toLowerCase(Locale.ROOT); + } + return stemKey.identifier().getNamespace() + "_" + stemKey.identifier().getPath(); } + } diff --git a/paper-server/src/main/java/io/papermc/paper/world/flag/PaperFeatureFlagProviderImpl.java b/paper-server/src/main/java/io/papermc/paper/world/flag/PaperFeatureFlagProviderImpl.java index c0953bb67dcf..0594a87cda7b 100644 --- a/paper-server/src/main/java/io/papermc/paper/world/flag/PaperFeatureFlagProviderImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/world/flag/PaperFeatureFlagProviderImpl.java @@ -9,8 +9,6 @@ import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.flag.FeatureFlags; import org.bukkit.FeatureFlag; -import org.bukkit.GameRule; -import org.bukkit.craftbukkit.CraftGameRule; import org.bukkit.craftbukkit.entity.CraftEntityType; import org.bukkit.craftbukkit.potion.CraftPotionType; import org.bukkit.entity.EntityType; @@ -49,8 +47,6 @@ static FeatureElement getFeatureElement(final FeatureDependant dependant) { return CraftEntityType.bukkitToMinecraft(entityType); } else if (dependant instanceof final PotionType potionType) { return CraftPotionType.bukkitToMinecraft(potionType); - } else if (dependant instanceof final GameRule gameRule) { - return () -> CraftGameRule.bukkitToMinecraft(gameRule).requiredFeatures(); } else { throw new IllegalArgumentException(dependant + " is not a valid feature dependant"); } diff --git a/paper-server/src/main/java/io/papermc/paper/world/migration/LegacyCraftBukkitWorldMigration.java b/paper-server/src/main/java/io/papermc/paper/world/migration/LegacyCraftBukkitWorldMigration.java new file mode 100644 index 000000000000..761ce73d2047 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/migration/LegacyCraftBukkitWorldMigration.java @@ -0,0 +1,255 @@ +package io.papermc.paper.world.migration; + +import com.mojang.logging.LogUtils; +import com.mojang.serialization.Dynamic; +import io.papermc.paper.world.saveddata.PaperLevelOverrides; +import io.papermc.paper.world.saveddata.PaperWorldMetadata; +import io.papermc.paper.world.saveddata.PaperWorldPDC; +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.UUID; +import net.minecraft.core.HolderLookup; +import net.minecraft.resources.ResourceKey; +import net.minecraft.util.datafix.DataFixers; +import net.minecraft.world.entity.raid.Raids; +import net.minecraft.world.level.TicketStorage; +import net.minecraft.world.level.border.WorldBorder; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.dimension.end.EnderDragonFight; +import net.minecraft.world.level.gamerules.GameRuleMap; +import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.saveddata.SavedDataType; +import net.minecraft.world.level.saveddata.WanderingTraderData; +import net.minecraft.world.level.saveddata.WeatherData; +import net.minecraft.world.level.storage.LevelResource; +import net.minecraft.world.level.storage.SavedDataStorage; +import net.minecraft.world.level.timers.TimerQueue; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +import org.slf4j.Logger; + +import static java.util.Objects.requireNonNullElseGet; + +@NullMarked +final class LegacyCraftBukkitWorldMigration { + private static final Logger LOGGER = LogUtils.getClassLogger(); + private static final String[] WORLD_BORDER_RELATIVE_CANDIDATES = {"minecraft/world_border.dat", "world_border.dat"}; + + private final WorldMigrationContext context; + private final Path sourceRoot; + private final Path targetDataRoot; + private final Path targetDimensionPath; + private final HolderLookup.Provider registryAccess; + private @Nullable Path initialExplicitWorldBorder; + private List sourceDataRoots = List.of(); + + static void migrate(final WorldMigrationContext context) throws IOException { + new LegacyCraftBukkitWorldMigration(context).run(); + } + + static void migrateApiWorld(final WorldMigrationContext context) throws IOException { + final Path sourceRoot = context.rootAccess().parent().getLevelPath(context.worldName()); + if (!Files.isDirectory(sourceRoot)) { + return; + } + if (!Files.isRegularFile(sourceRoot.resolve(LevelResource.LEVEL_DATA_FILE.id())) && !Files.isRegularFile(sourceRoot.resolve(LevelResource.OLD_LEVEL_DATA_FILE.id()))) { + return; + } + migrate(context); + } + + private LegacyCraftBukkitWorldMigration(final WorldMigrationContext context) { + this.context = context; + this.sourceRoot = context.rootAccess().parent().getLevelPath(context.worldName()); + this.targetDataRoot = context.targetDataRoot(); + this.targetDimensionPath = context.targetDimensionPath(); + this.registryAccess = context.registryAccess(); + } + + private void run() throws IOException { + LOGGER.info("Starting legacy CraftBukkit import for world '{}' ({})", this.context.worldName(), this.context.dimensionKey().identifier()); + try (final var sourceAccess = this.context.rootAccess().parent().createAccess(this.context.worldName())) { + Files.createDirectories(this.targetDataRoot); + + final List initialDimensionRoots = this.locateDimensionRoots(); + this.initialExplicitWorldBorder = this.findExplicitFile(this.explicitDataRoots(initialDimensionRoots), WORLD_BORDER_RELATIVE_CANDIDATES); + for (final Path sourceDimensionRoot : initialDimensionRoots) { + WorldMigrationSupport.migrateDimensionDirectories(sourceDimensionRoot, this.targetDimensionPath); + } + + WorldMigrationSupport.migratePaperWorldConfig(this.sourceRoot, this.targetDimensionPath); + + final var levelDataResult = WorldMigrationSupport.readFixedLevelData(sourceAccess); + if (levelDataResult.fatalError()) { + throw new IOException("Failed to read level data for world migration"); + } + + this.sourceDataRoots = this.locateSavedDataRoots(); + + final SavedDataStorage tempStorage = new SavedDataStorage(this.targetDataRoot, DataFixers.getDataFixer(), this.registryAccess); + this.migrateSharedSavedData(); + this.migrateLegacyCraftBukkitPaperData(tempStorage, levelDataResult.dataTag()); + tempStorage.saveAndJoin(); + } + deleteMigratedSeparateRoot(this.sourceRoot); + LOGGER.info("Completed legacy CraftBukkit import for world '{}' ({})", this.context.worldName(), this.context.dimensionKey().identifier()); + } + + private void migrateSharedSavedData() throws IOException { + this.copySavedDataIfPresent(GameRuleMap.TYPE); + this.copySavedDataIfPresent(WeatherData.TYPE); + this.copySavedDataIfPresent(TimerQueue.TYPE); + this.copySavedDataIfPresent(WanderingTraderData.TYPE); + this.copySavedDataIfPresent(TicketStorage.TYPE); + this.copySavedDataIfPresent(Raids.TYPE); + this.copySavedDataIfPresent(WorldGenSettings.TYPE); + this.migrateLegacyWorldBorder(); + + if (this.context.stemKey() == LevelStem.END) { + this.copySavedDataIfPresent(EnderDragonFight.TYPE); + } else { + Files.deleteIfExists(WorldMigrationSupport.savedDataPath(this.targetDataRoot, EnderDragonFight.TYPE)); + } + } + + static List rootOwnedDataRoots(final Path sourceRoot) { + return List.of( + WorldMigrationSupport.getStorageFolder(LevelStem.OVERWORLD.identifier(), sourceRoot).resolve(LevelResource.DATA.id()), + sourceRoot.resolve(LevelResource.DATA.id()) + ); + } + + private void migrateLegacyWorldBorder() throws IOException { + if (this.initialExplicitWorldBorder != null && Files.isRegularFile(this.initialExplicitWorldBorder)) { + WorldMigrationSupport.copySavedDataFileIfPresent(this.targetDataRoot, this.initialExplicitWorldBorder, WorldBorder.TYPE, true); + return; + } + + final Path rootOwnedBorder = this.findExplicitFile(rootOwnedDataRoots(this.sourceRoot), WORLD_BORDER_RELATIVE_CANDIDATES); + if (rootOwnedBorder != null) { + WorldMigrationSupport.copySavedDataFileIfPresent(this.targetDataRoot, rootOwnedBorder, WorldBorder.TYPE, true); + return; + } + + this.copySavedDataIfPresent(WorldBorder.TYPE); + } + + private List locateDimensionRoots() { + final ResourceKey stemKey = this.context.stemKey(); + final LinkedHashSet roots = new LinkedHashSet<>(); + roots.add(WorldMigrationSupport.getStorageFolder(stemKey.identifier(), this.sourceRoot)); + roots.add(DimensionType.getStorageFolder(this.context.dimensionKey(), this.sourceRoot)); + if (hasSourceContent(this.sourceRoot)) { + roots.add(this.sourceRoot); + } + + if (stemKey == LevelStem.NETHER) { + roots.add(this.sourceRoot.resolve("DIM-1")); + } else if (stemKey == LevelStem.END) { + roots.add(this.sourceRoot.resolve("DIM1")); + } + + roots.removeIf(path -> !Files.isDirectory(path)); + return List.copyOf(roots); + } + + private static boolean hasSourceContent(final Path root) { + if (Files.isDirectory(root.resolve(LevelResource.DATA.id()))) { + return true; + } + if (Files.isRegularFile(root.resolve(WorldMigrationSupport.PAPER_WORLD_CONFIG)) || Files.isRegularFile(root.resolve("uid.dat"))) { + return true; + } + for (final String directory : WorldMigrationSupport.DIMENSION_DIRECTORIES) { + if (Files.isDirectory(root.resolve(directory))) { + return true; + } + } + return false; + } + + private List locateSavedDataRoots() { + final LinkedHashSet dataRoots = new LinkedHashSet<>(); + dataRoots.addAll(this.explicitDataRoots(this.locateDimensionRoots())); + dataRoots.addAll(rootOwnedDataRoots(this.sourceRoot)); + return List.copyOf(dataRoots); + } + + private void copySavedDataIfPresent( + final SavedDataType type + ) throws IOException { + WorldMigrationSupport.copySavedDataIfPresent(this.sourceDataRoots, this.targetDataRoot, type, true); + } + + private static @Nullable UUID readLegacyUuid(final Path sourceRoot) { + final Path fileId = sourceRoot.resolve("uid.dat"); + if (!Files.isRegularFile(fileId)) { + return null; + } + + try (DataInputStream inputStream = new DataInputStream(Files.newInputStream(fileId))) { + return new UUID(inputStream.readLong(), inputStream.readLong()); + } catch (final IOException ex) { + LOGGER.warn("Failed to read {}", fileId, ex); + return null; + } + } + + private static void deleteMigratedSeparateRoot(final Path sourceRoot) throws IOException { + if (!Files.exists(sourceRoot)) { + return; + } + + try (final var paths = Files.walk(sourceRoot)) { + for (final Path path : paths.sorted(java.util.Comparator.reverseOrder()).toList()) { + Files.deleteIfExists(path); + } + } + } + + private @Nullable Path findExplicitFile( + final List dataRoots, + final String... relativeCandidates + ) { + for (final Path dataRoot : dataRoots) { + for (final String relativeCandidate : relativeCandidates) { + final Path source = dataRoot.resolve(relativeCandidate); + if (Files.isRegularFile(source)) { + return source; + } + } + } + return null; + } + + private void migrateLegacyCraftBukkitPaperData( + final SavedDataStorage targetStorage, + final @Nullable Dynamic levelData + ) { + targetStorage.set(PaperWorldMetadata.TYPE, new PaperWorldMetadata(requireNonNullElseGet(readLegacyUuid(this.sourceRoot), UUID::randomUUID))); + final PaperWorldPDC preservedPdc = WorldMigrationSupport.readLegacyPdc(levelData, this.registryAccess); + if (preservedPdc != null) { + targetStorage.set(PaperWorldPDC.TYPE, preservedPdc); + } + targetStorage.set(PaperLevelOverrides.TYPE, PaperLevelOverrides.createFromRawLevelData(levelData)); + } + + private List explicitDataRoots(final List dimensionRoots) { + final LinkedHashSet dataRoots = new LinkedHashSet<>(); + if (dimensionRoots.contains(this.sourceRoot)) { + dataRoots.add(this.sourceRoot.resolve(net.minecraft.world.level.storage.LevelResource.DATA.id())); + } + for (final Path dimensionRoot : dimensionRoots) { + if (dimensionRoot.equals(this.sourceRoot)) { + continue; + } + dataRoots.add(dimensionRoot.resolve(net.minecraft.world.level.storage.LevelResource.DATA.id())); + } + return List.copyOf(dataRoots); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/migration/VanillaWorldMigration.java b/paper-server/src/main/java/io/papermc/paper/world/migration/VanillaWorldMigration.java new file mode 100644 index 000000000000..53d45281db83 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/migration/VanillaWorldMigration.java @@ -0,0 +1,146 @@ +package io.papermc.paper.world.migration; + +import com.mojang.logging.LogUtils; +import com.mojang.serialization.Dynamic; +import io.papermc.paper.world.saveddata.PaperLevelOverrides; +import io.papermc.paper.world.saveddata.PaperWorldPDC; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import net.minecraft.resources.ResourceKey; +import net.minecraft.util.datafix.DataFixers; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.gamerules.GameRuleMap; +import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.saveddata.SavedDataType; +import net.minecraft.world.level.saveddata.WanderingTraderData; +import net.minecraft.world.level.saveddata.WeatherData; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.level.storage.LevelResource; +import net.minecraft.world.level.storage.PrimaryLevelData; +import net.minecraft.world.level.storage.SavedDataStorage; +import net.minecraft.world.level.timers.TimerQueue; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +import org.slf4j.Logger; + +@NullMarked +final class VanillaWorldMigration { + private static final Logger LOGGER = LogUtils.getClassLogger(); + + private VanillaWorldMigration() { + } + + static void migrate(final WorldMigrationContext context) throws IOException { + LOGGER.info("Starting Vanilla import for world '{}' ({})", context.worldName(), context.dimensionKey().identifier()); + final var levelDataResult = WorldMigrationSupport.readFixedLevelData(context.rootAccess()); + if (levelDataResult.fatalError()) { + throw new IOException("Failed to read level data for world migration"); + } + + final boolean rootOwnsThisWorld = context.dimensionKey() == Level.OVERWORLD; + + if (rootOwnsThisWorld) { + WorldMigrationSupport.migratePaperWorldConfig(context.baseRoot(), context.targetDimensionPath()); + } + + migrateSavedData(context); + createLevelOverrides(context, levelDataResult.dataTag()); + migrateLegacyPdc(context, levelDataResult.dataTag()); + + LOGGER.info("Completed Vanilla import for world '{}' ({})", context.worldName(), context.dimensionKey().identifier()); + } + + private static void migrateSavedData(final WorldMigrationContext context) throws IOException { + final List sourceDataRoots = List.of( + context.rootAccess().getDimensionPath(Level.OVERWORLD).resolve(LevelResource.DATA.id()), + context.baseRoot().resolve(LevelResource.DATA.id()) + ); + + WorldMigrationSupport.copySavedDataIfPresent(sourceDataRoots, context.targetDataRoot(), WorldGenSettings.TYPE, false); + WorldMigrationSupport.copySavedDataIfPresent(sourceDataRoots, context.targetDataRoot(), GameRuleMap.TYPE, false); + WorldMigrationSupport.copySavedDataIfPresent(sourceDataRoots, context.targetDataRoot(), WeatherData.TYPE, false); + if (context.dimensionKey() == Level.OVERWORLD) { + WorldMigrationSupport.copySavedDataIfPresent(sourceDataRoots, context.targetDataRoot(), TimerQueue.TYPE, false); + WorldMigrationSupport.copySavedDataIfPresent(sourceDataRoots, context.targetDataRoot(), WanderingTraderData.TYPE, false); + + deleteLegacyRootCopyIfMigrated(context, WorldGenSettings.TYPE); + deleteLegacyRootCopyIfMigrated(context, GameRuleMap.TYPE); + deleteLegacyRootCopyIfMigrated(context, WeatherData.TYPE); + deleteLegacyRootCopyIfMigrated(context, TimerQueue.TYPE); + deleteLegacyRootCopyIfMigrated(context, WanderingTraderData.TYPE); + } + } + + private static void deleteLegacyRootCopyIfMigrated(final WorldMigrationContext context, final SavedDataType type) throws IOException { + final Path target = WorldMigrationSupport.savedDataPath(context.targetDataRoot(), type); + if (!Files.isRegularFile(target)) { + return; + } + + final Path rootSource = WorldMigrationSupport.savedDataPath(context.baseRoot().resolve(LevelResource.DATA.id()), type); + Files.deleteIfExists(rootSource); + } + + private static void createLevelOverrides( + final WorldMigrationContext context, + final @Nullable Dynamic levelData + ) { + final Path levelOverridesPath = WorldMigrationSupport.savedDataPath(context.targetDataRoot(), PaperLevelOverrides.TYPE); + if (Files.exists(levelOverridesPath)) { + return; + } + + final PaperLevelOverrides levelOverrides = PaperLevelOverrides.createFromRawLevelData(levelData); + final ResourceKey explicitPaperRespawnDimension = resolveExplicitPaperRespawnDimension(levelData); + if (explicitPaperRespawnDimension == null && context.dimensionKey() != resolveVanillaRespawnDimension(levelData)) { + levelOverrides.setInitialized(false); + } + + final SavedDataStorage targetStorage = new SavedDataStorage(context.targetDataRoot(), DataFixers.getDataFixer(), context.registryAccess()); + targetStorage.set(PaperLevelOverrides.TYPE, levelOverrides); + targetStorage.saveAndJoin(); + } + + private static void migrateLegacyPdc( + final WorldMigrationContext context, + final @Nullable Dynamic levelData + ) { + final Path pdcPath = WorldMigrationSupport.savedDataPath(context.targetDataRoot(), PaperWorldPDC.TYPE); + if (!Files.exists(pdcPath)) { + final PaperWorldPDC pdc = WorldMigrationSupport.readLegacyPdc(levelData, context.registryAccess()); + if (pdc != null) { + final SavedDataStorage targetStorage = new SavedDataStorage(context.targetDataRoot(), DataFixers.getDataFixer(), context.registryAccess()); + targetStorage.set(PaperWorldPDC.TYPE, pdc); + targetStorage.saveAndJoin(); + + WorldMigrationSupport.clearLegacyPdc(levelData); + context.rootAccess().saveLevelData(levelData); + } + } + } + + private static @Nullable ResourceKey resolveExplicitPaperRespawnDimension(final @Nullable Dynamic levelData) { + if (levelData == null) { + return null; + } + + return levelData.get(PrimaryLevelData.PAPER_RESPAWN_DIMENSION) + .read(Level.RESOURCE_KEY_CODEC) + .result() + .orElse(null); + } + + private static ResourceKey resolveVanillaRespawnDimension(final @Nullable Dynamic levelData) { + if (levelData == null) { + return Level.OVERWORLD; + } + + return levelData.get("spawn") + .read(LevelData.RespawnData.CODEC) + .result() + .map(LevelData.RespawnData::dimension) + .orElse(Level.OVERWORLD); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/migration/WorldFolderMigration.java b/paper-server/src/main/java/io/papermc/paper/world/migration/WorldFolderMigration.java new file mode 100644 index 000000000000..9b6da34e53e5 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/migration/WorldFolderMigration.java @@ -0,0 +1,102 @@ +package io.papermc.paper.world.migration; + +import com.mojang.logging.LogUtils; +import io.papermc.paper.world.saveddata.PaperLevelOverrides; +import io.papermc.paper.world.saveddata.PaperWorldMetadata; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import net.minecraft.core.HolderLookup; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.storage.LevelResource; +import net.minecraft.world.level.storage.LevelStorageSource; +import org.jspecify.annotations.NullMarked; +import org.slf4j.Logger; + +@NullMarked +public final class WorldFolderMigration { + private static final Logger LOGGER = LogUtils.getClassLogger(); + private static final boolean DISABLE_MIGRATION_DELAY = Boolean.getBoolean("paper.disableMigrationDelay"); + private static boolean startupMigrationWarningShown; + + private WorldFolderMigration() { + } + + private enum MigrationMode { + LEGACY_CRAFTBUKKIT_MIGRATION, + VANILLA_MIGRATION, + NO_OP + } + + public static void migrateStartupWorld( + final LevelStorageSource.LevelStorageAccess rootAccess, + final HolderLookup.Provider registryAccess, + final String worldName, + final ResourceKey stemKey, + final ResourceKey dimensionKey + ) throws IOException { + final WorldMigrationContext context = new WorldMigrationContext(rootAccess, registryAccess, worldName, stemKey, dimensionKey); + final MigrationMode mode = classifyStartupMigration(context); + if (mode != MigrationMode.NO_OP) { + warnAndDelayStartupMigration(); + } + switch (mode) { + case LEGACY_CRAFTBUKKIT_MIGRATION -> LegacyCraftBukkitWorldMigration.migrate(context); + case VANILLA_MIGRATION -> VanillaWorldMigration.migrate(context); + case NO_OP -> {} + } + } + + public static void migrateApiWorld( + final LevelStorageSource.LevelStorageAccess rootAccess, + final HolderLookup.Provider registryAccess, + final String worldName, + final ResourceKey stemKey, + final ResourceKey dimensionKey + ) throws IOException { + LegacyCraftBukkitWorldMigration.migrateApiWorld(new WorldMigrationContext(rootAccess, registryAccess, worldName, stemKey, dimensionKey)); + } + + private static synchronized void warnAndDelayStartupMigration() throws IOException { + if (startupMigrationWarningShown) { + return; + } + startupMigrationWarningShown = true; + LOGGER.warn("===================== ! ALERT ! ====================="); + LOGGER.warn("World storage migration is required during startup."); + LOGGER.warn("If you do not have a backup: interrupt the server now. Use Ctrl+C, your panel kill function, etc."); + LOGGER.warn("====================================================="); + LOGGER.warn("Continuing in 30 seconds..."); + if (DISABLE_MIGRATION_DELAY) { + LOGGER.warn("Migration delay disabled by system property."); + } else { + try { + Thread.sleep(30_000L); + } catch (final InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new IOException("Interrupted while waiting before startup world migration", ex); + } + } + LOGGER.info("Continuing with startup world migration."); + } + + private static MigrationMode classifyStartupMigration(final WorldMigrationContext context) { + if (!context.rootAccess().hasWorldData()) { + return MigrationMode.NO_OP; + } + if (!context.rootAccess().getLevelId().equals(context.worldName()) && Files.isDirectory(context.rootAccess().parent().getLevelPath(context.worldName()))) { + return MigrationMode.LEGACY_CRAFTBUKKIT_MIGRATION; + } + return hasCurrentPaperData(context.rootAccess(), context.dimensionKey()) ? MigrationMode.NO_OP : MigrationMode.VANILLA_MIGRATION; + } + + static boolean hasCurrentPaperData(final LevelStorageSource.LevelStorageAccess rootAccess, final ResourceKey dimensionKey) { + final Path targetDataRoot = rootAccess.getDimensionPath(dimensionKey).resolve(LevelResource.DATA.id()); + return Files.isRegularFile(WorldMigrationSupport.savedDataPath(targetDataRoot, PaperWorldMetadata.TYPE)) + && Files.isRegularFile(WorldMigrationSupport.savedDataPath(targetDataRoot, PaperLevelOverrides.TYPE)) + && Files.isRegularFile(WorldMigrationSupport.savedDataPath(targetDataRoot, WorldGenSettings.TYPE)); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/migration/WorldMigrationContext.java b/paper-server/src/main/java/io/papermc/paper/world/migration/WorldMigrationContext.java new file mode 100644 index 000000000000..4e6a8b78f01d --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/migration/WorldMigrationContext.java @@ -0,0 +1,31 @@ +package io.papermc.paper.world.migration; + +import java.nio.file.Path; +import net.minecraft.core.HolderLookup; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.storage.LevelResource; +import net.minecraft.world.level.storage.LevelStorageSource; +import org.jspecify.annotations.NullMarked; + +@NullMarked +record WorldMigrationContext( + LevelStorageSource.LevelStorageAccess rootAccess, + HolderLookup.Provider registryAccess, + String worldName, + ResourceKey stemKey, + ResourceKey dimensionKey +) { + Path baseRoot() { + return this.rootAccess.levelDirectory.path(); + } + + Path targetDimensionPath() { + return this.rootAccess.getDimensionPath(this.dimensionKey); + } + + Path targetDataRoot() { + return this.targetDimensionPath().resolve(LevelResource.DATA.id()); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/migration/WorldMigrationSupport.java b/paper-server/src/main/java/io/papermc/paper/world/migration/WorldMigrationSupport.java new file mode 100644 index 000000000000..f4384489d524 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/migration/WorldMigrationSupport.java @@ -0,0 +1,187 @@ +package io.papermc.paper.world.migration; + +import com.mojang.logging.LogUtils; +import com.mojang.serialization.Dynamic; +import io.papermc.paper.world.saveddata.PaperWorldPDC; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.List; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.NbtException; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.ReportedNbtException; +import net.minecraft.resources.Identifier; +import net.minecraft.util.datafix.DataFixers; +import net.minecraft.util.worldupdate.UpgradeProgress; +import net.minecraft.world.level.saveddata.SavedDataType; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.LevelSummary; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +import org.slf4j.Logger; + +@NullMarked +final class WorldMigrationSupport { + private static final Logger LOGGER = LogUtils.getClassLogger(); + static final List DIMENSION_DIRECTORIES = List.of("region", "entities", "poi"); + static final String PAPER_WORLD_CONFIG = "paper-world.yml"; + + private WorldMigrationSupport() { + } + + static @Nullable PaperWorldPDC readLegacyPdc(final @Nullable Dynamic levelData, final HolderLookup.Provider registryAccess) { + if (levelData == null) { + return null; + } + + return levelData.get("BukkitValues") + .result() + .flatMap(dynamic -> PaperWorldPDC.CODEC.parse(registryAccess.createSerializationContext(NbtOps.INSTANCE), dynamic.convert(NbtOps.INSTANCE).getValue()).result()) + .orElse(null); + } + + static void clearLegacyPdc(final Dynamic levelData) { + levelData.remove("BukkitValues"); + } + + static Path getStorageFolder(final Identifier dimension, final Path baseFolder) { + return dimension.resolveAgainst(baseFolder.resolve("dimensions")); + } + + static void migratePaperWorldConfig(final Path sourceRoot, final Path targetDimensionPath) throws IOException { + final Path source = sourceRoot.resolve(PAPER_WORLD_CONFIG); + if (!Files.isRegularFile(source)) { + return; + } + + final Path target = targetDimensionPath.resolve(PAPER_WORLD_CONFIG); + if (Files.exists(target)) { + return; + } + + Files.createDirectories(target.getParent()); + LOGGER.info("Migrating Paper world config from {} to {}", source, target); + Files.move(source, target); + } + + static void migrateDimensionDirectories(final Path sourceDimensionRoot, final Path targetDimensionPath) throws IOException { + if (sourceDimensionRoot.equals(targetDimensionPath)) { + return; + } + + for (final String directory : DIMENSION_DIRECTORIES) { + final Path source = sourceDimensionRoot.resolve(directory); + if (!Files.exists(source)) { + continue; + } + + final Path target = targetDimensionPath.resolve(directory); + LOGGER.info("Migrating world directory from {} to {}", source, target); + mergeMove(source, target); + } + } + + private static void mergeMove(final Path source, final Path target) throws IOException { + if (Files.isDirectory(source)) { + Files.createDirectories(target); + try (final var entries = Files.list(source)) { + for (final Path child : entries.toList()) { + mergeMove(child, target.resolve(child.getFileName().toString())); + } + } + tryDeleteIfEmpty(source); + return; + } + + if (Files.exists(target)) { + throw new IOException("Refusing to overwrite existing migrated file " + target + " while moving " + source); + } + + Files.createDirectories(target.getParent()); + Files.move(source, target); + } + + private static void tryDeleteIfEmpty(final Path path) throws IOException { + try (final var entries = Files.list(path)) { + if (entries.findAny().isPresent()) { + return; + } + } + Files.deleteIfExists(path); + } + + record LevelDataResult(@Nullable Dynamic dataTag, boolean fatalError) {} + + static LevelDataResult readFixedLevelData(final LevelStorageSource.LevelStorageAccess access) { + if (!access.hasWorldData()) { + return new LevelDataResult(null, false); + } + + final Dynamic levelDataUnfixed; + try { + levelDataUnfixed = access.getUnfixedDataTagWithFallback(); + } catch (final NbtException | ReportedNbtException | IOException ex) { + LOGGER.error("Failed to load world data. World files may be corrupted. Shutting down.", ex); + return new LevelDataResult(null, true); + } + + final LevelSummary summary = access.fixAndGetSummaryFromTag(levelDataUnfixed); + if (summary.requiresManualConversion()) { + LOGGER.info("This world must be opened in an older version (like 1.6.4) to be safely converted"); + return new LevelDataResult(null, true); + } + + if (!summary.isCompatible()) { + LOGGER.info("This world was created by an incompatible version."); + return new LevelDataResult(null, true); + } + + return new LevelDataResult(DataFixers.getFileFixer().fix(access, levelDataUnfixed, new UpgradeProgress()), false); + } + + static Path savedDataPath(final Path dataRoot, final SavedDataType type) { + return type.id().withSuffix(".dat").resolveAgainst(dataRoot); + } + + static boolean copySavedDataIfPresent( + final List sourceDataRoots, + final Path targetDataRoot, + final SavedDataType type, + final boolean overwrite + ) throws IOException { + return copySavedDataFileIfPresent(targetDataRoot, findSavedDataFile(sourceDataRoots, type), type, overwrite); + } + + static boolean copySavedDataFileIfPresent( + final Path targetDataRoot, + final @Nullable Path source, + final SavedDataType type, + final boolean overwrite + ) throws IOException { + if (source == null) { + return false; + } + + final Path target = savedDataPath(targetDataRoot, type); + if (!overwrite && Files.isRegularFile(target)) { + return true; + } + + Files.createDirectories(target.getParent()); + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); + return true; + } + + static @Nullable Path findSavedDataFile(final List dataRoots, final SavedDataType type) { + for (final Path dataRoot : dataRoots) { + final Path source = savedDataPath(dataRoot, type); + if (Files.isRegularFile(source)) { + return source; + } + } + return null; + } + +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/saveddata/PaperLevelOverrides.java b/paper-server/src/main/java/io/papermc/paper/world/saveddata/PaperLevelOverrides.java new file mode 100644 index 000000000000..b0bed5d36339 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/saveddata/PaperLevelOverrides.java @@ -0,0 +1,245 @@ +package io.papermc.paper.world.saveddata; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.util.Mth; +import net.minecraft.util.datafix.DataFixTypes; +import net.minecraft.world.Difficulty; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelSettings; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.saveddata.SavedDataType; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.level.storage.PrimaryLevelData; +import net.minecraft.world.level.storage.ServerLevelData; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +public final class PaperLevelOverrides extends SavedData implements ServerLevelData { + private static final Codec LEGACY_GAME_TYPE_CODEC = Codec.INT.xmap(GameType::byId, GameType::getId); + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + RespawnData.CODEC.fieldOf("spawn").forGetter(levelOverrides -> levelOverrides.respawnData), + Codec.LONG.fieldOf("game_time").forGetter(levelOverrides -> levelOverrides.gameTime), + Codec.BOOL.fieldOf("initialized").forGetter(levelOverrides -> levelOverrides.initialized), + LEGACY_GAME_TYPE_CODEC.fieldOf("game_type").forGetter(levelOverrides -> levelOverrides.gameType), + LevelSettings.DifficultySettings.CODEC.fieldOf("difficulty_settings").forGetter(levelOverrides -> levelOverrides.difficultySettings) + ).apply(instance, PaperLevelOverrides::new)); + public static final SavedDataType TYPE = new SavedDataType<>( + Identifier.fromNamespaceAndPath(Identifier.PAPER_NAMESPACE, "level_overrides"), + PaperLevelOverrides::new, + CODEC, + DataFixTypes.NONE + ); + + private RespawnData respawnData; + private long gameTime; + private boolean initialized; + private GameType gameType; + private LevelSettings.DifficultySettings difficultySettings; + private transient @Nullable PrimaryLevelData rootData; + private transient ResourceKey dimension = Level.OVERWORLD; + + private PaperLevelOverrides() { + this(RespawnData.DEFAULT, 0L, false, GameType.SURVIVAL, LevelSettings.DifficultySettings.DEFAULT); + } + + private PaperLevelOverrides( + final RespawnData respawnData, + final long gameTime, + final boolean initialized, + final GameType gameType, + final LevelSettings.DifficultySettings difficultySettings + ) { + this.respawnData = respawnData.normalized(); + this.gameTime = gameTime; + this.initialized = initialized; + this.gameType = gameType; + this.difficultySettings = difficultySettings; + } + + public static PaperLevelOverrides createFromLiveLevelData(final PrimaryLevelData rootData) { + return new PaperLevelOverrides( + RespawnData.fromVanilla(rootData.getRespawnData()), + rootData.getGameTime(), + false, + rootData.getGameType(), + new LevelSettings.DifficultySettings(rootData.getDifficulty(), rootData.isHardcore(), rootData.isDifficultyLocked()) + ); + } + + public static PaperLevelOverrides createFromRawLevelData(final @Nullable Dynamic levelData) { + if (levelData == null) { + return new PaperLevelOverrides(); + } + return new PaperLevelOverrides( + RespawnData.fromVanilla(levelData.get("spawn").read(LevelData.RespawnData.CODEC).result().orElse(LevelData.RespawnData.DEFAULT)), + levelData.get("Time").asLong(0L), + levelData.get("initialized").asBoolean(true), + GameType.byId(levelData.get("GameType").asInt(GameType.SURVIVAL.getId())), + levelData.get("difficulty_settings").read(LevelSettings.DifficultySettings.CODEC).result().orElse(LevelSettings.DifficultySettings.DEFAULT) + ); + } + + public PaperLevelOverrides attach(final PrimaryLevelData rootData, final ResourceKey dimension) { + this.rootData = rootData; + this.dimension = dimension; + return this; + } + + private void setRespawn(final RespawnData respawnData) { + final RespawnData normalizedRespawnData = respawnData.normalized(); + if (!this.respawnData.equals(normalizedRespawnData)) { + this.respawnData = normalizedRespawnData; + // Do not syncRootData here, handled separately in MinecraftServer#updateEffectiveRespawnData + this.setDirty(); + } + } + + public void setDifficulty(final Difficulty difficulty) { + if (this.difficultySettings.difficulty() != difficulty) { + this.setDifficultySettings(new LevelSettings.DifficultySettings(difficulty, this.difficultySettings.hardcore(), this.difficultySettings.locked())); + } + } + + public void setHardcore(final boolean hardcore) { + if (this.difficultySettings.hardcore() != hardcore) { + this.setDifficultySettings(new LevelSettings.DifficultySettings(this.difficultySettings.difficulty(), hardcore, this.difficultySettings.locked())); + } + } + + public void setDifficultyLocked(final boolean difficultyLocked) { + if (this.difficultySettings.locked() != difficultyLocked) { + this.setDifficultySettings(new LevelSettings.DifficultySettings(this.difficultySettings.difficulty(), this.difficultySettings.hardcore(), difficultyLocked)); + } + } + + @Override + public LevelData.RespawnData getRespawnData() { + return this.respawnData.toVanilla(this.dimension); + } + + @Override + public long getGameTime() { + return this.gameTime; + } + + @Override + public GameType getGameType() { + return this.gameType; + } + + @Override + public String getLevelName() { + return this.rootDataOrThrow().getLevelName(); + } + + @Override + public void setGameTime(final long time) { + if (this.gameTime != time) { + this.gameTime = time; + this.syncRootData(rootData -> rootData.setGameTime(time)); + this.setDirty(); + } + } + + @Override + public void setSpawn(final LevelData.RespawnData respawnData) { + this.setRespawn(RespawnData.fromVanilla(respawnData)); + } + + @Override + public void setGameType(final GameType gameType) { + if (this.gameType != gameType) { + this.gameType = gameType; + this.syncRootData(rootData -> rootData.setGameType(gameType)); + this.setDirty(); + } + } + + @Override + public boolean isHardcore() { + return this.difficultySettings.hardcore(); + } + + @Override + public boolean isAllowCommands() { + return this.rootDataOrThrow().isAllowCommands(); + } + + @Override + public boolean isInitialized() { + return this.initialized; + } + + @Override + public void setInitialized(final boolean initialized) { + if (this.initialized != initialized) { + this.initialized = initialized; + this.syncRootData(rootData -> rootData.setInitialized(initialized)); + this.setDirty(); + } + } + + @Override + public Difficulty getDifficulty() { + return this.difficultySettings.difficulty(); + } + + @Override + public boolean isDifficultyLocked() { + return this.difficultySettings.locked(); + } + + private PrimaryLevelData rootDataOrThrow() { + if (this.rootData == null) { + throw new IllegalStateException("PaperWorldLevelOverrides not attached"); + } + return this.rootData; + } + + private void setDifficultySettings(final LevelSettings.DifficultySettings difficultySettings) { + if (!this.difficultySettings.equals(difficultySettings)) { + this.difficultySettings = difficultySettings; + this.syncRootData(rootData -> rootData.settings = rootData.settings + .withDifficulty(difficultySettings.difficulty()) + .withHardcore(difficultySettings.hardcore()) + .withDifficultyLock(difficultySettings.locked())); + this.setDirty(); + } + } + + private void syncRootData(final java.util.function.Consumer action) { + if (this.dimension == Level.OVERWORLD && this.rootData != null) { + action.accept(this.rootData); + } + } + + // Like LevelData.RespawnData, but with a BlockPos instead of a GlobalPos + public record RespawnData(BlockPos pos, float yaw, float pitch) { + public static final RespawnData DEFAULT = new RespawnData(BlockPos.ZERO, 0.0F, 0.0F); + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + BlockPos.CODEC.fieldOf("pos").forGetter(RespawnData::pos), + Codec.floatRange(-180.0F, 180.0F).fieldOf("yaw").forGetter(RespawnData::yaw), + Codec.floatRange(-90.0F, 90.0F).fieldOf("pitch").forGetter(RespawnData::pitch) + ).apply(instance, RespawnData::new)); + + public RespawnData normalized() { + return new RespawnData(this.pos.immutable(), Mth.wrapDegrees(this.yaw), Mth.clamp(this.pitch, -90.0F, 90.0F)); + } + + public LevelData.RespawnData toVanilla(final ResourceKey dimension) { + final RespawnData normalized = this.normalized(); + return LevelData.RespawnData.of(dimension, normalized.pos, normalized.yaw, normalized.pitch); + } + + public static RespawnData fromVanilla(final LevelData.RespawnData respawnData) { + return new RespawnData(respawnData.pos(), respawnData.yaw(), respawnData.pitch()).normalized(); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/saveddata/PaperWorldMetadata.java b/paper-server/src/main/java/io/papermc/paper/world/saveddata/PaperWorldMetadata.java new file mode 100644 index 000000000000..a90f216d4647 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/saveddata/PaperWorldMetadata.java @@ -0,0 +1,42 @@ +package io.papermc.paper.world.saveddata; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.Objects; +import java.util.UUID; +import net.minecraft.core.UUIDUtil; +import net.minecraft.resources.Identifier; +import net.minecraft.util.datafix.DataFixTypes; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.saveddata.SavedDataType; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public final class PaperWorldMetadata extends SavedData { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + UUIDUtil.CODEC.fieldOf("uuid").forGetter(PaperWorldMetadata::uuid) + ).apply(instance, PaperWorldMetadata::new)); + public static final SavedDataType TYPE = new SavedDataType<>( + Identifier.fromNamespaceAndPath(Identifier.PAPER_NAMESPACE, "metadata"), + () -> new PaperWorldMetadata(UUID.randomUUID()), + CODEC, + DataFixTypes.NONE + ); + + private UUID uuid; + + public PaperWorldMetadata(final UUID uuid) { + this.uuid = uuid; + } + + public UUID uuid() { + return this.uuid; + } + + public void set(final UUID uuid) { + if (!Objects.equals(this.uuid, uuid)) { + this.uuid = uuid; + this.setDirty(); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/saveddata/PaperWorldPDC.java b/paper-server/src/main/java/io/papermc/paper/world/saveddata/PaperWorldPDC.java new file mode 100644 index 000000000000..9a38a0824f50 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/saveddata/PaperWorldPDC.java @@ -0,0 +1,42 @@ +package io.papermc.paper.world.saveddata; + +import com.mojang.serialization.Codec; +import java.util.Objects; +import net.minecraft.resources.Identifier; +import net.minecraft.util.datafix.DataFixTypes; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.saveddata.SavedDataType; +import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; +import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public final class PaperWorldPDC extends SavedData { + private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry(); + public static final Codec CODEC = CraftPersistentDataContainer.createCodec(DATA_TYPE_REGISTRY) + .xmap(PaperWorldPDC::new, PaperWorldPDC::persistentData); + public static final SavedDataType TYPE = new SavedDataType<>( + Identifier.fromNamespaceAndPath(Identifier.PAPER_NAMESPACE, "persistent_data_container"), + () -> new PaperWorldPDC(new CraftPersistentDataContainer(DATA_TYPE_REGISTRY)), + CODEC, + DataFixTypes.NONE + ); + + private final CraftPersistentDataContainer persistentData; + + public PaperWorldPDC(final CraftPersistentDataContainer persistentData) { + this.persistentData = persistentData; + } + + public CraftPersistentDataContainer persistentData() { + return this.persistentData; + } + + public void setFrom(final CraftPersistentDataContainer source) { + if (!Objects.equals(this.persistentData, source)) { + this.persistentData.clear(); + this.persistentData.putAll(source.getTagsCloned()); + this.setDirty(); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java b/paper-server/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java index 8fa41e9d4210..58d12afe9b03 100644 --- a/paper-server/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java +++ b/paper-server/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java @@ -69,7 +69,7 @@ protected boolean compute() { } else if (blockY >= falseAtAndAboveY) { return false; } else { - double d = Mth.map(blockY, trueAtAndBelowY, falseAtAndAboveY, 1.0D, 0.0D); + double d = Mth.map(blockY, trueAtAndBelowY, falseAtAndAboveY, 1.0, 0.0); RandomSource randomSource = positionalRandomFactory.at(this.context.blockX, blockY, this.context.blockZ); return (double)randomSource.nextFloat() < d; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftChunk.java index 09baa8c13a56..757080357da8 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftChunk.java @@ -54,8 +54,8 @@ public class CraftChunk implements Chunk { public CraftChunk(net.minecraft.world.level.chunk.LevelChunk chunk) { this.level = chunk.level; - this.x = chunk.getPos().x; - this.z = chunk.getPos().z; + this.x = chunk.getPos().x(); + this.z = chunk.getPos().z(); } public CraftChunk(ServerLevel level, int x, int z) { @@ -114,7 +114,7 @@ public Block getBlock(int x, int y, int z) { @Override public boolean isEntitiesLoaded() { - return this.getCraftWorld().getHandle().areEntitiesLoaded(ChunkPos.asLong(this.x, this.z)); // Paper - chunk system + return this.getCraftWorld().getHandle().areEntitiesLoaded(ChunkPos.pack(this.x, this.z)); // Paper - chunk system } @Override @@ -392,7 +392,7 @@ public static ChunkSnapshot getEmptyChunkSnapshot(int x, int z, CraftWorld world empty[i] = true; if (biome != null) { - biome[i] = (PalettedContainer>) biomeCodec.parse(NbtOps.INSTANCE, biomeCodec.encodeStart(NbtOps.INSTANCE, actual.getSection(i).getBiomes()).getOrThrow()).getOrThrow(SerializableChunkData.ChunkReadException::new); + biome[i] = actual.getSection(i).getBiomes().copy(); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java index 251eb8dba549..00cf1caf9488 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java @@ -103,7 +103,7 @@ public Material getBlockType(int x, int y, int z) { public final BlockData getBlockData(int x, int y, int z) { this.validateChunkCoordinates(x, y, z); - return CraftBlockData.fromData(this.blockIds[this.getSectionIndex(y)].get(x, y & 0xF, z)); + return this.blockIds[this.getSectionIndex(y)].get(x, y & 0xF, z).asBlockData(); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftGameRule.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftGameRule.java index 5d42203a9079..57525b527ebd 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftGameRule.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftGameRule.java @@ -1,7 +1,7 @@ package org.bukkit.craftbukkit; import com.mojang.serialization.DataResult; -import io.papermc.paper.util.Holderable; +import io.papermc.paper.world.flag.PaperFeatureDependent; import java.util.function.BiFunction; import java.util.function.Function; import net.minecraft.core.Holder; @@ -11,7 +11,7 @@ import org.jspecify.annotations.NullMarked; @NullMarked -public class CraftGameRule extends GameRule implements Holderable> { +public class CraftGameRule extends GameRule implements PaperFeatureDependent> { public static final BiFunction, IllegalArgumentException> INVALID_VALUE = (value, error) -> { return new IllegalArgumentException("Invalid value: %s (%s)".formatted(value, error.message())); @@ -44,22 +44,22 @@ public Holder> getHolder() { @Override public NamespacedKey getKey() { - return Holderable.super.getKey(); + return PaperFeatureDependent.super.getKey(); } @Override public int hashCode() { - return Holderable.super.implHashCode(); + return PaperFeatureDependent.super.implHashCode(); } @Override public boolean equals(final Object obj) { - return Holderable.super.implEquals(obj); + return PaperFeatureDependent.super.implEquals(obj); } @Override public String toString() { - return Holderable.super.implToString(); + return PaperFeatureDependent.super.implToString(); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftParticle.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftParticle.java index 3f940a89a336..c994b2aafdb1 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftParticle.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftParticle.java @@ -82,7 +82,7 @@ public static ParticleOptions createParticleParam(Particle particle, D data) public static T convertLegacy(T object) { if (object instanceof MaterialData mat) { - return (T) CraftBlockData.fromData(CraftMagicNumbers.getBlock(mat)); + return (T) CraftMagicNumbers.getBlock(mat).asBlockData(); } return object; @@ -132,7 +132,7 @@ public ParticleOptions createParticleParam(Particle.DustOptions data) { BiFunction, CraftParticle> itemStackFunction = (name, particle) -> new CraftParticle<>(name, particle, ItemStack.class) { @Override public ParticleOptions createParticleParam(ItemStack data) { - return new ItemParticleOption((net.minecraft.core.particles.ParticleType) this.getHandle(), CraftItemStack.asNMSCopy(data)); + return new ItemParticleOption((net.minecraft.core.particles.ParticleType) this.getHandle(), CraftItemStack.asTemplate(data)); } }; @@ -158,7 +158,7 @@ public ParticleOptions createParticleParam(Vibration data) { PositionSource source; if (data.getDestination() instanceof Vibration.Destination.BlockDestination) { Location destination = ((Vibration.Destination.BlockDestination) data.getDestination()).getLocation(); - source = new BlockPositionSource(CraftLocation.toBlockPosition(destination)); + source = new BlockPositionSource(CraftLocation.toBlockPos(destination)); } else if (data.getDestination() instanceof Vibration.Destination.EntityDestination) { Entity destination = ((CraftEntity) ((Vibration.Destination.EntityDestination) data.getDestination()).getEntity()).getHandle(); source = new EntityPositionSource(destination, destination.getEyeHeight()); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java index 21d594a39d09..5339ff469a38 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java @@ -7,7 +7,6 @@ import java.util.Random; import java.util.function.Consumer; import java.util.function.Predicate; -import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; @@ -15,15 +14,15 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; +import net.minecraft.world.attribute.EnvironmentAttributes; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.SpawnGroupData; -import net.minecraft.world.level.Level; +import net.minecraft.world.level.MoonPhase; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.block.ChorusFlowerBlock; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; -import net.minecraft.world.level.portal.TeleportTransition; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.RegionAccessor; @@ -80,12 +79,12 @@ public Biome getComputedBiome(int x, int y, int z) { @Override public void setBiome(int x, int y, int z, Biome biome) { - Preconditions.checkArgument(biome != Biome.CUSTOM, "Cannot set the biome to %s", biome); - Holder biomeBase = CraftBiome.bukkitToMinecraftHolder(biome); - this.setBiome(x, y, z, biomeBase); + Holder b = CraftBiome.bukkitToMinecraftHolder(biome); + Preconditions.checkArgument(b != null, "Cannot set the biome to %s", biome); + this.setBiome(x, y, z, b); } - public abstract void setBiome(int x, int y, int z, Holder biomeBase); + public abstract void setBiome(int x, int y, int z, Holder biome); @Override public BlockState getBlockState(int x, int y, int z) { @@ -99,7 +98,7 @@ public io.papermc.paper.block.fluid.FluidData getFluidData(final int x, final in @Override public BlockData getBlockData(int x, int y, int z) { - return CraftBlockData.fromData(this.getData(x, y, z)); + return this.getData(x, y, z).asBlockData(); } @Override @@ -142,7 +141,7 @@ public int getHighestBlockYAt(Location location, org.bukkit.HeightMap heightMap) @Override public boolean generateTree(Location location, Random random, TreeType treeType) { - BlockPos pos = CraftLocation.toBlockPosition(location); + BlockPos pos = CraftLocation.toBlockPos(location); return this.generateTree(this.getHandle(), this.getHandle().getMinecraftWorld().getChunkSource().getGenerator(), pos, new RandomSourceWrapper(random), treeType); } @@ -156,10 +155,10 @@ public boolean generateTree(Location location, Random random, TreeType treeType, @Override public boolean generateTree(Location location, Random random, TreeType treeType, Predicate predicate) { - BlockPos pos = CraftLocation.toBlockPosition(location); + BlockPos pos = CraftLocation.toBlockPos(location); BlockStateListPopulator populator = new BlockStateListPopulator(this.getHandle()); boolean result = this.generateTree(populator, this.getHandle().getMinecraftWorld().getChunkSource().getGenerator(), pos, new RandomSourceWrapper(random), treeType); - populator.placeSomeBlocks(predicate == null ? ($ -> true) : predicate); + populator.placeSomeBlocks(predicate == null ? (_ -> true) : predicate); return result; } @@ -466,7 +465,8 @@ public net.minecraft.world.entity.Entity createEntity(Location location, Class Mth.square(128D)) { + if (end.distanceToSqr(start) > Mth.square(128.0)) { return false; // Return early if the distance is greater than 128 blocks } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java index 0d343556fbb6..f2d20cc87a89 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java @@ -218,7 +218,7 @@ public B get(NamespacedKey namespacedKey) { @NotNull @Override public Stream stream() { - return this.minecraftRegistry.keySet().stream().map(minecraftKey -> this.get(CraftNamespacedKey.fromMinecraft(minecraftKey))); + return this.minecraftRegistry.keySet().stream().map(key -> this.get(CraftNamespacedKey.fromMinecraft(key))); } @NotNull diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java index da49c8005592..e892844dae4b 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -10,12 +10,12 @@ import com.google.common.collect.MapMaker; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.serialization.Dynamic; -import com.mojang.serialization.Lifecycle; import io.papermc.paper.configuration.GlobalConfiguration; import io.papermc.paper.configuration.PaperServerConfiguration; import io.papermc.paper.configuration.ServerConfiguration; import io.papermc.paper.world.PaperWorldLoader; +import io.papermc.paper.world.migration.WorldFolderMigration; +import io.papermc.paper.world.saveddata.PaperLevelOverrides; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; @@ -26,6 +26,7 @@ import java.io.InputStreamReader; import java.net.InetAddress; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -44,6 +45,7 @@ import java.util.logging.Logger; import java.util.stream.Collectors; import javax.imageio.ImageIO; +import net.md_5.bungee.api.chat.BaseComponent; import net.minecraft.Optionull; import net.minecraft.advancements.AdvancementHolder; import net.minecraft.commands.CommandSourceStack; @@ -53,8 +55,8 @@ import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ReloadableServerRegistries; import net.minecraft.server.WorldLoader; @@ -76,7 +78,6 @@ import net.minecraft.tags.TagKey; import net.minecraft.util.GsonHelper; import net.minecraft.util.datafix.DataFixers; -import net.minecraft.world.Difficulty; import net.minecraft.world.damagesource.DamageType; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.ai.village.VillageSiege; @@ -95,24 +96,23 @@ import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.item.crafting.RepairItemRecipe; import net.minecraft.world.level.CustomSpawner; -import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.GameType; -import net.minecraft.world.level.LevelSettings; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.PatrolSpawner; import net.minecraft.world.level.levelgen.PhantomSpawner; import net.minecraft.world.level.levelgen.WorldDimensions; +import net.minecraft.world.level.levelgen.WorldGenSettings; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.saveddata.maps.MapId; import net.minecraft.world.level.saveddata.maps.MapItemSavedData; -import net.minecraft.world.level.storage.LevelDataAndDimensions; +import net.minecraft.world.level.storage.LevelResource; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.PlayerDataStorage; import net.minecraft.world.level.storage.PrimaryLevelData; -import net.minecraft.world.level.validation.ContentValidationException; +import net.minecraft.world.level.storage.SavedDataStorage; import org.apache.commons.lang3.StringUtils; import org.bukkit.BanList; import org.bukkit.Bukkit; @@ -260,8 +260,6 @@ import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.error.MarkedYAMLException; -import net.md_5.bungee.api.chat.BaseComponent; // Spigot - public final class CraftServer implements Server { private final String serverName = io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); private final String serverVersion; @@ -780,7 +778,7 @@ public String getWorldType() { @Override public boolean getGenerateStructures() { - return this.getServer().getWorldData().worldGenOptions().generateStructures(); + return this.getServer().getWorldGenSettings().options().generateStructures(); } @Override @@ -1053,7 +1051,6 @@ public void reload() { this.enablePlugins(PluginLoadOrder.STARTUP); this.enablePlugins(PluginLoadOrder.POSTWORLD); this.spark.registerCommandAfterPlugins(this); // Paper - spark - if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins // Paper start - brigadier command API io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // to clear invalid state for event fire below io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // call commands event for regular plugins @@ -1180,10 +1177,8 @@ public World createWorld(WorldCreator creator) { String name = creator.name(); ChunkGenerator chunkGenerator = creator.generator(); BiomeProvider biomeProvider = creator.biomeProvider(); - File folder = new File(this.getWorldContainer(), name); - World world = this.getWorld(name); - // Paper start + World world = this.getWorld(name); World worldByKey = this.getWorld(creator.key()); if (world != null || worldByKey != null) { if (world == worldByKey) { @@ -1191,11 +1186,6 @@ public World createWorld(WorldCreator creator) { } throw new IllegalArgumentException("Cannot create a world with key " + creator.key() + " and name " + name + " one (or both) already match a world that exists"); } - // Paper end - - if (folder.exists()) { - Preconditions.checkArgument(folder.isDirectory(), "File (%s) exists and isn't a folder", name); - } if (chunkGenerator == null) { chunkGenerator = this.getGenerator(name); @@ -1212,104 +1202,110 @@ public World createWorld(WorldCreator creator) { default -> throw new IllegalArgumentException("Illegal dimension (" + creator.environment() + ")"); }; - LevelStorageSource.LevelStorageAccess levelStorageAccess; - try { - levelStorageAccess = LevelStorageSource.createDefault(this.getWorldContainer().toPath()).validateAndCreateAccess(name, actualDimension); - } catch (IOException | ContentValidationException ex) { - throw new RuntimeException(ex); - } - - boolean hardcore = creator.hardcore(); - - PrimaryLevelData primaryLevelData; + final ResourceKey dimensionKey = PaperWorldLoader.dimensionKey(creator.key()); WorldLoader.DataLoadContext context = this.console.worldLoaderContext; RegistryAccess.Frozen registryAccess = context.datapackDimensions(); net.minecraft.core.Registry contextLevelStemRegistry = registryAccess.lookupOrThrow(Registries.LEVEL_STEM); - Dynamic dataTag = PaperWorldLoader.getLevelData(levelStorageAccess).dataTag(); - if (dataTag != null) { - LevelDataAndDimensions levelDataAndDimensions = LevelStorageSource.getLevelDataAndDimensions( - dataTag, context.dataConfiguration(), contextLevelStemRegistry, context.datapackWorldgen() + final LevelStem configuredStem = this.console.registryAccess().lookupOrThrow(Registries.LEVEL_STEM).getValue(actualDimension); + if (configuredStem == null) { + throw new IllegalStateException("Missing configured level stem " + actualDimension); + } + try { + WorldFolderMigration.migrateApiWorld( + this.console.storageSource, + this.console.registryAccess(), + name, + actualDimension, + dimensionKey ); - primaryLevelData = (PrimaryLevelData) levelDataAndDimensions.worldData(); - registryAccess = levelDataAndDimensions.dimensions().dimensionsRegistryAccess(); - } else { - LevelSettings levelSettings; + } catch (final IOException ex) { + throw new RuntimeException("Failed to migrate legacy world " + name, ex); + } + PaperWorldLoader.LoadedWorldData loadedWorldData = PaperWorldLoader.loadWorldData( + this.console, + dimensionKey, + name + ); + final PrimaryLevelData primaryLevelData = (PrimaryLevelData) this.console.getWorldData(); + WorldGenSettings worldGenSettings = LevelStorageSource.readExistingSavedData(this.console.storageSource, dimensionKey, this.console.registryAccess(), WorldGenSettings.TYPE) + .result() + .orElse(null); + if (worldGenSettings == null) { WorldOptions worldOptions = new WorldOptions(creator.seed(), creator.generateStructures(), creator.bonusChest()); - WorldDimensions worldDimensions; DedicatedServerProperties.WorldDimensionData properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse((creator.generatorSettings().isEmpty()) ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT)); - levelSettings = new LevelSettings( - name, - GameType.byId(this.getDefaultGameMode().getValue()), - hardcore, Difficulty.EASY, - false, - new GameRules(context.dataConfiguration().enabledFeatures()), - context.dataConfiguration() - ); - worldDimensions = properties.create(context.datapackWorldgen()); + WorldDimensions worldDimensions = properties.create(context.datapackWorldgen()); WorldDimensions.Complete complete = worldDimensions.bake(contextLevelStemRegistry); - Lifecycle lifecycle = complete.lifecycle().add(context.datapackWorldgen().allRegistriesLifecycle()); + if (complete.dimensions().getValue(actualDimension) == null) { + throw new IllegalStateException("Missing generated level stem " + actualDimension + " for world " + name); + } - primaryLevelData = new PrimaryLevelData(levelSettings, worldOptions, complete.specialWorldProperty(), lifecycle); + worldGenSettings = new WorldGenSettings(worldOptions, worldDimensions); registryAccess = complete.dimensionsRegistryAccess(); + loadedWorldData.levelOverrides().setHardcore(creator.hardcore()); + loadedWorldData = new PaperWorldLoader.LoadedWorldData( + loadedWorldData.bukkitName(), + loadedWorldData.uuid(), + loadedWorldData.pdc(), + loadedWorldData.levelOverrides() + ); } + final WorldGenSettings genSettingsFinal = worldGenSettings; contextLevelStemRegistry = registryAccess.lookupOrThrow(Registries.LEVEL_STEM); - primaryLevelData.customDimensions = contextLevelStemRegistry; - primaryLevelData.checkName(name); - primaryLevelData.setModdedInfo(this.console.getServerModName(), this.console.getModdedStatus().shouldReportAsModified()); if (this.console.options.has("forceUpgrade")) { - net.minecraft.server.Main.forceUpgrade(levelStorageAccess, primaryLevelData, DataFixers.getDataFixer(), this.console.options.has("eraseCache"), () -> true, registryAccess, this.console.options.has("recreateRegionFiles")); + net.minecraft.server.Main.forceUpgrade(this.console.storageSource, DataFixers.getDataFixer(), this.console.options.has("eraseCache"), () -> true, registryAccess, this.console.options.has("recreateRegionFiles")); } - long i = BiomeManager.obfuscateSeed(primaryLevelData.worldGenOptions().seed()); - List list = ImmutableList.of( - new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(primaryLevelData) - ); - LevelStem customStem = contextLevelStemRegistry.getValue(actualDimension); + long biomeZoomSeed = BiomeManager.obfuscateSeed(genSettingsFinal.options().seed()); + LevelStem customStem = genSettingsFinal.dimensions().get(actualDimension).orElse(null); + if (customStem == null) { + customStem = contextLevelStemRegistry.getValue(actualDimension); + } + if (customStem == null) { + throw new IllegalStateException("Missing level stem for world " + name + " using key " + actualDimension); + } - WorldInfo worldInfo = new CraftWorldInfo(primaryLevelData, levelStorageAccess, creator.environment(), customStem.type().value(), customStem.generator(), this.getHandle().getServer().registryAccess()); // Paper - Expose vanilla BiomeProvider from WorldInfo + WorldInfo worldInfo = new CraftWorldInfo(loadedWorldData.bukkitName(), genSettingsFinal.options().seed(), primaryLevelData.enabledFeatures(), creator.environment(), customStem.type().value(), customStem.generator(), this.getHandle().getServer().registryAccess(), loadedWorldData.uuid()); if (biomeProvider == null && chunkGenerator != null) { biomeProvider = chunkGenerator.getDefaultBiomeProvider(worldInfo); } - ResourceKey dimensionKey; - String levelName = this.getServer().getProperties().levelName; - if (name.equals(levelName + "_nether")) { - dimensionKey = net.minecraft.world.level.Level.NETHER; - } else if (name.equals(levelName + "_the_end")) { - dimensionKey = net.minecraft.world.level.Level.END; - } else { - dimensionKey = ResourceKey.create(Registries.DIMENSION, Identifier.fromNamespaceAndPath(creator.key().namespace(), creator.key().value())); - } + final SavedDataStorage savedDataStorage = new SavedDataStorage(this.console.storageSource.getDimensionPath(dimensionKey).resolve(LevelResource.DATA.id()), this.console.getFixerUpper(), this.console.registryAccess()); + savedDataStorage.set(WorldGenSettings.TYPE, new WorldGenSettings(genSettingsFinal.options(), genSettingsFinal.dimensions())); + List list = ImmutableList.of( + new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(savedDataStorage) + ); ServerLevel serverLevel = new ServerLevel( this.console, this.console.executor, - levelStorageAccess, - primaryLevelData, + this.console.storageSource, + genSettingsFinal, dimensionKey, customStem, primaryLevelData.isDebugWorld(), - i, + biomeZoomSeed, creator.environment() == Environment.NORMAL ? list : ImmutableList.of(), true, - this.console.overworld().getRandomSequences(), + actualDimension, creator.environment(), - chunkGenerator, biomeProvider + chunkGenerator, + biomeProvider, + savedDataStorage, + loadedWorldData ); if (!(this.worlds.containsKey(name.toLowerCase(Locale.ROOT)))) { return null; } - this.console.addLevel(serverLevel); // Paper - Put world into worldlist before initing the world; move up - this.console.initWorld(serverLevel, primaryLevelData, primaryLevelData.worldGenOptions()); + this.console.addLevel(serverLevel); + this.console.initWorld(serverLevel); serverLevel.setSpawnSettings(true); - // Paper - Put world into worldlist before initing the world; move up this.getServer().prepareLevel(serverLevel); @@ -1354,7 +1350,6 @@ public boolean unloadWorld(World world, boolean save) { handle.getChunkSource().close(save); io.papermc.paper.FeatureHooks.closeEntityManager(handle, save); // SPIGOT-6722: close entityManager // Paper - chunk system - handle.levelStorageAccess.close(); } catch (Exception ex) { this.getLogger().log(Level.SEVERE, null, ex); } @@ -1373,7 +1368,7 @@ public World getRespawnWorld() { public void setRespawnWorld(final World world) { Preconditions.checkArgument(world != null, "world cannot be null"); - this.console.overworld().serverLevelData.respawnDimension = ((CraftWorld) world).getHandle().dimension(); + ((PrimaryLevelData) this.console.getWorldData()).respawnDimension = ((CraftWorld) world).getHandle().dimension(); this.console.updateEffectiveRespawnData(); } @@ -1408,7 +1403,7 @@ public World getWorld(net.kyori.adventure.key.Key worldKey) { public void addWorld(World world) { // Check if a World already exists with the UID. if (this.getWorld(world.getUID()) != null) { - System.out.println("World " + world.getName() + " is a duplicate of another world and has been prevented from loading. Please delete the uid.dat file from " + world.getName() + "'s world directory if you want to be able to load the duplicate world."); + System.out.println("World " + world.getName() + " is a duplicate of another world and has been prevented from loading. Please remove or change the duplicated Paper world metadata before loading it again."); return; } this.worlds.put(world.getName().toLowerCase(Locale.ROOT), world); @@ -1475,7 +1470,7 @@ public boolean addRecipe(Recipe recipe, boolean resendRecipes) { return false; } } - toAdd.addToCraftingManager(); + toAdd.addToRecipeManager(); // Paper start - API for updating recipes on clients if (true || resendRecipes) { // Always needs to be resent now... TODO this.playerList.reloadRecipes(); @@ -1507,7 +1502,7 @@ public List getRecipesFor(ItemStack result) { public Recipe getRecipe(NamespacedKey recipeKey) { Preconditions.checkArgument(recipeKey != null, "NamespacedKey recipeKey cannot be null"); - return this.getServer().getRecipeManager().byKey(CraftRecipe.toMinecraft(recipeKey)).map(RecipeHolder::toBukkitRecipe).orElse(null); + return this.getServer().getRecipeManager().byKey(CraftNamespacedKey.toResourceKey(Registries.RECIPE, recipeKey)).map(RecipeHolder::toBukkitRecipe).orElse(null); } private CraftingContainer createCraftingContainer() { @@ -1553,19 +1548,18 @@ public ItemCraftResult craftItemResult(ItemStack[] craftingMatrix, World world, Optional> recipe = this.getNMSRecipe(craftingMatrix, craftingContainer, craftWorld); // Generate the resulting ItemStack from the Crafting Matrix - net.minecraft.world.item.ItemStack itemstack = net.minecraft.world.item.ItemStack.EMPTY; + net.minecraft.world.item.ItemStack result = net.minecraft.world.item.ItemStack.EMPTY; if (recipe.isPresent()) { RecipeHolder recipeCrafting = recipe.get(); craftingContainer.setCurrentRecipe(recipeCrafting); if (craftResult.setRecipeUsed(craftPlayer.getHandle(), recipeCrafting)) { - itemstack = recipeCrafting.value().assemble(craftingContainer.asCraftInput(), craftWorld.getHandle().registryAccess()); + result = recipeCrafting.value().assemble(craftingContainer.asCraftInput()); } } // Call Bukkit event to check for matrix/result changes. - net.minecraft.world.item.ItemStack result = CraftEventFactory.callPreCraftEvent(craftingContainer, craftResult, itemstack, container.getBukkitView(), recipe.map(RecipeHolder::value).orElse(null) instanceof RepairItemRecipe); - + result = CraftEventFactory.callPreCraftEvent(craftingContainer, craftResult, result, container.getBukkitView(), recipe.map(RecipeHolder::value).orElse(null) instanceof RepairItemRecipe); return this.createItemCraftResult(recipe, CraftItemStack.asBukkitCopy(result), craftingContainer); } @@ -1577,43 +1571,40 @@ public ItemCraftResult craftItemResult(ItemStack[] craftingMatrix, World world) // Create a players Crafting Inventory and get the recipe CraftingContainer craftingContainer = this.createCraftingContainer(); - Optional> recipe = this.getNMSRecipe(craftingMatrix, craftingContainer, craftWorld); // Generate the resulting ItemStack from the Crafting Matrix - net.minecraft.world.item.ItemStack itemStack = net.minecraft.world.item.ItemStack.EMPTY; - - if (recipe.isPresent()) { - itemStack = recipe.get().value().assemble(craftingContainer.asCraftInput(), craftWorld.getHandle().registryAccess()); - } + final ItemStack result = recipe.map(holder -> { + return CraftItemStack.asBukkitCopy(holder.value().assemble(craftingContainer.asCraftInput())); + }).orElseGet(ItemStack::empty); - return this.createItemCraftResult(recipe, CraftItemStack.asBukkitCopy(itemStack), craftingContainer); + return this.createItemCraftResult(recipe, result, craftingContainer); } - private CraftItemCraftResult createItemCraftResult(Optional> recipe, ItemStack itemStack, CraftingContainer inventoryCrafting) { - CraftItemCraftResult craftItemResult = new CraftItemCraftResult(itemStack); + private CraftItemCraftResult createItemCraftResult(Optional> recipe, ItemStack result, CraftingContainer craftingContainer) { + CraftItemCraftResult craftItemResult = new CraftItemCraftResult(result); // tl;dr: this is an API adopted implementation of ResultSlot#onTake - final CraftingInput.Positioned positionedCraftInput = inventoryCrafting.asPositionedCraftInput(); + final CraftingInput.Positioned positionedCraftInput = craftingContainer.asPositionedCraftInput(); final CraftingInput craftingInput = positionedCraftInput.input(); recipe.map((holder) -> holder.value().getRemainingItems(craftingInput)).ifPresent((remainingItems) -> { // Set the resulting matrix items and overflow items for (int height = 0; height < craftingInput.height(); height++) { for (int width = 0; width < craftingInput.width(); width++) { - final int inventorySlot = width + positionedCraftInput.left() + (height + positionedCraftInput.top()) * inventoryCrafting.getWidth(); - net.minecraft.world.item.ItemStack itemInMenu = inventoryCrafting.getItem(inventorySlot); + final int inventorySlot = width + positionedCraftInput.left() + (height + positionedCraftInput.top()) * craftingContainer.getWidth(); + net.minecraft.world.item.ItemStack itemInMenu = craftingContainer.getItem(inventorySlot); net.minecraft.world.item.ItemStack remainingItem = remainingItems.get(width + height * craftingInput.width()); if (!itemInMenu.isEmpty()) { - inventoryCrafting.removeItem(inventorySlot, 1); - itemInMenu = inventoryCrafting.getItem(inventorySlot); + craftingContainer.removeItem(inventorySlot, 1); + itemInMenu = craftingContainer.getItem(inventorySlot); } if (!remainingItem.isEmpty()) { if (itemInMenu.isEmpty()) { - inventoryCrafting.setItem(inventorySlot, remainingItem); + craftingContainer.setItem(inventorySlot, remainingItem); } else if (net.minecraft.world.item.ItemStack.isSameItemSameComponents(itemInMenu, remainingItem)) { remainingItem.grow(itemInMenu.getCount()); - inventoryCrafting.setItem(inventorySlot, remainingItem); + craftingContainer.setItem(inventorySlot, remainingItem); } else { craftItemResult.getOverflowItems().add(CraftItemStack.asBukkitCopy(remainingItem)); } @@ -1622,23 +1613,23 @@ private CraftItemCraftResult createItemCraftResult(Optional> getNMSRecipe(ItemStack[] craftingMatrix, CraftingContainer inventoryCrafting, CraftWorld world) { + private Optional> getNMSRecipe(ItemStack[] craftingMatrix, CraftingContainer craftingContainer, CraftWorld world) { Preconditions.checkArgument(craftingMatrix != null, "craftingMatrix must not be null"); Preconditions.checkArgument(craftingMatrix.length == 9, "craftingMatrix must be an array of length 9"); Preconditions.checkArgument(world != null, "world must not be null"); for (int i = 0; i < craftingMatrix.length; i++) { - inventoryCrafting.setItem(i, CraftItemStack.asNMSCopy(craftingMatrix[i])); + craftingContainer.setItem(i, CraftItemStack.asNMSCopy(craftingMatrix[i])); } - return this.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, inventoryCrafting.asCraftInput(), world.getHandle()); + return this.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftingContainer.asCraftInput(), world.getHandle()); } @Override @@ -1661,8 +1652,8 @@ public boolean removeRecipe(NamespacedKey recipeKey, boolean resendRecipes) { Preconditions.checkArgument(recipeKey != null, "recipeKey == null"); // Paper start - resend recipes on successful removal - final ResourceKey> minecraftKey = CraftRecipe.toMinecraft(recipeKey); - final boolean removed = this.getServer().getRecipeManager().removeRecipe(minecraftKey); + final ResourceKey> id = CraftNamespacedKey.toResourceKey(Registries.RECIPE, recipeKey); + final boolean removed = this.getServer().getRecipeManager().removeRecipe(id); if (removed/* && resendRecipes*/) { // TODO Always need to resend them rn - deprecate this method? this.playerList.reloadRecipes(); } @@ -1838,7 +1829,7 @@ public CraftMapView createMap(World world) { ServerLevel level = ((CraftWorld) world).getHandle(); // creates a new map at world spawn with the scale of 3, without tracking position and unlimited tracking - BlockPos spawn = level.getLevelData().getRespawnData().pos(); + BlockPos spawn = level.serverLevelData.getRespawnData().pos(); MapId newId = MapItem.createNewSavedData(level, spawn.getX(), spawn.getZ(), 3, false, false, level.dimension()); return level.getMapData(newId).mapView; } @@ -1854,7 +1845,7 @@ public ItemStack createExplorerMap(World world, Location location, StructureType if (structureLocation == null) { throw new IllegalStateException("Could not find a structure for " + structureType); } - BlockPos structurePos = CraftLocation.toBlockPosition(structureLocation); + BlockPos structurePos = CraftLocation.toBlockPos(structureLocation); // Create map with trackingPosition = true, unlimitedTracking = true net.minecraft.world.item.ItemStack stack = MapItem.create(level, structurePos.getX(), structurePos.getZ(), MapView.Scale.NORMAL.getValue(), true, true); @@ -2180,7 +2171,12 @@ public WorldMetadataStore getWorldMetadata() { @Override public File getWorldContainer() { - return this.getServer().storageSource.getDimensionPath(net.minecraft.world.level.Level.OVERWORLD).getParent().toFile(); + return this.getServer().storageSource.getLevelDirectory().path().getParent().toFile(); + } + + @Override + public Path getLevelDirectory() { + return this.getServer().storageSource.getLevelDirectory().path(); } @Override @@ -2441,7 +2437,7 @@ public KeyedBossBar createBossBar(NamespacedKey key, String title, BarColor barC Preconditions.checkArgument(barColor != null, "BarColor key cannot be null"); Preconditions.checkArgument(barStyle != null, "BarStyle key cannot be null"); - CustomBossEvent bossBattleCustom = this.getServer().getCustomBossEvents().create(CraftNamespacedKey.toMinecraft(key), CraftChatMessage.fromString(title, true)[0]); + CustomBossEvent bossBattleCustom = this.getServer().getCustomBossEvents().create(net.minecraft.util.RandomSource.create(), CraftNamespacedKey.toMinecraft(key), CraftChatMessage.fromString(title, true)[0]); CraftKeyedBossbar craftKeyedBossbar = new CraftKeyedBossbar(bossBattleCustom); craftKeyedBossbar.setColor(barColor); craftKeyedBossbar.setStyle(barStyle); @@ -2547,7 +2543,7 @@ public BlockData createBlockData(org.bukkit.Material material, String data) { Preconditions.checkArgument(type != null, "Provided material must be a block"); } - return CraftBlockData.newData(type, data); + return CraftBlockData.fromString(type, data); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java index e786c0cce9ce..ca0c490d81a4 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java @@ -111,21 +111,21 @@ public enum CraftStatistic { PICKUP(Identifier.withDefaultNamespace("picked_up")), USE_ITEM(Identifier.withDefaultNamespace("used")); // End generate - CraftStatisticType - private final Identifier minecraftKey; + private final Identifier key; private final org.bukkit.Statistic bukkit; private static final BiMap statistics; static { ImmutableBiMap.Builder statisticBuilder = ImmutableBiMap.builder(); for (CraftStatistic statistic : CraftStatistic.values()) { - statisticBuilder.put(statistic.minecraftKey, statistic.bukkit); + statisticBuilder.put(statistic.key, statistic.bukkit); } statistics = statisticBuilder.build(); } - private CraftStatistic(Identifier minecraftKey) { - this.minecraftKey = minecraftKey; + private CraftStatistic(Identifier key) { + this.key = key; this.bukkit = org.bukkit.Statistic.valueOf(this.name()); Preconditions.checkState(this.bukkit != null, "Bukkit statistic %s does not exist", this.name()); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index ebc65e3338c6..0a3c290edcf6 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -35,12 +35,12 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; +import net.minecraft.core.QuartPos; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.network.protocol.game.ClientboundLevelEventPacket; -import net.minecraft.network.protocol.game.ClientboundSetTimePacket; import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; import net.minecraft.network.protocol.game.ClientboundSoundPacket; import net.minecraft.resources.Identifier; @@ -74,10 +74,8 @@ import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.gamerules.GameRule; -import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.levelgen.structure.StructureStart; import net.minecraft.world.level.storage.LevelData; -import net.minecraft.world.level.storage.LevelResource; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; @@ -89,6 +87,7 @@ import org.bukkit.Difficulty; import org.bukkit.Effect; import org.bukkit.FluidCollisionMode; +import org.bukkit.GameRules; import org.bukkit.Location; import org.bukkit.NamespacedKey; import org.bukkit.Particle; @@ -138,6 +137,8 @@ import org.bukkit.entity.Trident; import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; import org.bukkit.event.weather.LightningStrikeEvent; +import org.bukkit.event.weather.ThunderChangeEvent; +import org.bukkit.event.weather.WeatherChangeEvent; import org.bukkit.event.world.SpawnChangeEvent; import org.bukkit.event.world.TimeSkipEvent; import org.bukkit.generator.BiomeProvider; @@ -323,7 +324,7 @@ public Block getBlockAt(int x, int y, int z) { @Override public Location getSpawnLocation() { - final LevelData.RespawnData respawnData = this.world.serverLevelData.getRespawnData(); + final var respawnData = this.world.serverLevelData.getRespawnData(); return CraftLocation.toBukkit(respawnData.pos(), this, respawnData.yaw(), respawnData.pitch()); } @@ -336,11 +337,7 @@ public boolean setSpawnLocation(Location location) { private boolean setSpawnLocation(int x, int y, int z, float yaw, float pitch) { try { - Location previousLocation = this.getSpawnLocation(); - this.world.serverLevelData.setSpawn(LevelData.RespawnData.of(this.world.dimension(), new BlockPos(x, y, z), yaw, pitch)); - - this.server.getServer().updateEffectiveRespawnData(); - new SpawnChangeEvent(this, previousLocation).callEvent(); + this.world.setRespawnData(LevelData.RespawnData.of(this.world.dimension(), new BlockPos(x, y, z), yaw, pitch)); return true; } catch (Exception e) { return false; @@ -475,7 +472,7 @@ private boolean unloadChunk0(int x, int z, boolean save) { @Override public boolean refreshChunk(int x, int z) { - ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z)); + ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.getVisibleChunkIfPresent(ChunkPos.pack(x, z)); if (playerChunk == null) return false; // Paper start - chunk system @@ -619,7 +616,7 @@ public Collection getIntersectingChunks(@NotNull BoundingBox boundingBox) @Override public boolean isChunkForceLoaded(int x, int z) { - return this.getHandle().getForceLoadedChunks().contains(ChunkPos.asLong(x, z)); + return this.getHandle().getForceLoadedChunks().contains(ChunkPos.pack(x, z)); } @Override @@ -632,8 +629,8 @@ public void setChunkForceLoaded(int x, int z, boolean forced) { public Collection getForceLoadedChunks() { Set chunks = new HashSet<>(); - for (long coord : this.getHandle().getForceLoadedChunks()) { - chunks.add(new CraftChunk(this.getHandle(), ChunkPos.getX(coord), ChunkPos.getZ(coord))); + for (long pos : this.getHandle().getForceLoadedChunks()) { + chunks.add(new CraftChunk(this.getHandle(), ChunkPos.getX(pos), ChunkPos.getZ(pos))); } return Collections.unmodifiableCollection(chunks); @@ -663,9 +660,9 @@ public org.bukkit.entity.Item dropItemNaturally(Location location, ItemStack ite Preconditions.checkArgument(location != null, "Location cannot be null"); Preconditions.checkArgument(item != null, "ItemStack cannot be null"); - double xs = Mth.nextDouble(this.world.random, -0.25D, 0.25D); - double ys = Mth.nextDouble(this.world.random, -0.25D, 0.25D) - ((double) EntityType.ITEM.getHeight() / 2.0D); - double zs = Mth.nextDouble(this.world.random, -0.25D, 0.25D); + double xs = Mth.nextDouble(this.world.getRandom(), -0.25, 0.25); + double ys = Mth.nextDouble(this.world.getRandom(), -0.25, 0.25) - ((double) EntityType.ITEM.getHeight() / 2.0); + double zs = Mth.nextDouble(this.world.getRandom(), -0.25, 0.25); location = location.clone().add(xs, ys, zs); return this.dropItem(location, item, function); } @@ -717,7 +714,7 @@ private LightningStrike strikeLightning0(Location loc, boolean isVisual) { // Paper start - Add methods to find targets for lightning strikes @Override public Location findLightningRod(Location location) { - return this.world.findLightningRod(CraftLocation.toBlockPosition(location)) + return this.world.findLightningRod(CraftLocation.toBlockPos(location)) .map(blockPos -> CraftLocation.toBukkit(blockPos, this.world) // get the actual rod pos .subtract(0, 1, 0)) @@ -726,7 +723,7 @@ public Location findLightningRod(Location location) { @Override public Location findLightningTarget(Location location) { - final BlockPos pos = this.world.findLightningTargetAround(CraftLocation.toBlockPosition(location), true); + final BlockPos pos = this.world.findLightningTargetAround(CraftLocation.toBlockPos(location), true); return pos == null ? null : CraftLocation.toBukkit(pos, this.world); } // Paper end - Add methods to find targets for lightning strikes @@ -762,7 +759,7 @@ public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate del @Override public String getName() { - return this.world.serverLevelData.getLevelName(); + return this.world.bukkitName; } @Override @@ -796,27 +793,24 @@ public void setTime(long time) { @Override public long getFullTime() { - return this.world.getDayTime(); + return this.world.getDefaultClockTime(); } @Override public void setFullTime(long time) { - // Notify anyone who's listening - TimeSkipEvent event = new TimeSkipEvent(this, TimeSkipEvent.SkipReason.CUSTOM, time - this.world.getDayTime()); + if (this.world.dimensionType().defaultClock().isEmpty()) { + throw new IllegalArgumentException("Cannot set time in world without world clock"); + } + + final long currentClockTime = this.world.getDefaultClockTime(); + final TimeSkipEvent event = new TimeSkipEvent(this, TimeSkipEvent.SkipReason.CUSTOM, time - currentClockTime); this.server.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } - this.world.setDayTime(this.world.getDayTime() + event.getSkipAmount()); - - // Forces the client to update to the new time immediately - for (Player p : this.getPlayers()) { - CraftPlayer cp = (CraftPlayer) p; - if (cp.getHandle().connection == null) continue; - - cp.getHandle().connection.send(new ClientboundSetTimePacket(cp.getHandle().level().getGameTime(), cp.getHandle().getPlayerTime(), cp.getHandle().relativeTime && cp.getHandle().level().getGameRules().get(GameRules.ADVANCE_TIME))); - } + // Updates the clock for all players + this.world.clockManager().setTotalTicks(this.world.dimensionType().defaultClock().get(), currentClockTime + event.getSkipAmount()); } // Paper start @@ -910,12 +904,12 @@ public void setBiome(int x, int z, Biome bio) { } @Override - public void setBiome(int x, int y, int z, Holder bb) { + public void setBiome(int x, int y, int z, Holder biome) { BlockPos pos = new BlockPos(x, 0, z); if (this.world.hasChunkAt(pos)) { net.minecraft.world.level.chunk.LevelChunk chunk = this.world.getChunkAt(pos); - chunk.setBiome(x >> 2, y >> 2, z >> 2, bb); + chunk.setNoiseBiome(QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z), biome); chunk.markUnsaved(); // SPIGOT-2890 } } @@ -923,12 +917,12 @@ public void setBiome(int x, int y, int z, Holder> 2, y >> 2, z >> 2).value().getTemperature(pos, this.world.getSeaLevel()); + return this.world.getNoiseBiome(QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z)).value().getTemperature(pos, this.world.getSeaLevel()); } @Override public double getHumidity(int x, int y, int z) { - return this.world.getNoiseBiome(x >> 2, y >> 2, z >> 2).value().climateSettings.downfall(); + return this.world.getNoiseBiome(QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z)).value().climateSettings.downfall(); } @Override @@ -992,7 +986,7 @@ public RayTraceResult rayTraceEntities(io.papermc.paper.math.Position start, Vec Preconditions.checkArgument(direction.lengthSquared() > 0, "Direction's magnitude (%s) need to be greater than 0", direction.lengthSquared()); - if (maxDistance < 0.0D) { + if (maxDistance < 0.0) { return null; } @@ -1040,7 +1034,7 @@ public RayTraceResult rayTraceBlocks(io.papermc.paper.math.Position start, Vecto Preconditions.checkArgument(fluidCollisionMode != null, "FluidCollisionMode cannot be null"); Preconditions.checkArgument(blockCollisionMode != null, "BlockCollisionMode cannot be null"); - if (maxDistance < 0.0D) { + if (maxDistance < 0.0) { return null; } @@ -1178,46 +1172,46 @@ public BlockMetadataStore getBlockMetadata() { @Override public boolean hasStorm() { - return this.world.levelData.isRaining(); + return this.world.getWeatherData().isRaining(); } @Override public void setStorm(boolean hasStorm) { - this.world.serverLevelData.setRaining(hasStorm, org.bukkit.event.weather.WeatherChangeEvent.Cause.PLUGIN); // Paper - Add cause to Weather/ThunderChangeEvents + this.world.getWeatherData().setRaining(hasStorm, WeatherChangeEvent.Cause.PLUGIN); this.setWeatherDuration(0); // Reset weather duration (legacy behaviour) this.setClearWeatherDuration(0); // Reset clear weather duration (reset "/weather clear" commands) } @Override public int getWeatherDuration() { - return this.world.serverLevelData.getRainTime(); + return this.world.getWeatherData().getRainTime(); } @Override public void setWeatherDuration(int duration) { - this.world.serverLevelData.setRainTime(duration); + this.world.getWeatherData().setRainTime(duration); } @Override public boolean isThundering() { - return this.world.levelData.isThundering(); + return this.world.getWeatherData().isThundering(); } @Override public void setThundering(boolean thundering) { - this.world.serverLevelData.setThundering(thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.PLUGIN); // Paper - Add cause to Weather/ThunderChangeEvents + this.world.getWeatherData().setThundering(thundering, ThunderChangeEvent.Cause.PLUGIN); this.setThunderDuration(0); // Reset weather duration (legacy behaviour) this.setClearWeatherDuration(0); // Reset clear weather duration (reset "/weather clear" commands) } @Override public int getThunderDuration() { - return this.world.serverLevelData.getThunderTime(); + return this.world.getWeatherData().getThunderTime(); } @Override public void setThunderDuration(int duration) { - this.world.serverLevelData.setThunderTime(duration); + this.world.getWeatherData().setThunderTime(duration); } @Override @@ -1227,12 +1221,12 @@ public boolean isClearWeather() { @Override public void setClearWeatherDuration(int duration) { - this.world.serverLevelData.setClearWeatherTime(duration); + this.world.getWeatherData().setClearWeatherTime(duration); } @Override public int getClearWeatherDuration() { - return this.world.serverLevelData.getClearWeatherTime(); + return this.world.getWeatherData().getClearWeatherTime(); } @Override @@ -1247,7 +1241,7 @@ public boolean getPVP() { @Override public void setPVP(boolean pvp) { - this.world.getGameRules().set(GameRules.PVP, pvp, this.world); + this.setGameRule(GameRules.PVP, pvp); } @Override @@ -1270,7 +1264,7 @@ public void playEffect(Location location, Effect effect, int data, int radius) { Preconditions.checkArgument(location != null, "Location cannot be null"); Preconditions.checkArgument(location.getWorld() != null, "World of Location cannot be null"); int packetData = effect.getId(); - ClientboundLevelEventPacket packet = new ClientboundLevelEventPacket(packetData, CraftLocation.toBlockPosition(location), data, false); + ClientboundLevelEventPacket packet = new ClientboundLevelEventPacket(packetData, CraftLocation.toBlockPos(location), data, false); int distance; radius *= radius; @@ -1428,7 +1422,7 @@ public boolean equals(Object obj) { @Override public Path getWorldPath() { - return this.world.levelStorageAccess.getLevelPath(LevelResource.ROOT).getParent(); + return this.world.getServer().storageSource.getDimensionPath(this.world.dimension()); } @Override @@ -1458,12 +1452,12 @@ public org.bukkit.WorldType getWorldType() { @Override public boolean canGenerateStructures() { - return this.world.serverLevelData.worldGenOptions().generateStructures(); + return this.world.worldGenSettings.options().generateStructures(); } @Override public boolean hasBonusChest() { - return this.world.serverLevelData.worldGenOptions().generateBonusChest(); + return this.world.worldGenSettings.options().generateBonusChest(); } @Override @@ -1473,7 +1467,7 @@ public boolean isHardcore() { @Override public void setHardcore(boolean hardcore) { - this.world.serverLevelData.settings.hardcore = hardcore; + this.world.serverLevelData.setHardcore(hardcore); } @Override @@ -1538,12 +1532,12 @@ public void setSpawnLimit(SpawnCategory spawnCategory, int limit) { @Override public void playSound(Location loc, Sound sound, org.bukkit.SoundCategory category, float volume, float pitch) { - this.playSound(loc, sound, category, volume, pitch, this.getHandle().random.nextLong()); + this.playSound(loc, sound, category, volume, pitch, this.getHandle().getRandom().nextLong()); } @Override public void playSound(Location loc, String sound, org.bukkit.SoundCategory category, float volume, float pitch) { - this.playSound(loc, sound, category, volume, pitch, this.getHandle().random.nextLong()); + this.playSound(loc, sound, category, volume, pitch, this.getHandle().getRandom().nextLong()); } @Override @@ -1568,17 +1562,17 @@ public void playSound(Location loc, String sound, org.bukkit.SoundCategory categ double z = loc.getZ(); ClientboundSoundPacket packet = new ClientboundSoundPacket(Holder.direct(SoundEvent.createVariableRangeEvent(Identifier.parse(sound))), SoundSource.valueOf(category.name()), x, y, z, volume, pitch, seed); - this.world.getServer().getPlayerList().broadcast(null, x, y, z, volume > 1.0F ? 16.0F * volume : 16.0D, this.world.dimension(), packet); + this.world.getServer().getPlayerList().broadcast(null, x, y, z, volume > 1.0F ? 16.0F * volume : 16.0, this.world.dimension(), packet); } @Override public void playSound(Entity entity, Sound sound, org.bukkit.SoundCategory category, float volume, float pitch) { - this.playSound(entity, sound, category, volume, pitch, this.getHandle().random.nextLong()); + this.playSound(entity, sound, category, volume, pitch, this.getHandle().getRandom().nextLong()); } @Override public void playSound(Entity entity, String sound, org.bukkit.SoundCategory category, float volume, float pitch) { - this.playSound(entity, sound, category, volume, pitch, this.getHandle().random.nextLong()); + this.playSound(entity, sound, category, volume, pitch, this.getHandle().getRandom().nextLong()); } @Override @@ -1770,7 +1764,7 @@ public void spawnParticle(Particle particle, List receivers, Player force, false, x, y, z, // Position - count, // Count + count, // Count offsetX, offsetY, offsetZ, // Random offset extra // Speed? ); @@ -1845,7 +1839,7 @@ private StructureSearchResult locateNearestStructure(Location origin, List> found = this.getHandle().getChunkSource().getGenerator().findNearestMapStructure( this.getHandle(), HolderSet.direct(CraftStructure::bukkitToMinecraftHolder, structures), - CraftLocation.toBlockPosition(origin), + CraftLocation.toBlockPos(origin), radius, findUnexplored ); @@ -1880,7 +1874,7 @@ public void sendGameEvent(Entity sourceEntity, org.bukkit.GameEvent gameEvent, V @Override public BiomeSearchResult locateNearestBiome(Location origin, int radius, int horizontalInterval, int verticalInterval, Biome... biomes) { - BlockPos originPos = CraftLocation.toBlockPosition(origin); + BlockPos originPos = CraftLocation.toBlockPos(origin); Set> holders = new HashSet<>(); for (Biome biome : biomes) { @@ -1903,7 +1897,7 @@ public Raid locateNearestRaid(Location location, int radius) { Preconditions.checkArgument(radius >= 0, "Radius value (%s) cannot be negative", radius); Raids persistentRaid = this.world.getRaids(); - net.minecraft.world.entity.raid.Raid raid = persistentRaid.getNearbyRaid(CraftLocation.toBlockPosition(location), radius * radius); + net.minecraft.world.entity.raid.Raid raid = persistentRaid.getNearbyRaid(CraftLocation.toBlockPos(location), radius * radius); return (raid == null) ? null : new CraftRaid(raid, this.world); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorldBorder.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorldBorder.java index e52f412d01cf..70d4be7ce9ac 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorldBorder.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorldBorder.java @@ -38,14 +38,14 @@ public double getSize() { @Override public void setSize(double newSize) { - Preconditions.checkArgument(newSize >= 1.0D && newSize <= this.getMaxSize(), "newSize must be between 1.0D and %s", this.getMaxSize()); + Preconditions.checkArgument(newSize >= 1.0 && newSize <= this.getMaxSize(), "newSize must be between 1.0 and %s", this.getMaxSize()); this.handle.setSize(newSize); } @Override public void changeSize(double newSize, long ticks) { Preconditions.checkArgument(ticks >= 0 && ticks <= Integer.MAX_VALUE, "ticks must be between 0-%s", Integer.MAX_VALUE); - Preconditions.checkArgument(newSize >= 1.0D && newSize <= this.getMaxSize(), "newSize must be between 1.0D and %s", this.getMaxSize()); + Preconditions.checkArgument(newSize >= 1.0 && newSize <= this.getMaxSize(), "newSize must be between 1.0 and %s", this.getMaxSize()); if (ticks > 0L) { final long startTime = (this.getWorld() != null) ? this.getWorld().getGameTime() : 0; // Virtual Borders don't have a World diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java index f3673c0cc1a8..cc6e8b25f864 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java @@ -33,7 +33,7 @@ protected CraftBanner(CraftBanner state, Location location) { public void load(BannerBlockEntity blockEntity) { super.load(blockEntity); - this.base = DyeColor.getByWoolData((byte) ((AbstractBannerBlock) this.data.getBlock()).getColor().getId()); + this.base = DyeColor.getByWoolData((byte) ((AbstractBannerBlock) this.block.getBlock()).getColor().getId()); this.patterns = new ArrayList<>(); for (int i = 0; i < blockEntity.getPatterns().layers().size(); i++) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java index 687311a9224d..90c8deade7dc 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java @@ -2,12 +2,10 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.Optional; import net.minecraft.advancements.criterion.DataComponentMatchers; import net.minecraft.advancements.criterion.ItemPredicate; -import net.minecraft.advancements.criterion.MinMaxBounds; import net.minecraft.core.component.DataComponentExactPredicate; +import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; import net.minecraft.world.LockCode; @@ -111,18 +109,22 @@ public boolean isLocked() { @Override public String getLock() { - Optional customName = this.getSnapshot().lockKey.predicate().components().exact().asPatch().get(DataComponents.CUSTOM_NAME); - - return (customName != null) ? customName.map(CraftChatMessage::fromComponent).orElse("") : ""; + Component customName = this.getSnapshot().lockKey.predicate().components().exact().asPatch().get(DataComponentMap.EMPTY, DataComponents.CUSTOM_NAME); + return (customName != null) ? CraftChatMessage.fromComponent(customName) : ""; } @Override public void setLock(String key) { - if (key == null) { + if (key == null || key.isEmpty()) { this.getSnapshot().lockKey = LockCode.NO_LOCK; } else { - DataComponentExactPredicate predicate = DataComponentExactPredicate.builder().expect(DataComponents.CUSTOM_NAME, CraftChatMessage.fromStringOrNull(key)).build(); - this.getSnapshot().lockKey = new LockCode(new ItemPredicate(Optional.empty(), MinMaxBounds.Ints.ANY, new DataComponentMatchers(predicate, Collections.emptyMap()))); + this.getSnapshot().lockKey = new LockCode(ItemPredicate.Builder.item().withComponents( + DataComponentMatchers.Builder.components().exact( + DataComponentExactPredicate.builder().expect( + DataComponents.CUSTOM_NAME, CraftChatMessage.fromString(key)[0] + ).build() + ).build() + ).build()); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java index ed4692090a11..aa00185b64f2 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java @@ -33,7 +33,7 @@ public Location getFlower() { @Override public void setFlower(Location location) { Preconditions.checkArgument(location == null || this.getWorld().equals(location.getWorld()), "Flower must be in same world"); - this.getSnapshot().savedFlowerPos = (location == null) ? null : CraftLocation.toBlockPosition(location); + this.getSnapshot().savedFlowerPos = (location == null) ? null : CraftLocation.toBlockPos(location); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java index 31e665388bc6..5d6116c55072 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java @@ -17,9 +17,13 @@ import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LevelEvent; import net.minecraft.world.level.block.RedStoneWireBlock; import net.minecraft.world.level.block.SaplingBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.redstone.Redstone; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; @@ -36,7 +40,6 @@ import org.bukkit.block.Biome; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; import org.bukkit.block.PistonMoveReaction; import org.bukkit.block.data.BlockData; import org.bukkit.craftbukkit.CraftFluidCollisionMode; @@ -60,39 +63,36 @@ import org.bukkit.util.BoundingBox; import org.bukkit.util.RayTraceResult; import org.bukkit.util.Vector; +import org.jspecify.annotations.Nullable; public class CraftBlock implements Block { - private final net.minecraft.world.level.LevelAccessor world; + private final net.minecraft.world.level.LevelAccessor level; private final BlockPos position; - private CraftBlock(LevelAccessor world, BlockPos position) { - this.world = world; + private CraftBlock(LevelAccessor level, BlockPos position) { + this.level = level; this.position = position.immutable(); } - public static CraftBlock at(LevelAccessor world, BlockPos position) { - return new CraftBlock(world, position); + public static CraftBlock at(LevelAccessor level, BlockPos position) { + return new CraftBlock(level, position); } - public net.minecraft.world.level.block.state.BlockState getNMS() { - return this.world.getBlockState(this.position); - } - - public net.minecraft.world.level.material.FluidState getNMSFluid() { - return this.world.getFluidState(this.position); + public net.minecraft.world.level.block.state.BlockState getBlockState() { + return this.level.getBlockState(this.position); } public BlockPos getPosition() { return this.position; } - public LevelAccessor getHandle() { - return this.world; + public LevelAccessor getLevel() { + return this.level; } @Override public World getWorld() { - return this.world.getMinecraftWorld().getWorld(); + return this.level.getMinecraftWorld().getWorld(); } public CraftWorld getCraftWorld() { @@ -111,8 +111,7 @@ public Location getLocation(Location loc) { loc.setX(this.position.getX()); loc.setY(this.position.getY()); loc.setZ(this.position.getZ()); - loc.setYaw(0); - loc.setPitch(0); + loc.setRotation(0, 0); } return loc; @@ -147,26 +146,21 @@ public void setData(final byte data) { } public void setData(final byte data, boolean applyPhysics) { - if (applyPhysics) { - this.setData(data, net.minecraft.world.level.block.Block.UPDATE_ALL); - } else { - this.setData(data, net.minecraft.world.level.block.Block.UPDATE_CLIENTS); - } + this.setData(data, net.minecraft.world.level.block.Block.UPDATE_CLIENTS | (applyPhysics ? net.minecraft.world.level.block.Block.UPDATE_NEIGHBORS : 0)); } private void setData(final byte data, @net.minecraft.world.level.block.Block.UpdateFlags int flags) { - this.world.setBlock(this.position, CraftMagicNumbers.getBlock(this.getType(), data), flags); + this.level.setBlock(this.position, CraftMagicNumbers.getBlock(this.getType(), data), flags); } @Override public byte getData() { - net.minecraft.world.level.block.state.BlockState state = this.world.getBlockState(this.position); - return CraftMagicNumbers.toLegacyData(state); + return CraftMagicNumbers.toLegacyData(this.getBlockState()); } @Override public BlockData getBlockData() { - return CraftBlockData.fromData(this.getNMS()); + return this.getBlockState().asBlockData(); } @Override @@ -192,7 +186,7 @@ public void setBlockData(BlockData data, boolean applyPhysics) { } boolean setBlockState(final net.minecraft.world.level.block.state.BlockState state, final boolean applyPhysics) { - return CraftBlock.setBlockState(this.world, this.position, this.getNMS(), state, applyPhysics); + return setBlockState(this.level, this.position, this.getBlockState(), state, applyPhysics); } public static boolean setBlockState(LevelAccessor world, BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, boolean applyPhysics) { @@ -227,30 +221,22 @@ public static boolean setBlockState(LevelAccessor world, BlockPos pos, net.minec @Override public Material getType() { - return this.world.getBlockState(this.position).getBukkitMaterial(); // Paper - optimise getType calls + return this.getBlockState().getBukkitMaterial(); } @Override public byte getLightLevel() { - return (byte) this.world.getMinecraftWorld().getMaxLocalRawBrightness(this.position); + return (byte) this.level.getMinecraftWorld().getMaxLocalRawBrightness(this.position); } @Override public byte getLightFromSky() { - return (byte) this.world.getBrightness(LightLayer.SKY, this.position); + return (byte) this.level.getBrightness(LightLayer.SKY, this.position); } @Override public byte getLightFromBlocks() { - return (byte) this.world.getBrightness(LightLayer.BLOCK, this.position); - } - - public Block getFace(final BlockFace face) { - return this.getRelative(face, 1); - } - - public Block getFace(final BlockFace face, final int distance) { - return this.getRelative(face, distance); + return (byte) this.level.getBrightness(LightLayer.BLOCK, this.position); } @Override @@ -283,76 +269,53 @@ public BlockFace getFace(final Block block) { @Override public String toString() { - return "CraftBlock{pos=" + this.position + ",type=" + this.getType() + ",data=" + this.getNMS() + ",fluid=" + this.getNMSFluid() + '}'; + net.minecraft.world.level.block.state.BlockState state = this.getBlockState(); + return "CraftBlock{pos=" + this.position + ", type=" + this.getType() + ", data=" + state + ", fluid=" + state.getFluidState() + '}'; } - public static BlockFace notchToBlockFace(Direction notch) { - if (notch == null) { - return BlockFace.SELF; - } - switch (notch) { - case DOWN: - return BlockFace.DOWN; - case UP: - return BlockFace.UP; - case NORTH: - return BlockFace.NORTH; - case SOUTH: - return BlockFace.SOUTH; - case WEST: - return BlockFace.WEST; - case EAST: - return BlockFace.EAST; - default: - return BlockFace.SELF; - } + public static BlockFace notchToBlockFace(@Nullable Direction direction) { + return switch (direction) { + case DOWN -> BlockFace.DOWN; + case UP -> BlockFace.UP; + case NORTH -> BlockFace.NORTH; + case SOUTH -> BlockFace.SOUTH; + case WEST -> BlockFace.WEST; + case EAST -> BlockFace.EAST; + case null -> BlockFace.SELF; + }; } - public static Direction blockFaceToNotch(BlockFace face) { - if (face == null) { - return null; - } - switch (face) { - case DOWN: - return Direction.DOWN; - case UP: - return Direction.UP; - case NORTH: - return Direction.NORTH; - case SOUTH: - return Direction.SOUTH; - case WEST: - return Direction.WEST; - case EAST: - return Direction.EAST; - default: - return null; - } + public static @Nullable Direction blockFaceToNotch(@Nullable BlockFace face) { + return switch (face) { + case DOWN -> Direction.DOWN; + case UP -> Direction.UP; + case NORTH -> Direction.NORTH; + case SOUTH -> Direction.SOUTH; + case WEST -> Direction.WEST; + case EAST -> Direction.EAST; + case null, default -> null; + }; } @Override - public BlockState getState() { + public org.bukkit.block.BlockState getState() { return CraftBlockStates.getBlockState(this); } - // Paper start @Override - public BlockState getState(boolean useSnapshot) { + public org.bukkit.block.BlockState getState(boolean useSnapshot) { return CraftBlockStates.getBlockState(this, useSnapshot); } - // Paper end @Override public Biome getBiome() { return this.getWorld().getBiome(this.getX(), this.getY(), this.getZ()); } - // Paper start @Override public Biome getComputedBiome() { return this.getWorld().getComputedBiome(this.getX(), this.getY(), this.getZ()); } - // Paper end @Override public void setBiome(Biome bio) { @@ -361,7 +324,7 @@ public void setBiome(Biome bio) { @Override public double getTemperature() { - return this.world.getBiome(this.position).value().getTemperature(this.position, this.world.getSeaLevel()); + return this.level.getBiome(this.position).value().getTemperature(this.position, this.level.getSeaLevel()); } @Override @@ -371,12 +334,12 @@ public double getHumidity() { @Override public boolean isBlockPowered() { - return this.world.getMinecraftWorld().getDirectSignalTo(this.position) > 0; + return this.level.getMinecraftWorld().getDirectSignalTo(this.position) > Redstone.SIGNAL_MIN; } @Override public boolean isBlockIndirectlyPowered() { - return this.world.getMinecraftWorld().hasNeighborSignal(this.position); + return this.level.getMinecraftWorld().hasNeighborSignal(this.position); } @Override @@ -398,87 +361,101 @@ public int hashCode() { @Override public boolean isBlockFacePowered(BlockFace face) { - return this.world.getMinecraftWorld().hasSignal(this.position, CraftBlock.blockFaceToNotch(face)); + Direction direction = blockFaceToNotch(face); + Preconditions.checkArgument(direction != null, face + " is not a valid cartesian face"); + return this.level.getMinecraftWorld().hasSignal(this.position, direction); } @Override public boolean isBlockFaceIndirectlyPowered(BlockFace face) { - int power = this.world.getMinecraftWorld().getSignal(this.position, CraftBlock.blockFaceToNotch(face)); + Direction direction = blockFaceToNotch(face); + Preconditions.checkArgument(direction != null, face + " is not a valid cartesian face"); + if (this.level.getMinecraftWorld().hasSignal(this.position, direction)) { + return true; + } - Block relative = this.getRelative(face); - if (relative.getType() == Material.REDSTONE_WIRE) { - return Math.max(power, relative.getData()) > 0; // todo remove legacy usage + BlockState neighborState = this.level.getBlockState(this.position.relative(direction)); + if (neighborState.hasProperty(RedStoneWireBlock.POWER)) { + return neighborState.getValue(RedStoneWireBlock.POWER) > Redstone.SIGNAL_MIN; } - return power > 0; + return false; } @Override public int getBlockPower(BlockFace face) { - int power = 0; - net.minecraft.world.level.Level world = this.world.getMinecraftWorld(); - int x = this.getX(); - int y = this.getY(); - int z = this.getZ(); - if ((face == BlockFace.DOWN || face == BlockFace.SELF) && world.hasSignal(new BlockPos(x, y - 1, z), Direction.DOWN)) power = CraftBlock.getPower(power, world.getBlockState(new BlockPos(x, y - 1, z))); - if ((face == BlockFace.UP || face == BlockFace.SELF) && world.hasSignal(new BlockPos(x, y + 1, z), Direction.UP)) power = CraftBlock.getPower(power, world.getBlockState(new BlockPos(x, y + 1, z))); - if ((face == BlockFace.EAST || face == BlockFace.SELF) && world.hasSignal(new BlockPos(x + 1, y, z), Direction.EAST)) power = CraftBlock.getPower(power, world.getBlockState(new BlockPos(x + 1, y, z))); - if ((face == BlockFace.WEST || face == BlockFace.SELF) && world.hasSignal(new BlockPos(x - 1, y, z), Direction.WEST)) power = CraftBlock.getPower(power, world.getBlockState(new BlockPos(x - 1, y, z))); - if ((face == BlockFace.NORTH || face == BlockFace.SELF) && world.hasSignal(new BlockPos(x, y, z - 1), Direction.NORTH)) power = CraftBlock.getPower(power, world.getBlockState(new BlockPos(x, y, z - 1))); - if ((face == BlockFace.SOUTH || face == BlockFace.SELF) && world.hasSignal(new BlockPos(x, y, z + 1), Direction.SOUTH)) power = CraftBlock.getPower(power, world.getBlockState(new BlockPos(x, y, z + 1))); - return power > 0 ? power : (face == BlockFace.SELF ? this.isBlockIndirectlyPowered() : this.isBlockFaceIndirectlyPowered(face)) ? 15 : 0; - } - - private static int getPower(int power, net.minecraft.world.level.block.state.BlockState state) { - if (!state.is(Blocks.REDSTONE_WIRE)) { + Preconditions.checkArgument(face != null, "face cannot be null"); + + net.minecraft.world.level.Level level = this.level.getMinecraftWorld(); + boolean searchSelfIncluded = face == BlockFace.SELF; + Direction onlyFace = blockFaceToNotch(face); + + int power = Redstone.SIGNAL_NONE; + for (Direction direction : SignalGetter.DIRECTIONS) { + if (!searchSelfIncluded && direction != onlyFace) { + continue; + } + + BlockPos neighborPos = this.position.relative(direction); + if (level.hasSignal(neighborPos, direction)) { + BlockState state = level.getBlockState(neighborPos); + if (state.hasProperty(RedStoneWireBlock.POWER)) { + power = Math.max(state.getValue(RedStoneWireBlock.POWER), power); + if (power == Redstone.SIGNAL_MAX) { + return power; + } + } + } + } + + if (power != Redstone.SIGNAL_NONE) { return power; - } else { - return Math.max(state.getValue(RedStoneWireBlock.POWER), power); } - } - @Override - public int getBlockPower() { - return this.getBlockPower(BlockFace.SELF); + if (searchSelfIncluded) { + return this.isBlockIndirectlyPowered() ? Redstone.SIGNAL_MAX : Redstone.SIGNAL_MIN; + } + return this.isBlockFaceIndirectlyPowered(face) ? Redstone.SIGNAL_MAX : Redstone.SIGNAL_MIN; } @Override public boolean isEmpty() { - return this.getNMS().isAir(); + return this.getBlockState().isAir(); } @Override public boolean isLiquid() { - return this.getNMS().liquid(); + return this.getBlockState().liquid(); } - // Paper start @Override public boolean isBuildable() { - return this.getNMS().isSolid(); // This is in fact isSolid, despite the fact that isSolid below returns blocksMotion + return this.getBlockState().isSolid(); // This is in fact isSolid, despite the fact that isSolid below returns blocksMotion } + @Override public boolean isBurnable() { - return this.getNMS().ignitedByLava(); + return this.getBlockState().ignitedByLava(); } + @Override public boolean isReplaceable() { - return this.getNMS().canBeReplaced(); + return this.getBlockState().canBeReplaced(); } + @Override public boolean isSolid() { - return this.getNMS().blocksMotion(); + return this.getBlockState().blocksMotion(); } @Override public boolean isCollidable() { - return getNMS().getBlock().hasCollision; + return getBlockState().getBlock().hasCollision; } - // Paper end @Override public PistonMoveReaction getPistonMoveReaction() { - return PistonMoveReaction.getById(this.getNMS().getPistonPushReaction().ordinal()); + return PistonMoveReaction.getById(this.getBlockState().getPistonPushReaction().ordinal()); } @Override @@ -488,7 +465,6 @@ public boolean breakNaturally() { @Override public boolean breakNaturally(ItemStack item) { - // Paper start return this.breakNaturally(item, false); } @@ -504,51 +480,47 @@ public boolean breakNaturally(ItemStack item, boolean triggerEffect, boolean dro @Override public boolean breakNaturally(ItemStack item, boolean triggerEffect, boolean dropExperience, boolean forceEffect) { - // Paper end // Order matters here, need to drop before setting to air so skulls can get their data - net.minecraft.world.level.block.state.BlockState state = this.getNMS(); + BlockState state = this.getBlockState(); net.minecraft.world.level.block.Block block = state.getBlock(); net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item); boolean result = false; // Modelled off Player#hasCorrectToolForDrops - if (block != Blocks.AIR && (item == null || !state.requiresCorrectToolForDrops() || nmsItem.isCorrectToolForDrops(state))) { - net.minecraft.world.level.block.Block.dropResources(state, this.world.getMinecraftWorld(), this.position, this.world.getBlockEntity(this.position), null, nmsItem, false); // Paper - Properly handle xp dropping - // Paper start - improve Block#breakNaturally - if (dropExperience) block.popExperience(this.world.getMinecraftWorld(), this.position, block.getExpDrop(state, this.world.getMinecraftWorld(), this.position, nmsItem, true)); - // Paper end + if (!state.isAir() && (item == null || !state.requiresCorrectToolForDrops() || nmsItem.isCorrectToolForDrops(state))) { + net.minecraft.world.level.block.Block.dropResources(state, this.level.getMinecraftWorld(), this.position, this.level.getBlockEntity(this.position), null, nmsItem, false); + if (dropExperience) block.popExperience(this.level.getMinecraftWorld(), this.position, block.getExpDrop(state, this.level.getMinecraftWorld(), this.position, nmsItem, true)); result = true; } - if ((result && triggerEffect) || (forceEffect && block != Blocks.AIR)) { - if (state.getBlock() instanceof net.minecraft.world.level.block.BaseFireBlock) { - this.world.levelEvent(net.minecraft.world.level.block.LevelEvent.SOUND_EXTINGUISH_FIRE, this.position, 0); + if ((result && triggerEffect) || (forceEffect && !state.isAir())) { + if (block instanceof net.minecraft.world.level.block.BaseFireBlock) { + this.level.levelEvent(LevelEvent.SOUND_EXTINGUISH_FIRE, this.position, 0); } else { - this.world.levelEvent(net.minecraft.world.level.block.LevelEvent.PARTICLES_DESTROY_BLOCK, this.position, net.minecraft.world.level.block.Block.getId(state)); + this.level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, this.position, net.minecraft.world.level.block.Block.getId(state)); } } - // SPIGOT-6778: Directly call setBlock instead of setBlockState, so that the block entity is not removed and custom remove logic is run. - // Paper start - improve breakNaturally - boolean destroyed = this.world.removeBlock(this.position, false); + boolean destroyed = this.level.removeBlock(this.position, false); if (destroyed) { - block.destroy(this.world, this.position, state); + block.destroy(this.level, this.position, state); } if (result) { // special cases if (block instanceof net.minecraft.world.level.block.IceBlock iceBlock) { - iceBlock.afterDestroy(this.world.getMinecraftWorld(), this.position, nmsItem); + iceBlock.afterDestroy(this.level.getMinecraftWorld(), this.position, nmsItem); } else if (block instanceof net.minecraft.world.level.block.TurtleEggBlock turtleEggBlock) { - turtleEggBlock.decreaseEggs(this.world.getMinecraftWorld(), this.position, state); + turtleEggBlock.decreaseEggs(this.level.getMinecraftWorld(), this.position, state); } } return destroyed && result; - // Paper end } @Override public boolean applyBoneMeal(BlockFace face) { - Direction direction = CraftBlock.blockFaceToNotch(face); + Direction direction = blockFaceToNotch(face); + Preconditions.checkArgument(direction != null, face + " is not a valid cartesian face"); + BlockFertilizeEvent event = null; ServerLevel world = this.getCraftWorld().getHandle(); UseOnContext context = new UseOnContext(world, null, InteractionHand.MAIN_HAND, Items.BONE_MEAL.getDefaultInstance(), new BlockHitResult(Vec3.ZERO, direction, this.getPosition(), false)); @@ -561,7 +533,7 @@ public boolean applyBoneMeal(BlockFace face) { if (!world.capturedBlockStates.isEmpty()) { TreeType treeType = SaplingBlock.treeType; SaplingBlock.treeType = null; - List states = new ArrayList<>(world.capturedBlockStates.values()); + List states = new ArrayList<>(world.capturedBlockStates.values()); world.capturedBlockStates.clear(); StructureGrowEvent structureEvent = null; @@ -575,10 +547,10 @@ public boolean applyBoneMeal(BlockFace face) { Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { - for (BlockState state : states) { + for (org.bukkit.block.BlockState state : states) { CraftBlockState craftBlockState = (CraftBlockState) state; craftBlockState.place(craftBlockState.getFlags()); - world.checkCapturedTreeStateForObserverNotify(this.position, craftBlockState); // Paper - notify observers even if grow failed + world.checkCapturedTreeStateForObserverNotify(this.position, craftBlockState); } } } @@ -598,12 +570,12 @@ public Collection getDrops(ItemStack item) { @Override public Collection getDrops(ItemStack item, Entity entity) { - net.minecraft.world.level.block.state.BlockState state = this.getNMS(); + BlockState state = this.getBlockState(); net.minecraft.world.item.ItemStack nms = CraftItemStack.asNMSCopy(item); // Modelled off Player#hasCorrectToolForDrops if (item == null || CraftBlockData.isPreferredTool(state, nms)) { - return net.minecraft.world.level.block.Block.getDrops(state, this.world.getMinecraftWorld(), this.position, this.world.getBlockEntity(this.position), entity == null ? null : ((CraftEntity) entity).getHandle(), nms) + return net.minecraft.world.level.block.Block.getDrops(state, this.level.getMinecraftWorld(), this.position, this.level.getBlockEntity(this.position), entity == null ? null : ((CraftEntity) entity).getHandle(), nms) .stream().map(CraftItemStack::asBukkitCopy).collect(Collectors.toList()); } else { return Collections.emptyList(); @@ -612,15 +584,13 @@ public Collection getDrops(ItemStack item, Entity entity) { @Override public boolean isPreferredTool(ItemStack item) { - net.minecraft.world.level.block.state.BlockState state = this.getNMS(); - net.minecraft.world.item.ItemStack nms = CraftItemStack.asNMSCopy(item); - return CraftBlockData.isPreferredTool(state, nms); + return CraftBlockData.isPreferredTool(this.getBlockState(), CraftItemStack.asNMSCopy(item)); } @Override public float getBreakSpeed(Player player) { Preconditions.checkArgument(player != null, "player cannot be null"); - return this.getNMS().getDestroyProgress(((CraftPlayer) player).getHandle(), this.world, this.position); + return this.getBlockState().getDestroyProgress(((CraftPlayer) player).getHandle(), this.level, this.position); } @Override @@ -645,7 +615,7 @@ public void removeMetadata(String metadataKey, Plugin owningPlugin) { @Override public boolean isPassable() { - return this.getNMS().getCollisionShape(this.world, this.position).isEmpty(); + return this.getBlockState().getCollisionShape(this.level, this.position).isEmpty(); } @Override @@ -659,7 +629,7 @@ public RayTraceResult rayTrace(Location start, Vector direction, double maxDista Preconditions.checkArgument(direction.lengthSquared() > 0, "Direction's magnitude (%s) must be greater than 0", direction.lengthSquared()); Preconditions.checkArgument(fluidCollisionMode != null, "FluidCollisionMode cannot be null"); - if (maxDistance < 0.0D) { + if (maxDistance < 0.0) { return null; } @@ -667,13 +637,13 @@ public RayTraceResult rayTrace(Location start, Vector direction, double maxDista Vec3 startPos = CraftLocation.toVec3(start); Vec3 endPos = startPos.add(dir.getX(), dir.getY(), dir.getZ()); - HitResult hitResult = this.world.clip(new ClipContext(startPos, endPos, ClipContext.Block.OUTLINE, CraftFluidCollisionMode.toFluid(fluidCollisionMode), CollisionContext.empty()), this.position); - return CraftRayTraceResult.convertFromInternal(this.world, hitResult); + HitResult hitResult = this.level.clip(new ClipContext(startPos, endPos, ClipContext.Block.OUTLINE, CraftFluidCollisionMode.toFluid(fluidCollisionMode), CollisionContext.empty()), this.position); + return CraftRayTraceResult.convertFromInternal(this.level, hitResult); } @Override public BoundingBox getBoundingBox() { - VoxelShape shape = this.getNMS().getShape(this.world, this.position); + VoxelShape shape = this.getBlockState().getShape(this.level, this.position); if (shape.isEmpty()) { return new BoundingBox(); // Return an empty bounding box if the block has no dimension @@ -685,65 +655,62 @@ public BoundingBox getBoundingBox() { @Override public org.bukkit.util.VoxelShape getCollisionShape() { - VoxelShape shape = this.getNMS().getCollisionShape(this.world, this.position); + VoxelShape shape = this.getBlockState().getCollisionShape(this.level, this.position); return new CraftVoxelShape(shape); } @Override public boolean canPlace(BlockData data) { Preconditions.checkArgument(data != null, "BlockData cannot be null"); - net.minecraft.world.level.block.state.BlockState iblockdata = ((CraftBlockData) data).getState(); - net.minecraft.world.level.Level world = this.world.getMinecraftWorld(); - return iblockdata.canSurvive(world, this.position); + BlockState state = ((CraftBlockData) data).getState(); + return state.canSurvive(this.level.getMinecraftWorld(), this.position); } @Override public String getTranslationKey() { - return this.getNMS().getBlock().getDescriptionId(); + return this.getBlockState().getBlock().getDescriptionId(); } @Override public boolean isSuffocating() { - return this.getNMS().isSuffocating(this.world, this.position); + return this.getBlockState().isSuffocating(this.level, this.position); } - // Paper start @Override public com.destroystokyo.paper.block.BlockSoundGroup getSoundGroup() { - return new com.destroystokyo.paper.block.CraftBlockSoundGroup(getNMS().getBlock().defaultBlockState().getSoundType()); + return new com.destroystokyo.paper.block.CraftBlockSoundGroup(this.getBlockState().getSoundType()); } @Override public org.bukkit.SoundGroup getBlockSoundGroup() { - return org.bukkit.craftbukkit.CraftSoundGroup.getSoundGroup(this.getNMS().getSoundType()); + return org.bukkit.craftbukkit.CraftSoundGroup.getSoundGroup(this.getBlockState().getSoundType()); } @Override public String translationKey() { - return this.getNMS().getBlock().getDescriptionId(); + return this.getBlockState().getBlock().getDescriptionId(); } - public boolean isValidTool(ItemStack itemStack) { - return getDrops(itemStack).size() != 0; + @Override + public boolean isValidTool(ItemStack tool) { + return !this.getDrops(tool).isEmpty(); } @Override public void tick() { - final ServerLevel level = this.world.getMinecraftWorld(); - this.getNMS().tick(level, this.position, level.random); + final ServerLevel level = this.level.getMinecraftWorld(); + this.getBlockState().tick(level, this.position, level.getRandom()); } - @Override public void fluidTick() { - this.getNMSFluid().tick(this.world.getMinecraftWorld(), this.position, this.getNMS()); + this.level.getFluidState(this.position).tick(this.level.getMinecraftWorld(), this.position, this.getBlockState()); } @Override public void randomTick() { - final ServerLevel level = this.world.getMinecraftWorld(); - this.getNMS().randomTick(level, this.position, level.random); + final ServerLevel level = this.level.getMinecraftWorld(); + this.getBlockState().randomTick(level, this.position, level.getRandom()); } - // Paper end } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java index 26feb76c5263..2857d6144afe 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java @@ -219,7 +219,7 @@ public PersistentDataContainer getPersistentDataContainer() { @Nullable public Packet getUpdatePacket(@NotNull Location location) { - return new ClientboundBlockEntityDataPacket(CraftLocation.toBlockPosition(location), this.snapshot.getType(), this.getUpdateNBT()); + return new ClientboundBlockEntityDataPacket(CraftLocation.toBlockPos(location), this.snapshot.getType(), this.getUpdateNBT()); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java index 3036f3fa8b58..88524015e15c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java @@ -27,16 +27,16 @@ public class CraftBlockState implements BlockState { protected final CraftWorld world; private final BlockPos position; - protected net.minecraft.world.level.block.state.BlockState data; + protected net.minecraft.world.level.block.state.BlockState block; @net.minecraft.world.level.block.Block.UpdateFlags protected int capturedFlags; // todo move out of this class - private WeakReference weakWorld; + private WeakReference weakLevel; protected CraftBlockState(final Block block) { - this(block.getWorld(), ((CraftBlock) block).getPosition(), ((CraftBlock) block).getNMS()); + this(block.getWorld(), ((CraftBlock) block).getPosition(), ((CraftBlock) block).getBlockState()); this.capturedFlags = net.minecraft.world.level.block.Block.UPDATE_ALL; - this.setWorldHandle(((CraftBlock) block).getHandle()); + this.setWorldHandle(((CraftBlock) block).getLevel()); } @Deprecated @@ -46,54 +46,54 @@ protected CraftBlockState(final Block block, @net.minecraft.world.level.block.Bl } // world can be null for non-placed BlockStates. - protected CraftBlockState(@Nullable World world, BlockPos pos, net.minecraft.world.level.block.state.BlockState data) { + protected CraftBlockState(@Nullable World world, BlockPos pos, net.minecraft.world.level.block.state.BlockState block) { this.world = (CraftWorld) world; this.position = pos; - this.data = data; + this.block = block; } // Creates an unplaced copy of the given CraftBlockState at the given location - protected CraftBlockState(CraftBlockState state, @Nullable Location location) { + protected CraftBlockState(CraftBlockState from, @Nullable Location location) { if (location == null) { this.world = null; - this.position = state.getPosition().immutable(); + this.position = from.getPosition().immutable(); } else { this.world = (CraftWorld) location.getWorld(); - this.position = CraftLocation.toBlockPosition(location); + this.position = CraftLocation.toBlockPos(location); } - this.data = state.data; - this.capturedFlags = state.capturedFlags; - this.setWorldHandle(state.getWorldHandle()); + this.block = from.block; + this.capturedFlags = from.capturedFlags; + this.setWorldHandle(from.getWorldHandle()); } - public void setWorldHandle(LevelAccessor generatorAccess) { - if (generatorAccess instanceof net.minecraft.world.level.Level) { - this.weakWorld = null; + public void setWorldHandle(LevelAccessor level) { + if (level instanceof net.minecraft.world.level.Level) { + this.weakLevel = null; } else { - this.weakWorld = new WeakReference<>(generatorAccess); + this.weakLevel = new WeakReference<>(level); } } - // Returns null if weakWorld is not available and the BlockState is not placed. - // If this returns a World instead of only a GeneratorAccess, this implies that this BlockState is placed. + // Returns null if weakLevel is not available and the BlockState is not placed. + // If this returns a Level instead of only a LevelAccessor, this implies that this BlockState is placed. @Nullable public LevelAccessor getWorldHandle() { - if (this.weakWorld == null) { + if (this.weakLevel == null) { return this.isPlaced() ? this.world.getHandle() : null; } - LevelAccessor access = this.weakWorld.get(); - if (access == null) { - this.weakWorld = null; + LevelAccessor level = this.weakLevel.get(); + if (level == null) { + this.weakLevel = null; return this.isPlaced() ? this.world.getHandle() : null; } - return access; + return level; } protected final boolean isWorldGeneration() { - LevelAccessor generatorAccess = this.getWorldHandle(); - return generatorAccess != null && !(generatorAccess instanceof net.minecraft.world.level.Level); + LevelAccessor level = this.getWorldHandle(); + return level != null && !(level instanceof net.minecraft.world.level.Level); } protected final void ensureNoWorldGeneration() { @@ -127,8 +127,8 @@ public Chunk getChunk() { return this.world.getChunkAt(this.getX() >> 4, this.getZ() >> 4); } - public void setData(net.minecraft.world.level.block.state.BlockState data) { - this.data = data; + public void setBlock(net.minecraft.world.level.block.state.BlockState block) { + this.block = block; } public BlockPos getPosition() { @@ -136,35 +136,34 @@ public BlockPos getPosition() { } public net.minecraft.world.level.block.state.BlockState getHandle() { - return this.data; + return this.block; } @Override public BlockData getBlockData() { - return CraftBlockData.fromData(this.data); + return this.block.asBlockData(); } @Override public void setBlockData(BlockData data) { + // todo this is weird for block entities since the old methods are still available but not the new might be better to have wither Preconditions.checkArgument(data != null, "BlockData cannot be null"); - this.data = ((CraftBlockData) data).getState(); + this.block = ((CraftBlockData) data).getState(); } @Override public void setData(final MaterialData data) { - Material mat = CraftMagicNumbers.getMaterial(this.data).getItemType(); + Material mat = CraftMagicNumbers.getMaterial(this.block).getItemType(); - if ((mat == null) || (mat.getData() == null)) { - this.data = CraftMagicNumbers.getBlock(data); - } else { + if (mat != null) { Preconditions.checkArgument((data.getClass() == mat.getData()) || (data.getClass() == MaterialData.class), "Provided data is not of type %s, found %s", mat.getData().getName(), data.getClass().getName()); - this.data = CraftMagicNumbers.getBlock(data); } + this.block = CraftMagicNumbers.getBlock(data); } @Override public MaterialData getData() { - return CraftMagicNumbers.getMaterial(this.data); + return CraftMagicNumbers.getMaterial(this.block); } @Override @@ -173,13 +172,13 @@ public void setType(final Material type) { Preconditions.checkArgument(type.isBlock(), "Material must be a block!"); if (this.getType() != type) { - this.data = CraftBlockType.bukkitToMinecraft(type).defaultBlockState(); + this.block = CraftBlockType.bukkitToMinecraft(type).defaultBlockState(); } } @Override public Material getType() { - return this.data.getBukkitMaterial(); + return this.block.getBukkitMaterial(); } public void setFlags(@net.minecraft.world.level.block.Block.UpdateFlags int flags) { @@ -225,12 +224,12 @@ public boolean update(boolean force, boolean applyPhysics) { } } - net.minecraft.world.level.block.state.BlockState newBlock = this.data; + net.minecraft.world.level.block.state.BlockState newBlock = this.block; block.setBlockState(newBlock, applyPhysics); if (access instanceof net.minecraft.world.level.Level) { this.world.getHandle().sendBlockUpdated( this.position, - block.getNMS(), + block.getBlockState(), newBlock, net.minecraft.world.level.block.Block.UPDATE_ALL ); @@ -250,7 +249,7 @@ public boolean place(@net.minecraft.world.level.block.Block.UpdateFlags int flag return false; } - return this.getWorldHandle().setBlock(this.position, this.data, flags); + return this.getWorldHandle().setBlock(this.position, this.block, flags); } // used to revert a block placement due to an event being cancelled for example @@ -266,7 +265,7 @@ public boolean revertPlace() { @Override public byte getRawData() { - return CraftMagicNumbers.toLegacyData(this.data); + return CraftMagicNumbers.toLegacyData(this.block); } @Override @@ -281,8 +280,7 @@ public Location getLocation(Location loc) { loc.setX(this.getX()); loc.setY(this.getY()); loc.setZ(this.getZ()); - loc.setYaw(0); - loc.setPitch(0); + loc.setRotation(0, 0); } return loc; @@ -290,21 +288,19 @@ public Location getLocation(Location loc) { @Override public void setRawData(byte data) { - this.data = CraftMagicNumbers.getBlock(this.getType(), data); + this.block = CraftMagicNumbers.getBlock(this.getType(), data); } @Override public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (this.getClass() != obj.getClass()) { + if (obj == null || this.getClass() != obj.getClass()) { return false; } + final CraftBlockState other = (CraftBlockState) obj; return Objects.equals(this.world, other.world) && Objects.equals(this.position, other.position) && - Objects.equals(this.data, other.data); + Objects.equals(this.block, other.block); } @Override @@ -312,7 +308,7 @@ public int hashCode() { int hash = 7; hash = 73 * hash + (this.world != null ? this.world.hashCode() : 0); hash = 73 * hash + (this.position != null ? this.position.hashCode() : 0); - hash = 73 * hash + (this.data != null ? this.data.hashCode() : 0); + hash = 73 * hash + (this.block != null ? this.block.hashCode() : 0); return hash; } @@ -361,22 +357,22 @@ public BlockState copy(Location location) { @Override public boolean isCollidable() { - return this.data.getBlock().hasCollision; + return this.block.getBlock().hasCollision; } @Override - public java.util.Collection getDrops(org.bukkit.inventory.ItemStack item, org.bukkit.entity.Entity entity) { + public java.util.Collection getDrops(org.bukkit.inventory.ItemStack tool, org.bukkit.entity.Entity entity) { this.requirePlaced(); - net.minecraft.world.item.ItemStack nms = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(item); + net.minecraft.world.item.ItemStack item = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(tool); // Modelled off Player#hasCorrectToolForDrops - if (item == null || !data.requiresCorrectToolForDrops() || nms.isCorrectToolForDrops(this.data)) { + if (tool == null || !this.block.requiresCorrectToolForDrops() || item.isCorrectToolForDrops(this.block)) { return net.minecraft.world.level.block.Block.getDrops( - this.data, + this.block, this.world.getHandle(), this.position, this.world.getHandle().getBlockEntity(this.position), entity == null ? null : - ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle(), nms + ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle(), item ).stream().map(org.bukkit.craftbukkit.inventory.CraftItemStack::asBukkitCopy).toList(); } else { return java.util.Collections.emptyList(); @@ -386,6 +382,6 @@ public java.util.Collection getDrops(org.bukkit. @Override public boolean isSuffocating() { this.requirePlaced(); - return this.data.isSuffocating(this.getWorldHandle(), this.position); + return this.block.isSuffocating(this.getWorldHandle(), this.position); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java index bcd73c788768..e3fbe9d9a4fd 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java @@ -194,13 +194,13 @@ public static BlockState getBlockState(Block block, boolean useSnapshot) { CraftBlock craftBlock = (CraftBlock) block; CraftWorld world = (CraftWorld) block.getWorld(); BlockPos pos = craftBlock.getPosition(); - net.minecraft.world.level.block.state.BlockState state = craftBlock.getNMS(); - BlockEntity blockEntity = craftBlock.getHandle().getBlockEntity(pos); + net.minecraft.world.level.block.state.BlockState state = craftBlock.getBlockState(); + BlockEntity blockEntity = craftBlock.getLevel().getBlockEntity(pos); boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT; CraftBlockEntityState.DISABLE_SNAPSHOT = !useSnapshot; try { CraftBlockState blockState = CraftBlockStates.getBlockState(world, pos, state, blockEntity); - blockState.setWorldHandle(craftBlock.getHandle()); // Inject the block's generator access + blockState.setWorldHandle(craftBlock.getLevel()); // Inject the block's level accessor return blockState; } finally { CraftBlockEntityState.DISABLE_SNAPSHOT = prev; @@ -212,14 +212,14 @@ public static BlockState getBlockState(BlockPos pos, Material material, @Nullabl return CraftBlockStates.getBlockState(CraftRegistry.getMinecraftRegistry(), pos, material, blockEntityTag); } - public static BlockState getBlockState(LevelReader world, BlockPos pos, Material material, @Nullable CompoundTag blockEntityTag) { - return CraftBlockStates.getBlockState(world.registryAccess(), pos, material, blockEntityTag); + public static BlockState getBlockState(LevelReader level, BlockPos pos, Material material, @Nullable CompoundTag blockEntityTag) { + return CraftBlockStates.getBlockState(level.registryAccess(), pos, material, blockEntityTag); } - public static BlockState getBlockState(RegistryAccess registry, BlockPos pos, Material material, @Nullable CompoundTag blockEntityTag) { + public static BlockState getBlockState(RegistryAccess registryAccess, BlockPos pos, Material material, @Nullable CompoundTag blockEntityTag) { Preconditions.checkNotNull(material, "material is null"); - net.minecraft.world.level.block.state.BlockState blockData = CraftBlockType.bukkitToMinecraft(material).defaultBlockState(); - return CraftBlockStates.getBlockState(registry, pos, blockData, blockEntityTag); + net.minecraft.world.level.block.state.BlockState state = CraftBlockType.bukkitToMinecraft(material).defaultBlockState(); + return CraftBlockStates.getBlockState(registryAccess, pos, state, blockEntityTag); } @Deprecated @@ -257,8 +257,8 @@ public static boolean isBlockEntityOptional(Material material) { } // This ignores block entity data. - public static CraftBlockState getBlockState(LevelAccessor world, BlockPos pos) { - return new CraftBlockState(CraftBlock.at(world, pos)); + public static CraftBlockState getBlockState(LevelAccessor level, BlockPos pos) { + return new CraftBlockState(CraftBlock.at(level, pos)); } @Nullable diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockSupport.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockSupport.java index 5d902c1dfad7..a11e8eb3f113 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockSupport.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockSupport.java @@ -13,16 +13,14 @@ public static BlockSupport toBukkit(SupportType support) { case FULL -> BlockSupport.FULL; case CENTER -> BlockSupport.CENTER; case RIGID -> BlockSupport.RIGID; - default -> throw new IllegalArgumentException("Unsupported EnumBlockSupport type: " + support + ". This is a bug."); }; } - public static SupportType toNMS(BlockSupport support) { + public static SupportType toMinecraft(BlockSupport support) { return switch (support) { case FULL -> SupportType.FULL; case CENTER -> SupportType.CENTER; case RIGID -> SupportType.RIGID; - default -> throw new IllegalArgumentException("Unsupported BlockSupport type: " + support + ". This is a bug."); }; } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java index ace48e7b45e1..78744b74d7a0 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java @@ -99,7 +99,7 @@ private static boolean isInteractable(Block block) { @SuppressWarnings("unchecked") public CraftBlockType(final Holder holder) { super(holder); - this.blockDataClass = Suppliers.memoize(() -> (Class) CraftBlockData.fromData(this.getHandle().defaultBlockState()).getClass().getInterfaces()[0]); + this.blockDataClass = Suppliers.memoize(() -> (Class) this.getHandle().defaultBlockState().asBlockData().getClass().getInterfaces()[0]); this.interactable = Suppliers.memoize(() -> CraftBlockType.isInteractable(this.getHandle())); } @@ -150,7 +150,7 @@ public Collection createBlockDataStates() { final ImmutableList possibleStates = this.getHandle().getStateDefinition().getPossibleStates(); final ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize(possibleStates.size()); for (final BlockState possibleState : possibleStates) { - builder.add(this.blockDataClass.get().cast(possibleState.createCraftBlockData())); + builder.add(this.blockDataClass.get().cast(possibleState.asBlockData())); } return builder.build(); } @@ -169,7 +169,7 @@ public B createBlockData(final @Nullable Consumer consumer) { @SuppressWarnings("unchecked") @Override public B createBlockData(final @Nullable String data) { - return (B) CraftBlockData.newData(this, data); + return (B) CraftBlockData.fromString(this, data); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java index f05ef791e4c1..904cfffdded1 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java @@ -1,8 +1,12 @@ package org.bukkit.craftbukkit.block; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.world.MenuProvider; +import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.DoubleBlockCombiner; import net.minecraft.world.level.block.entity.ChestBlockEntity; import net.minecraft.world.level.block.state.BlockState; import org.bukkit.Location; @@ -46,11 +50,11 @@ public Inventory getInventory() { CraftWorld world = (CraftWorld) this.getWorld(); - ChestBlock block = this.data.getBlock() instanceof ChestBlock chestBlock ? chestBlock : (ChestBlock) Blocks.CHEST; - MenuProvider provider = block.getMenuProvider(this.data, world.getHandle(), this.getPosition(), true); + ChestBlock block = this.block.getBlock() instanceof ChestBlock chestBlock ? chestBlock : (ChestBlock) Blocks.CHEST; + MenuProvider provider = block.getMenuProvider(this.block, world.getHandle(), this.getPosition(), true); - if (provider instanceof ChestBlock.DoubleInventory) { - inventory = new CraftInventoryDoubleChest((ChestBlock.DoubleInventory) provider); + if (provider instanceof CraftInventoryDoubleChest.Provider doubleChestProvider) { + inventory = new CraftInventoryDoubleChest(doubleChestProvider); } return inventory; } @@ -58,12 +62,12 @@ public Inventory getInventory() { @Override public void open() { this.requirePlaced(); - if (!this.getBlockEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) { + if (!this.getBlockEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level level) { BlockState block = this.getBlockEntity().getBlockState(); int openCount = this.getBlockEntity().openersCounter.getOpenerCount(); - this.getBlockEntity().openersCounter.onAPIOpen((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block); - this.getBlockEntity().openersCounter.openerAPICountChanged((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block, openCount, openCount + 1); + this.getBlockEntity().openersCounter.onOpenAPI(level, this.getPosition(), block); + this.getBlockEntity().openersCounter.openerCountChangedAPI(level, this.getPosition(), block, openCount, openCount + 1); } this.getBlockEntity().openersCounter.opened = true; } @@ -71,55 +75,52 @@ public void open() { @Override public void close() { this.requirePlaced(); - if (this.getBlockEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) { + if (this.getBlockEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level level) { BlockState block = this.getBlockEntity().getBlockState(); int openCount = this.getBlockEntity().openersCounter.getOpenerCount(); - this.getBlockEntity().openersCounter.onAPIClose((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block); - this.getBlockEntity().openersCounter.openerAPICountChanged((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block, openCount, 0); + this.getBlockEntity().openersCounter.onCloseAPI(level, this.getPosition(), block); + this.getBlockEntity().openersCounter.openerCountChangedAPI(level, this.getPosition(), block, openCount, 0); } this.getBlockEntity().openersCounter.opened = false; } - @Override - public CraftChest copy() { - return new CraftChest(this, null); - } - - @Override - public CraftChest copy(Location location) { - return new CraftChest(this, location); - } - - // Paper start - More Lidded Block API @Override public boolean isOpen() { - return getBlockEntity().openersCounter.opened; + return this.getBlockEntity().openersCounter.opened; } - // Paper end - More Lidded Block API - // Paper start - More Chest Block API @Override public boolean isBlocked() { // Method mimics vanilla logic in ChestBlock and DoubleBlockCombiner when trying to open chest's container if (!isPlaced()) { return false; } - net.minecraft.world.level.LevelAccessor world = getWorldHandle(); - if (ChestBlock.isChestBlockedAt(world, getPosition())) { + + LevelAccessor level = this.getWorldHandle(); + if (ChestBlock.isChestBlockedAt(level, this.getPosition())) { return true; } - if (ChestBlock.getBlockType(this.data) == net.minecraft.world.level.block.DoubleBlockCombiner.BlockType.SINGLE) { + if (ChestBlock.getBlockType(this.block) == DoubleBlockCombiner.BlockType.SINGLE) { return false; } - net.minecraft.core.Direction direction = ChestBlock.getConnectedDirection(this.data); - net.minecraft.core.BlockPos neighbourBlockPos = getPosition().relative(direction); - BlockState neighbourBlockState = world.getBlockStateIfLoaded(neighbourBlockPos); - return neighbourBlockState != null - && neighbourBlockState.is(this.data.getBlock()) - && ChestBlock.getBlockType(neighbourBlockState) != net.minecraft.world.level.block.DoubleBlockCombiner.BlockType.SINGLE - && ChestBlock.getConnectedDirection(neighbourBlockState) == direction.getOpposite() - && ChestBlock.isChestBlockedAt(world, neighbourBlockPos); + Direction direction = ChestBlock.getConnectedDirection(this.block); + BlockPos neighborPos = this.getPosition().relative(direction); + BlockState neighborState = level.getBlockStateIfLoaded(neighborPos); + return neighborState != null + && neighborState.is(this.block.getBlock()) + && ChestBlock.getBlockType(neighborState) != DoubleBlockCombiner.BlockType.SINGLE + && ChestBlock.getConnectedDirection(neighborState) == direction.getOpposite() + && ChestBlock.isChestBlockedAt(level, neighborPos); + } + + @Override + public CraftChest copy() { + return new CraftChest(this, null); + } + + @Override + public CraftChest copy(Location location) { + return new CraftChest(this, location); } - // Paper end - More Chest Block API } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java index bbb685e537ff..170606047e8a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java @@ -33,7 +33,7 @@ public String getName() { @Override public void setName(String name) { - this.getSnapshot().getCommandBlock().setCustomName(CraftChatMessage.fromStringOrNull(name != null ? name : "@")); + this.getSnapshot().getCommandBlock().setCustomName(CraftChatMessage.fromStringOrNull(name)); } @Override @@ -53,7 +53,7 @@ public net.kyori.adventure.text.Component name() { @Override public void name(net.kyori.adventure.text.Component name) { - this.getSnapshot().getCommandBlock().setCustomName(name == null ? net.minecraft.network.chat.Component.literal("@") : io.papermc.paper.adventure.PaperAdventure.asVanilla(name)); + this.getSnapshot().getCommandBlock().setCustomName(io.papermc.paper.adventure.PaperAdventure.asVanilla(name)); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java index 1c448bc38fa5..eb7840449c03 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.Collection; -import net.minecraft.Optionull; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.EntityReference; import net.minecraft.world.level.block.entity.ConduitBlockEntity; @@ -104,7 +103,7 @@ public boolean setTarget(LivingEntity target) { ConduitBlockEntity.updateAndAttackTarget( conduit.getLevel().getMinecraftWorld(), this.getPosition(), - this.data, + this.block, conduit, conduit.effectBlocks.size() >= ConduitBlockEntity.MIN_KILL_SIZE, false diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftContainer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftContainer.java index 075921649b8a..ad3da82fa35e 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftContainer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftContainer.java @@ -1,11 +1,9 @@ package org.bukkit.craftbukkit.block; -import java.util.Collections; -import java.util.Optional; import net.minecraft.advancements.criterion.DataComponentMatchers; import net.minecraft.advancements.criterion.ItemPredicate; -import net.minecraft.advancements.criterion.MinMaxBounds; import net.minecraft.core.component.DataComponentExactPredicate; +import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; import net.minecraft.world.LockCode; @@ -34,18 +32,22 @@ public boolean isLocked() { @Override public String getLock() { - Optional customName = this.getSnapshot().lockKey.predicate().components().exact().asPatch().get(DataComponents.CUSTOM_NAME); - - return (customName != null) ? customName.map(CraftChatMessage::fromComponent).orElse("") : ""; + Component customName = this.getSnapshot().lockKey.predicate().components().exact().asPatch().get(DataComponentMap.EMPTY, DataComponents.CUSTOM_NAME); + return (customName != null) ? CraftChatMessage.fromComponent(customName) : ""; } @Override public void setLock(String key) { - if (key == null) { + if (key == null || key.isEmpty()) { this.getSnapshot().lockKey = LockCode.NO_LOCK; } else { - DataComponentExactPredicate predicate = DataComponentExactPredicate.builder().expect(DataComponents.CUSTOM_NAME, CraftChatMessage.fromStringOrNull(key)).build(); - this.getSnapshot().lockKey = new LockCode(new ItemPredicate(Optional.empty(), MinMaxBounds.Ints.ANY, new DataComponentMatchers(predicate, Collections.emptyMap()))); + this.getSnapshot().lockKey = new LockCode(ItemPredicate.Builder.item().withComponents( + DataComponentMatchers.Builder.components().exact( + DataComponentExactPredicate.builder().expect( + DataComponents.CUSTOM_NAME, CraftChatMessage.fromString(key)[0] + ).build() + ).build() + ).build()); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftEndGateway.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftEndGateway.java index c6b931fc200c..90aaf37320d0 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftEndGateway.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftEndGateway.java @@ -31,7 +31,7 @@ public void setExitLocation(Location location) { } else if (!Objects.equals(location.getWorld(), this.isPlaced() ? this.getWorld() : null)) { throw new IllegalArgumentException("Cannot set exit location to different world"); } else { - this.getSnapshot().exitPortal = CraftLocation.toBlockPosition(location); + this.getSnapshot().exitPortal = CraftLocation.toBlockPos(location); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java index cb2a78e6ca51..2a9243e416bb 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.block; +import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.entity.EnderChestBlockEntity; import net.minecraft.world.level.block.state.BlockState; import org.bukkit.Location; @@ -19,12 +20,12 @@ protected CraftEnderChest(CraftEnderChest state, Location location) { @Override public void open() { this.requirePlaced(); - if (!this.getBlockEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) { + if (!this.getBlockEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level level) { BlockState block = this.getBlockEntity().getBlockState(); int openCount = this.getBlockEntity().openersCounter.getOpenerCount(); - this.getBlockEntity().openersCounter.onAPIOpen((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block); - this.getBlockEntity().openersCounter.openerAPICountChanged((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block, openCount, openCount + 1); + this.getBlockEntity().openersCounter.onOpenAPI(level, this.getPosition(), block); + this.getBlockEntity().openersCounter.openerCountChangedAPI(level, this.getPosition(), block, openCount, openCount + 1); } this.getBlockEntity().openersCounter.opened = true; } @@ -32,39 +33,35 @@ public void open() { @Override public void close() { this.requirePlaced(); - if (this.getBlockEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) { + if (this.getBlockEntity().openersCounter.opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level level) { BlockState block = this.getBlockEntity().getBlockState(); int openCount = this.getBlockEntity().openersCounter.getOpenerCount(); - this.getBlockEntity().openersCounter.onAPIClose((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block); - this.getBlockEntity().openersCounter.openerAPICountChanged((net.minecraft.world.level.Level) this.getWorldHandle(), this.getPosition(), block, openCount, 0); + this.getBlockEntity().openersCounter.onCloseAPI(level, this.getPosition(), block); + this.getBlockEntity().openersCounter.openerCountChangedAPI(level, this.getPosition(), block, openCount, 0); } this.getBlockEntity().openersCounter.opened = false; } @Override - public CraftEnderChest copy() { - return new CraftEnderChest(this, null); + public boolean isOpen() { + return this.getBlockEntity().openersCounter.opened; } @Override - public CraftEnderChest copy(Location location) { - return new CraftEnderChest(this, location); + public boolean isBlocked() { + // Uses the same logic as EnderChestBlock's check for opening container + final BlockPos abovePos = this.getPosition().above(); + return this.isPlaced() && this.getWorldHandle().getBlockState(abovePos).isRedstoneConductor(this.getWorldHandle(), abovePos); } - // Paper start - More Lidded Block API @Override - public boolean isOpen() { - return getBlockEntity().openersCounter.opened; + public CraftEnderChest copy() { + return new CraftEnderChest(this, null); } - // Paper end - More Lidded Block API - // Paper start - More Chest Block API @Override - public boolean isBlocked() { - // Uses the same logic as EnderChestBlock's check for opening container - final net.minecraft.core.BlockPos abovePos = this.getPosition().above(); - return this.isPlaced() && this.getWorldHandle().getBlockState(abovePos).isRedstoneConductor(this.getWorldHandle(), abovePos); + public CraftEnderChest copy(Location location) { + return new CraftEnderChest(this, location); } - // Paper end - More Chest Block API } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java index a8b57a406b9f..33a7f3e3cc6a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java @@ -46,7 +46,7 @@ public short getBurnTime() { @Override public void setBurnTime(short burnTime) { this.getSnapshot().litTimeRemaining = burnTime; - this.data = this.data.trySetValue(AbstractFurnaceBlock.LIT, burnTime > 0); + this.block = this.block.trySetValue(AbstractFurnaceBlock.LIT, burnTime > 0); // only try, block data might have changed to something different that would not allow this property } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java index ba7227f95220..d5b7cae0925d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java @@ -43,7 +43,7 @@ public boolean update(boolean force, boolean applyPhysics) { boolean result = super.update(force, applyPhysics); if (result && this.isPlaced() && this.getType() == Material.JUKEBOX) { - this.getWorldHandle().setBlock(this.getPosition(), this.data, Block.UPDATE_ALL); + this.getWorldHandle().setBlock(this.getPosition(), this.block, Block.UPDATE_ALL); BlockEntity blockEntity = this.getBlockEntityFromWorld(); if (blockEntity instanceof JukeboxBlockEntity jukebox) { @@ -70,7 +70,7 @@ record = Material.AIR; @Override public boolean hasRecord() { - return this.data.getValueOrElse(JukeboxBlock.HAS_RECORD, false) && !this.getPlaying().isAir(); + return this.block.getValueOrElse(JukeboxBlock.HAS_RECORD, false) && !this.getPlaying().isAir(); } @Override @@ -86,7 +86,7 @@ public void setRecord(org.bukkit.inventory.ItemStack record) { JukeboxBlockEntity snapshot = this.getSnapshot(); snapshot.setSongItemWithoutPlaying(nms, snapshot.getSongPlayer().getTicksSinceSongStarted()); - this.data = this.data.trySetValue(JukeboxBlock.HAS_RECORD, !nms.isEmpty()); + this.block = this.block.trySetValue(JukeboxBlock.HAS_RECORD, !nms.isEmpty()); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftMovingPiston.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftMovingPiston.java index b4b79f519ae8..2beb8ec07de1 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftMovingPiston.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftMovingPiston.java @@ -27,7 +27,7 @@ public CraftMovingPiston copy(Location location) { // Paper start - Add Moving Piston API @Override public org.bukkit.block.data.BlockData getMovingBlock() { - return org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(this.getBlockEntity().getMovedState()); + return this.getBlockEntity().getMovedState().asBlockData(); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java index 0f08bc5f79ac..c9337d641d5f 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java @@ -46,9 +46,9 @@ public DyeColor getColor() { public void open() { this.requirePlaced(); if (!this.getBlockEntity().opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) { - net.minecraft.world.level.Level world = this.getBlockEntity().getLevel(); - world.blockEvent(this.getPosition(), this.getBlockEntity().getBlockState().getBlock(), 1, 1); - world.playSound(null, this.getPosition(), SoundEvents.SHULKER_BOX_OPEN, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); + net.minecraft.world.level.Level level = this.getBlockEntity().getLevel(); + level.blockEvent(this.getPosition(), this.getBlockEntity().getBlockState().getBlock(), 1, 1); + level.playSound(null, this.getPosition(), SoundEvents.SHULKER_BOX_OPEN, SoundSource.BLOCKS, 0.5F, level.getRandom().nextFloat() * 0.1F + 0.9F); } this.getBlockEntity().opened = true; } @@ -57,9 +57,9 @@ public void open() { public void close() { this.requirePlaced(); if (this.getBlockEntity().opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) { - net.minecraft.world.level.Level world = this.getBlockEntity().getLevel(); - world.blockEvent(this.getPosition(), this.getBlockEntity().getBlockState().getBlock(), 1, 0); - world.playSound(null, this.getPosition(), SoundEvents.SHULKER_BOX_CLOSE, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); // Paper - More Lidded Block API (Wrong sound) + net.minecraft.world.level.Level level = this.getBlockEntity().getLevel(); + level.blockEvent(this.getPosition(), this.getBlockEntity().getBlockState().getBlock(), 1, 0); + level.playSound(null, this.getPosition(), SoundEvents.SHULKER_BOX_CLOSE, SoundSource.BLOCKS, 0.5F, level.getRandom().nextFloat() * 0.1F + 0.9F); // Paper - More Lidded Block API (Wrong sound) } this.getBlockEntity().opened = false; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawner.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawner.java index 6230ef5030f0..4fedf2554b25 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawner.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawner.java @@ -143,23 +143,23 @@ public void stopTrackingEntity(Entity entity) { @Override public boolean isOminous() { - return this.data.getValueOrElse(TrialSpawnerBlock.OMINOUS, false); + return this.block.getValueOrElse(TrialSpawnerBlock.OMINOUS, false); } @Override public void setOminous(boolean ominous) { - if (!this.data.hasProperty(TrialSpawnerBlock.OMINOUS)) { + if (!this.block.hasProperty(TrialSpawnerBlock.OMINOUS)) { return; // block data changed } this.getSnapshot().trialSpawner.isOminous = ominous; if (ominous) { - this.data = this.data.setValue(TrialSpawnerBlock.OMINOUS, true); + this.block = this.block.setValue(TrialSpawnerBlock.OMINOUS, true); // TODO: Consider calling TrialSpawnerData#resetAfterBecomingOminous in update(...), but note that method also removes entities return; } - this.data = this.data.setValue(TrialSpawnerBlock.OMINOUS, false); + this.block = this.block.setValue(TrialSpawnerBlock.OMINOUS, false); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawnerConfiguration.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawnerConfiguration.java index 358c65f5b344..dd70d5010d2d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawnerConfiguration.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawnerConfiguration.java @@ -273,11 +273,11 @@ public void addPossibleReward(LootTable table, int weight) { public void removePossibleReward(LootTable table) { Preconditions.checkArgument(table != null, "Key cannot be null"); - ResourceKey minecraftKey = CraftLootTable.bukkitToMinecraft(table); + ResourceKey key = CraftLootTable.bukkitToMinecraft(table); WeightedList.Builder> builder = WeightedList.builder(); for (Weighted> entry : this.lootTablesToEject.unwrap()) { - if (!entry.value().equals(minecraftKey)) { + if (!entry.value().equals(key)) { builder.add(entry.value(), entry.weight()); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java index 68bb1b852273..944acb68a5e5 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java @@ -5,30 +5,39 @@ import com.google.common.collect.ImmutableSet; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import java.util.stream.Stream; import net.minecraft.commands.arguments.blocks.BlockStateParser; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.util.StringRepresentable; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateHolder; -import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.EnumProperty; -import net.minecraft.world.level.block.state.properties.IntegerProperty; import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.apache.commons.lang3.mutable.MutableDouble; import org.bukkit.Color; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.SoundGroup; import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; import org.bukkit.block.BlockSupport; import org.bukkit.block.BlockType; import org.bukkit.block.PistonMoveReaction; @@ -41,187 +50,143 @@ import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.craftbukkit.block.CraftBlockStates; import org.bukkit.craftbukkit.block.CraftBlockSupport; -import org.bukkit.craftbukkit.block.CraftBlockType; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.craftbukkit.inventory.CraftItemType; import org.bukkit.craftbukkit.util.CraftLocation; +import org.bukkit.craftbukkit.util.CraftVoxelShape; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.Nullable; public class CraftBlockData implements BlockData { - private net.minecraft.world.level.block.state.BlockState state; - private Map, Comparable> parsedStates; + private BlockState state; + private List> parsedStates; - protected CraftBlockData() { - throw new AssertionError("Template Constructor"); - } - - protected CraftBlockData(net.minecraft.world.level.block.state.BlockState state) { + protected CraftBlockData(BlockState state) { this.state = state; } @Override public Material getMaterial() { - return this.state.getBukkitMaterial(); // Paper - optimise getType calls + return this.state.getBukkitMaterial(); } - public net.minecraft.world.level.block.state.BlockState getState() { + public BlockState getState() { return this.state; } - /** - * Get a given BlockStateEnum's value as its Bukkit counterpart. - * - * @param nms the NMS state to convert - * @param bukkit the Bukkit class - * @param the type - * @return the matching Bukkit type - */ - protected > B get(EnumProperty nms, Class bukkit) { - return CraftBlockData.toBukkit(this.state.getValue(nms), bukkit); - } - - /** - * Convert all values from the given BlockStateEnum to their appropriate - * Bukkit counterpart. - * - * @param nms the NMS state to get values from - * @param bukkit the bukkit class to convert the values to - * @param the bukkit class type - * @return an immutable Set of values in their appropriate Bukkit type - */ - @SuppressWarnings("unchecked") - protected > Set getValues(EnumProperty nms, Class bukkit) { - ImmutableSet.Builder values = ImmutableSet.builder(); - - for (Enum e : nms.getPossibleValues()) { - values.add(CraftBlockData.toBukkit(e, bukkit)); - } - - return values.build(); - } - - /** - * Set a given {@link EnumProperty} with the matching enum from Bukkit. - * - * @param nms the NMS BlockStateEnum to set - * @param bukkit the matching Bukkit Enum - * @param the Bukkit type - * @param the NMS type - */ - protected , N extends Enum & StringRepresentable> void set(EnumProperty nms, Enum bukkit) { - this.parsedStates = null; - this.state = this.state.setValue(nms, CraftBlockData.toNMS(bukkit, nms.getValueClass())); - } - @Override public BlockData merge(BlockData data) { CraftBlockData craft = (CraftBlockData) data; - Preconditions.checkArgument(craft.parsedStates != null, "Data not created via string parsing"); - Preconditions.checkArgument(this.state.getBlock() == craft.state.getBlock(), "States have different types (got %s, expected %s)", data, this); + Preconditions.checkArgument(craft.parsedStates != null, "Block data not created via string parsing"); + Preconditions.checkArgument(this.state.getBlock() == craft.state.getBlock(), "States have different types (got %s, expected %s)", craft.state.getBlock(), this.state.getBlock()); CraftBlockData clone = (CraftBlockData) this.clone(); clone.parsedStates = null; - for (Property parsed : craft.parsedStates.keySet()) { - clone.state = clone.state.setValue(parsed, craft.state.getValue(parsed)); + for (Property.Value parsed : craft.parsedStates) { + clone.state = setValue(clone.state, parsed); } return clone; } + private static > BlockState setValue(final BlockState state, final Property.Value propertyValue) { + return state.setValue(propertyValue.property(), propertyValue.value()); + } + + private > BlockState copyProperty(BlockState source, BlockState target, Property property) { + return target.setValue(property, source.getValue(property)); + } + @Override public boolean matches(BlockData data) { - if (data == null) { - return false; - } - if (!(data instanceof CraftBlockData)) { + if (!(data instanceof final CraftBlockData craft)) { return false; } - - CraftBlockData craft = (CraftBlockData) data; if (this.state.getBlock() != craft.state.getBlock()) { return false; } // Fastpath an exact match - boolean exactMatch = this.equals(data); + if (this.equals(data)) { + return true; + } // If that failed, do a merge and check - if (!exactMatch && craft.parsedStates != null) { + if (craft.parsedStates != null) { return this.merge(data).equals(this); } - - return exactMatch; + return false; } - private static final Map>, Enum[]> ENUM_VALUES = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - cache block data strings; make thread safe + private static final ClassValue[]> ENUM_VALUES = new ClassValue<>() { + @Override + protected Enum[] computeValue(final Class klass) { + return (Enum[]) klass.getEnumConstants(); + } + }; - /** - * Convert an NMS Enum (usually a BlockStateEnum) to its appropriate Bukkit - * enum from the given class. - * - * @throws IllegalStateException if the Enum could not be converted - */ - @SuppressWarnings("unchecked") - public static > B toBukkit(Enum nms, Class bukkit) { - if (nms instanceof Direction) { - return (B) CraftBlock.notchToBlockFace((Direction) nms); + public static > B fromVanilla(Enum internal, Class bukkit) { + final Object rawValue; + if (internal instanceof Direction direction) { + rawValue = CraftBlock.notchToBlockFace(direction); + } else { + rawValue = ENUM_VALUES.get(bukkit)[internal.ordinal()]; } - return (B) CraftBlockData.ENUM_VALUES.computeIfAbsent(bukkit, Class::getEnumConstants)[nms.ordinal()]; - } - - /** - * Convert a given Bukkit enum to its matching NMS enum type. - * - * @param bukkit the Bukkit enum to convert - * @param nms the NMS class - * @return the matching NMS type - * @throws IllegalStateException if the Enum could not be converted - */ - @SuppressWarnings("unchecked") - public static & StringRepresentable> N toNMS(Enum bukkit, Class nms) { - if (bukkit instanceof BlockFace) { - return (N) CraftBlock.blockFaceToNotch((BlockFace) bukkit); + return bukkit.cast(rawValue); + } + + public static & StringRepresentable> M toVanilla(Enum bukkit, Class internalClass) { + final Object rawValue; + if (bukkit instanceof BlockFace face) { + rawValue = CraftBlock.blockFaceToNotch(face); + } else { + rawValue = ENUM_VALUES.get(internalClass)[bukkit.ordinal()]; + } + return internalClass.cast(rawValue); + } + + protected > A get(EnumProperty property, Class bukkitClass) { + return fromVanilla(this.state.getValue(property), bukkitClass); + } + + protected & StringRepresentable, A extends Enum> @Unmodifiable Set getValues(EnumProperty property, Class bukkitClass) { + List values = property.getPossibleValues(); + ImmutableSet.Builder result = ImmutableSet.builderWithExpectedSize(values.size()); + + for (M e : values) { + result.add(fromVanilla(e, bukkitClass)); } - return (N) CraftBlockData.ENUM_VALUES.computeIfAbsent(nms, Class::getEnumConstants)[bukkit.ordinal()]; - } - - /** - * Get the current value of a given state. - * - * @param ibs the state to check - * @param the type - * @return the current value of the given state - */ - protected > T get(Property ibs) { - // Straight integer or boolean getter - return this.state.getValue(ibs); - } - - /** - * Set the specified state's value. - * - * @param ibs the state to set - * @param v the new value - * @param the state's type - * @param the value's type. Must match the state's type. - */ - public , V extends T> void set(Property ibs, V v) { - // Straight integer or boolean setter + + return result.build(); + } + + protected , M extends Enum & StringRepresentable> void set(EnumProperty property, A bukkit) { this.parsedStates = null; - this.state = this.state.setValue(ibs, v); + this.state = this.state.setValue(property, toVanilla(bukkit, property.getValueClass())); + } + + // Straight integer or boolean getter + protected > T get(Property property) { + return this.state.getValue(property); + } + + // Straight integer or boolean setter + public , V extends T> void set(Property property, V value) { + this.parsedStates = null; + this.state = this.state.setValue(property, value); } @Override public String getAsString() { - return this.toString(this.state.getValues()); + return this.toString(this.state.getValues(), this.state.isSingletonState()); } @Override public String getAsString(boolean hideUnspecified) { - return (hideUnspecified && this.parsedStates != null) ? this.toString(this.parsedStates) : this.getAsString(); + return (hideUnspecified && this.parsedStates != null) ? this.toString(this.parsedStates.stream(), this.parsedStates.isEmpty()) : this.getAsString(); } @Override @@ -238,33 +203,25 @@ public String toString() { return "CraftBlockData{" + this.getAsString() + "}"; } - // Mimicked from BlockDataAbstract#toString() - public String toString(Map, Comparable> states) { + // Mimicked from StateHolder#toString() prefixed by just the key instead of Block{key} + public String toString(Stream> states, boolean singleton) { StringBuilder stateString = new StringBuilder(BuiltInRegistries.BLOCK.getKey(this.state.getBlock()).toString()); - - if (!states.isEmpty()) { + if (!singleton) { stateString.append('['); - stateString.append(states.entrySet().stream().map(StateHolder.PROPERTY_ENTRY_TO_STRING_FUNCTION).collect(Collectors.joining(","))); + stateString.append(states.map(Property.Value::toString).collect(Collectors.joining(","))); stateString.append(']'); } - return stateString.toString(); } public Map toStates(boolean hideUnspecified) { - return (hideUnspecified && this.parsedStates != null) ? CraftBlockData.toStates(this.parsedStates) : CraftBlockData.toStates(this.state.getValues()); - } - - private static Map toStates(Map, Comparable> states) { - Map compound = new HashMap<>(); - - for (Map.Entry, Comparable> entry : states.entrySet()) { - Property iblockstate = (Property) entry.getKey(); - - compound.put(iblockstate.getName(), iblockstate.getName(entry.getValue())); + Map serializedStates = new HashMap<>(); + if (hideUnspecified && this.parsedStates != null) { + this.parsedStates.forEach(state -> serializedStates.put(state.property().getName(), state.valueName())); + } else { + this.state.getValues().forEach(state -> serializedStates.put(state.property().getName(), state.valueName())); } - - return compound; + return serializedStates; } @Override @@ -277,69 +234,6 @@ public int hashCode() { return this.state.hashCode(); } - protected static BooleanProperty getBoolean(String name) { - throw new AssertionError("Template Method"); - } - - protected static BooleanProperty getBoolean(String name, boolean optional) { - throw new AssertionError("Template Method"); - } - - protected static EnumProperty getEnum(String name) { - throw new AssertionError("Template Method"); - } - - protected static IntegerProperty getInteger(String name) { - throw new AssertionError("Template Method"); - } - - protected static BooleanProperty getBoolean(Class block, String name) { - return (BooleanProperty) CraftBlockData.getState(block, name, false); - } - - protected static BooleanProperty getBoolean(Class block, String name, boolean optional) { - return (BooleanProperty) CraftBlockData.getState(block, name, optional); - } - - protected static EnumProperty getEnum(Class block, String name) { - return (EnumProperty) CraftBlockData.getState(block, name, false); - } - - protected static IntegerProperty getInteger(Class block, String name) { - return (IntegerProperty) CraftBlockData.getState(block, name, false); - } - - /** - * Get a specified {@link Property} from a given block's class with a - * given name - * - * @param block the class to retrieve the state from - * @param name the name of the state to retrieve - * @param optional if the state can be null - * @return the specified state or null - * @throws IllegalStateException if the state is null and {@code optional} - * is false. - */ - private static Property getState(Class block, String name, boolean optional) { - Property state = null; - - for (Block instance : BuiltInRegistries.BLOCK) { - if (instance.getClass() == block) { - if (state == null) { - state = instance.getStateDefinition().getProperty(name); - } else { - Property newState = instance.getStateDefinition().getProperty(name); - - Preconditions.checkState(state == newState, "State mismatch %s,%s", state, newState); - } - } - } - - Preconditions.checkState(optional || state != null, "Null state for %s,%s", block, name); - - return state; - } - public static final BlockFace[] ROTATION_CYCLE = { BlockFace.SOUTH, BlockFace.SOUTH_SOUTH_WEST, BlockFace.SOUTH_WEST, BlockFace.WEST_SOUTH_WEST, BlockFace.WEST, BlockFace.WEST_NORTH_WEST, BlockFace.NORTH_WEST, BlockFace.NORTH_NORTH_WEST, @@ -347,21 +241,11 @@ private static Property getState(Class block, String name, b BlockFace.EAST, BlockFace.EAST_SOUTH_EAST, BlockFace.SOUTH_EAST, BlockFace.SOUTH_SOUTH_EAST }; - /** - * Get the maximum value allowed by the BlockStateInteger. - * - * @param state the state to check - * @return the maximum value allowed - */ - protected static int getMax(IntegerProperty state) { - return state.max; - } - - private static final Map, Function> MAP = new HashMap<>(); + private static final Map, Function> INSTANCE_CREATOR = new HashMap<>(); static { // - // Start generate - CraftBlockData#MAP + // Start generate - CraftBlockData#INSTANCE_CREATOR register(net.minecraft.world.level.block.AmethystClusterBlock.class, org.bukkit.craftbukkit.block.impl.CraftAmethystCluster::new); register(net.minecraft.world.level.block.AnvilBlock.class, org.bukkit.craftbukkit.block.impl.CraftAnvil::new); register(net.minecraft.world.level.block.AttachedStemBlock.class, org.bukkit.craftbukkit.block.impl.CraftAttachedStem::new); @@ -424,7 +308,7 @@ protected static int getMax(IntegerProperty state) { register(net.minecraft.world.level.block.EndPortalFrameBlock.class, org.bukkit.craftbukkit.block.impl.CraftEndPortalFrame::new); register(net.minecraft.world.level.block.EndRodBlock.class, org.bukkit.craftbukkit.block.impl.CraftEndRod::new); register(net.minecraft.world.level.block.EnderChestBlock.class, org.bukkit.craftbukkit.block.impl.CraftEnderChest::new); - register(net.minecraft.world.level.block.FarmBlock.class, org.bukkit.craftbukkit.block.impl.CraftFarm::new); + register(net.minecraft.world.level.block.FarmlandBlock.class, org.bukkit.craftbukkit.block.impl.CraftFarmland::new); register(net.minecraft.world.level.block.FenceBlock.class, org.bukkit.craftbukkit.block.impl.CraftFence::new); register(net.minecraft.world.level.block.FenceGateBlock.class, org.bukkit.craftbukkit.block.impl.CraftFenceGate::new); register(net.minecraft.world.level.block.FireBlock.class, org.bukkit.craftbukkit.block.impl.CraftFire::new); @@ -498,7 +382,7 @@ protected static int getMax(IntegerProperty state) { register(net.minecraft.world.level.block.SmokerBlock.class, org.bukkit.craftbukkit.block.impl.CraftSmoker::new); register(net.minecraft.world.level.block.SnifferEggBlock.class, org.bukkit.craftbukkit.block.impl.CraftSnifferEgg::new); register(net.minecraft.world.level.block.SnowLayerBlock.class, org.bukkit.craftbukkit.block.impl.CraftSnowLayer::new); - register(net.minecraft.world.level.block.SnowyDirtBlock.class, org.bukkit.craftbukkit.block.impl.CraftSnowyDirt::new); + register(net.minecraft.world.level.block.SnowyBlock.class, org.bukkit.craftbukkit.block.impl.CraftSnowy::new); register(net.minecraft.world.level.block.StainedGlassPaneBlock.class, org.bukkit.craftbukkit.block.impl.CraftStainedGlassPane::new); register(net.minecraft.world.level.block.StairBlock.class, org.bukkit.craftbukkit.block.impl.CraftStair::new); register(net.minecraft.world.level.block.StandingSignBlock.class, org.bukkit.craftbukkit.block.impl.CraftStandingSign::new); @@ -550,88 +434,73 @@ protected static int getMax(IntegerProperty state) { register(net.minecraft.world.level.block.piston.MovingPistonBlock.class, org.bukkit.craftbukkit.block.impl.CraftMovingPiston::new); register(net.minecraft.world.level.block.piston.PistonBaseBlock.class, org.bukkit.craftbukkit.block.impl.CraftPistonBase::new); register(net.minecraft.world.level.block.piston.PistonHeadBlock.class, org.bukkit.craftbukkit.block.impl.CraftPistonHead::new); - // End generate - CraftBlockData#MAP + // End generate - CraftBlockData#INSTANCE_CREATOR // } - private static void register(Class nms, Function bukkit) { - Preconditions.checkState(CraftBlockData.MAP.put(nms, bukkit) == null, "Duplicate mapping %s->%s", nms, bukkit); + private static void register(Class blockClass, Function newInstance) { + Preconditions.checkState(INSTANCE_CREATOR.put(blockClass, newInstance) == null, "Duplicate mapping %s->%s", blockClass, newInstance); } - // Paper start - cache block data strings - private static Map stringDataCache = new java.util.concurrent.ConcurrentHashMap<>(); + private static final Map ENCODER_CACHE = new ConcurrentHashMap<>(); static { - // cache all of the default states at startup, will not cache ones with the custom states inside of the + // cache all the default states at startup, will not cache ones with the custom states inside of the // brackets in a different order, though reloadCache(); } public static void reloadCache() { - stringDataCache.clear(); - Block.BLOCK_STATE_REGISTRY.forEach(blockData -> stringDataCache.put(blockData.toString(), blockData.createCraftBlockData())); + ENCODER_CACHE.clear(); + Block.BLOCK_STATE_REGISTRY.forEach(state -> { + CraftBlockData data = state.asBlockData(); + ENCODER_CACHE.put(data.getAsString(), data); + }); } - // Paper end - cache block data strings - - public static CraftBlockData newData(BlockType blockType, String data) { - // Paper start - cache block data strings + public static CraftBlockData fromString(@Nullable BlockType blockType, @Nullable String data) { + StringBuilder serializedData = new StringBuilder(); if (blockType != null) { - Block block = CraftBlockType.bukkitToMinecraftNew(blockType); - if (block != null) { - net.minecraft.resources.Identifier key = BuiltInRegistries.BLOCK.getKey(block); - data = data == null ? key.toString() : key + data; - } + serializedData.append(blockType.key().asString()); } - CraftBlockData cached = stringDataCache.computeIfAbsent(data, s -> createNewData(null, s)); - return (CraftBlockData) cached.clone(); - } - - private static CraftBlockData createNewData(BlockType blockType, String data) { - // Paper end - cache block data strings - net.minecraft.world.level.block.state.BlockState blockData; - Block block = blockType == null ? null : ((CraftBlockType) blockType).getHandle(); - Map, Comparable> parsed = null; - - // Data provided, use it if (data != null) { - try { - // Material provided, force that material in - if (block != null) { - data = BuiltInRegistries.BLOCK.getKey(block) + data; - } + serializedData.append(data); + } - StringReader reader = new StringReader(data); - BlockStateParser.BlockResult arg = BlockStateParser.parseForBlock(CraftRegistry.getMinecraftRegistry(Registries.BLOCK), reader, false); - Preconditions.checkArgument(!reader.canRead(), "Spurious trailing data: " + data); + CraftBlockData cached = ENCODER_CACHE.computeIfAbsent(serializedData.toString(), CraftBlockData::parseData); + return (CraftBlockData) cached.clone(); + } - blockData = arg.blockState(); - parsed = arg.properties(); - } catch (CommandSyntaxException ex) { - throw new IllegalArgumentException("Could not parse data: " + data, ex); + private static CraftBlockData parseData(String serializedData) { + final BlockStateParser.BlockResult result; + try { + StringReader reader = new StringReader(serializedData); + result = BlockStateParser.parseForBlock(CraftRegistry.getMinecraftRegistry(Registries.BLOCK), reader, false); + if (reader.canRead()) { + throw new IllegalArgumentException("Spurious trailing data: " + reader.getRemaining()); } - } else { - blockData = block.defaultBlockState(); + } catch (CommandSyntaxException ex) { + throw new IllegalArgumentException("Could not parse data: " + serializedData, ex); } - CraftBlockData craft = CraftBlockData.fromData(blockData); - craft.parsedStates = parsed; - return craft; + CraftBlockData data = result.blockState().asBlockData(); + List> parsed = new ArrayList<>(result.properties().size()); + result.properties().forEach((property, value) -> { + parsed.add(StateHolder.createValue(property, value)); + }); + + data.parsedStates = parsed; + return data; } - // Paper start - optimize creating BlockData to not need a map lookup static { // Initialize cached data for all BlockState instances after registration - Block.BLOCK_STATE_REGISTRY.iterator().forEachRemaining(net.minecraft.world.level.block.state.BlockState::createCraftBlockData); - } - public static CraftBlockData fromData(net.minecraft.world.level.block.state.BlockState data) { - return data.createCraftBlockData(); + Block.BLOCK_STATE_REGISTRY.forEach(BlockState::asBlockData); } - public static CraftBlockData createData(net.minecraft.world.level.block.state.BlockState data) { - // Paper end - return CraftBlockData.MAP.getOrDefault(data.getBlock().getClass(), CraftBlockData::new).apply(data); + public static CraftBlockData createData(BlockState state) { + return INSTANCE_CREATOR.getOrDefault(state.getBlock().getClass(), CraftBlockData::new).apply(state); } @Override @@ -657,13 +526,11 @@ public boolean requiresCorrectToolForDrops() { @Override public boolean isPreferredTool(ItemStack tool) { Preconditions.checkArgument(tool != null, "tool must not be null"); - - net.minecraft.world.item.ItemStack nms = CraftItemStack.asNMSCopy(tool); - return CraftBlockData.isPreferredTool(this.state, nms); + return isPreferredTool(this.state, CraftItemStack.asNMSCopy(tool)); } - public static boolean isPreferredTool(net.minecraft.world.level.block.state.BlockState iblockdata, net.minecraft.world.item.ItemStack nmsItem) { - return !iblockdata.requiresCorrectToolForDrops() || nmsItem.isCorrectToolForDrops(iblockdata); + public static boolean isPreferredTool(BlockState state, net.minecraft.world.item.ItemStack item) { + return !state.requiresCorrectToolForDrops() || item.isCorrectToolForDrops(state); } @Override @@ -686,7 +553,7 @@ public boolean isSupported(Location location) { CraftWorld world = (CraftWorld) location.getWorld(); Preconditions.checkArgument(world != null, "location must not have a null world"); - BlockPos position = CraftLocation.toBlockPosition(location); + BlockPos position = CraftLocation.toBlockPos(location); return this.state.canSurvive(world.getHandle(), position); } @@ -695,10 +562,9 @@ public boolean isFaceSturdy(BlockFace face, BlockSupport support) { Preconditions.checkArgument(face != null, "face must not be null"); Preconditions.checkArgument(support != null, "support must not be null"); - return this.state.isFaceSturdy(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CraftBlock.blockFaceToNotch(face), CraftBlockSupport.toNMS(support)); + return this.state.isFaceSturdy(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, CraftBlock.blockFaceToNotch(face), CraftBlockSupport.toMinecraft(support)); } - // Paper start @Override public org.bukkit.util.VoxelShape getCollisionShape(Location location) { Preconditions.checkArgument(location != null, "location must not be null"); @@ -706,11 +572,10 @@ public org.bukkit.util.VoxelShape getCollisionShape(Location location) { CraftWorld world = (CraftWorld) location.getWorld(); Preconditions.checkArgument(world != null, "location must not have a null world"); - BlockPos position = CraftLocation.toBlockPosition(location); - net.minecraft.world.phys.shapes.VoxelShape shape = this.state.getCollisionShape(world.getHandle(), position); - return new org.bukkit.craftbukkit.util.CraftVoxelShape(shape); + BlockPos position = CraftLocation.toBlockPos(location); + VoxelShape shape = this.state.getCollisionShape(world.getHandle(), position); + return new CraftVoxelShape(shape); } - // Paper end @Override public Color getMapColor() { @@ -735,45 +600,39 @@ public void mirror(Mirror mirror) { @Override public void copyTo(BlockData blockData) { CraftBlockData other = (CraftBlockData) blockData; - net.minecraft.world.level.block.state.BlockState nms = other.state; + BlockState otherState = other.state; for (Property property : this.state.getBlock().getStateDefinition().getProperties()) { - if (nms.hasProperty(property)) { - nms = this.copyProperty(this.state, nms, property); + if (otherState.hasProperty(property)) { + otherState = this.copyProperty(this.state, otherState, property); } } - other.state = nms; - } - - private > net.minecraft.world.level.block.state.BlockState copyProperty(net.minecraft.world.level.block.state.BlockState source, net.minecraft.world.level.block.state.BlockState target, Property property) { - return target.setValue(property, source.getValue(property)); + other.state = otherState; } - @NotNull @Override - public BlockState createBlockState() { + public org.bukkit.block.BlockState createBlockState() { return CraftBlockStates.getBlockState(this.state, null); } - // Paper start - destroy speed API @Override - public float getDestroySpeed(final ItemStack itemStack, final boolean considerEnchants) { - net.minecraft.world.item.ItemStack nmsItemStack = CraftItemStack.unwrap(itemStack); - float speed = nmsItemStack.getDestroySpeed(this.state); + public float getDestroySpeed(final ItemStack item, final boolean considerEnchants) { + net.minecraft.world.item.ItemStack itemStack = CraftItemStack.unwrap(item); + float speed = itemStack.getDestroySpeed(this.state); if (speed > 1.0F && considerEnchants) { - final net.minecraft.core.Holder attribute = net.minecraft.world.entity.ai.attributes.Attributes.MINING_EFFICIENCY; + final Holder attribute = Attributes.MINING_EFFICIENCY; // Logic sourced from AttributeInstance#calculateValue final double initialBaseValue = attribute.value().getDefaultValue(); - final org.apache.commons.lang3.mutable.MutableDouble modifiedBaseValue = new org.apache.commons.lang3.mutable.MutableDouble(initialBaseValue); - final org.apache.commons.lang3.mutable.MutableDouble baseValMul = new org.apache.commons.lang3.mutable.MutableDouble(1); - final org.apache.commons.lang3.mutable.MutableDouble totalValMul = new org.apache.commons.lang3.mutable.MutableDouble(1); + final MutableDouble modifiedBaseValue = new MutableDouble(initialBaseValue); + final MutableDouble baseValMul = new MutableDouble(1); + final MutableDouble totalValMul = new MutableDouble(1); - net.minecraft.world.item.enchantment.EnchantmentHelper.forEachModifier( - nmsItemStack, net.minecraft.world.entity.EquipmentSlot.MAINHAND, (attributeHolder, attributeModifier) -> { + EnchantmentHelper.forEachModifier( + itemStack, EquipmentSlot.MAINHAND, (_, attributeModifier) -> { switch (attributeModifier.operation()) { case ADD_VALUE -> modifiedBaseValue.add(attributeModifier.amount()); case ADD_MULTIPLIED_BASE -> baseValMul.add(attributeModifier.amount()); - case ADD_MULTIPLIED_TOTAL -> totalValMul.setValue(totalValMul.doubleValue() * (1D + attributeModifier.amount())); + case ADD_MULTIPLIED_TOTAL -> totalValMul.setValue(totalValMul.doubleValue() * (1.0 + attributeModifier.amount())); } } ); @@ -784,14 +643,11 @@ public float getDestroySpeed(final ItemStack itemStack, final boolean considerEn } return speed; } - // Paper end - destroy speed API - // Paper start - Block tick API @Override public boolean isRandomlyTicked() { return this.state.isRandomlyTicking(); } - // Paper end - Block tick API @Override public boolean isReplaceable() { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftBossBar.java b/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftBossBar.java index 8430d131bbc1..3b2220d895a6 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftBossBar.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftBossBar.java @@ -10,6 +10,8 @@ import net.minecraft.network.protocol.game.ClientboundBossEventPacket; import net.minecraft.server.level.ServerBossEvent; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; import net.minecraft.world.BossEvent; import org.bukkit.boss.BarColor; import org.bukkit.boss.BarFlag; @@ -26,9 +28,10 @@ public class CraftBossBar implements BossBar { public CraftBossBar(String title, BarColor color, BarStyle style, BarFlag... flags) { this.handle = new ServerBossEvent( - CraftChatMessage.fromString(title, true)[0], - this.convertColor(color), - this.convertStyle(style) + Mth.createInsecureUUID(RandomSource.create()), + CraftChatMessage.fromString(title, true)[0], + this.convertColor(color), + this.convertStyle(style) ); this.initialize(); @@ -41,8 +44,8 @@ public CraftBossBar(String title, BarColor color, BarStyle style, BarFlag... fla this.setStyle(style); } - public CraftBossBar(ServerBossEvent bossBattleServer) { - this.handle = bossBattleServer; + public CraftBossBar(ServerBossEvent bossEvent) { + this.handle = bossEvent; this.initialize(); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftDragonBattle.java b/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftDragonBattle.java index 9d96237cecbc..543af9f698fd 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftDragonBattle.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftDragonBattle.java @@ -1,17 +1,19 @@ package org.bukkit.craftbukkit.boss; import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import io.papermc.paper.math.Position; +import io.papermc.paper.util.MCUtil; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.boss.enderdragon.EndCrystal; import net.minecraft.world.level.block.state.pattern.BlockPattern; -import net.minecraft.world.level.dimension.end.DragonRespawnAnimation; -import net.minecraft.world.level.dimension.end.EndDragonFight; +import net.minecraft.world.level.dimension.end.DragonRespawnStage; +import net.minecraft.world.level.dimension.end.EnderDragonFight; import org.bukkit.Location; -import org.bukkit.World; import org.bukkit.boss.BossBar; import org.bukkit.boss.DragonBattle; import org.bukkit.craftbukkit.CraftWorld; @@ -22,9 +24,9 @@ public class CraftDragonBattle implements DragonBattle { - private final EndDragonFight handle; + private final EnderDragonFight handle; - public CraftDragonBattle(EndDragonFight handle) { + public CraftDragonBattle(EnderDragonFight handle) { this.handle = handle; } @@ -41,16 +43,16 @@ public BossBar getBossBar() { @Override public Location getEndPortalLocation() { - if (this.handle.portalLocation == null) { + if (this.handle.exitPortalLocation == null) { return null; } - return CraftLocation.toBukkit(this.handle.portalLocation, this.handle.level); + return CraftLocation.toBukkit(this.handle.exitPortalLocation, this.handle.level); } @Override public boolean generateEndPortal(boolean withPortals) { - if (this.handle.portalLocation != null || this.handle.findExitPortal() != null) { + if (this.handle.exitPortalLocation != null || this.handle.findExitPortal() != null) { return false; } @@ -65,7 +67,7 @@ public boolean hasBeenPreviouslyKilled() { @Override public void setPreviouslyKilled(boolean previouslyKilled) { - this.handle.previouslyKilled = previouslyKilled; + this.handle.hasPreviouslyKilledDragon = previouslyKilled; } @Override @@ -74,27 +76,27 @@ public void initiateRespawn() { } @Override - public boolean initiateRespawn(Collection list) { + public boolean initiateRespawn(Collection crystals) { // todo doesn't seems to work without crystals if (this.hasBeenPreviouslyKilled() && this.getRespawnPhase() == RespawnPhase.NONE) { - // Copy from EndDragonFight#tryRespawn for generate exit portal if not exists - if (this.handle.portalLocation == null) { - BlockPattern.BlockPatternMatch patternMatch = this.handle.findExitPortal(); - if (patternMatch == null) { + // Copy from EnderDragonFight#tryRespawn for generate exit portal if not exists + if (this.handle.exitPortalLocation == null) { + BlockPattern.BlockPatternMatch match = this.handle.findExitPortal(); + if (match == null) { this.handle.spawnExitPortal(true); } } - list = (list != null) ? new ArrayList<>(list) : Collections.emptyList(); - list.removeIf(enderCrystal -> { - if (enderCrystal == null) { + List filteredCrystals = crystals != null ? new ArrayList<>(crystals) : new ArrayList<>(); + filteredCrystals.removeIf(crystal -> { + if (crystal == null) { return true; } - - World world = enderCrystal.getWorld(); - return !((CraftWorld) world).getHandle().equals(this.handle.level); + return !((CraftWorld) crystal.getWorld()).getHandle().equals(this.handle.level); }); - return this.handle.respawnDragon(list.stream().map(enderCrystal -> ((CraftEnderCrystal) enderCrystal).getHandle()).collect(Collectors.toList())); + return this.handle.respawnDragon( + Lists.transform(filteredCrystals, crystal -> ((CraftEnderCrystal) crystal).getHandle()) + ); } return false; } @@ -131,50 +133,51 @@ public boolean equals(Object obj) { return obj instanceof CraftDragonBattle && ((CraftDragonBattle) obj).handle == this.handle; } - private RespawnPhase toBukkitRespawnPhase(DragonRespawnAnimation phase) { + private RespawnPhase toBukkitRespawnPhase(DragonRespawnStage phase) { return (phase != null) ? RespawnPhase.values()[phase.ordinal()] : RespawnPhase.NONE; } - private DragonRespawnAnimation toNMSRespawnPhase(RespawnPhase phase) { - return (phase != RespawnPhase.NONE) ? DragonRespawnAnimation.values()[phase.ordinal()] : null; + private DragonRespawnStage toNMSRespawnPhase(RespawnPhase phase) { + return (phase != RespawnPhase.NONE) ? DragonRespawnStage.values()[phase.ordinal()] : null; } @Override public int getGatewayCount() { - return EndDragonFight.GATEWAY_COUNT - this.handle.gateways.size(); + return EnderDragonFight.GATEWAY_COUNT - this.handle.gateways.size(); } @Override public boolean spawnNewGateway() { - return this.handle.spawnNewGatewayIfPossible(); + return this.handle.spawnNewGateway(); } @Override - public void spawnNewGateway(final io.papermc.paper.math.Position position) { - this.handle.spawnNewGateway(io.papermc.paper.util.MCUtil.toBlockPos(position)); + public void spawnNewGateway(final Position position) { + this.handle.spawnNewGateway(MCUtil.toBlockPos(position)); } @Override - public List getRespawnCrystals() { - if (this.handle.respawnCrystals == null) { + public List getRespawnCrystals() { + if (this.handle.respawnCrystals.isEmpty()) { return Collections.emptyList(); } - final List enderCrystals = new ArrayList<>(); - for (final net.minecraft.world.entity.boss.enderdragon.EndCrystal endCrystal : this.handle.respawnCrystals) { - if (!endCrystal.isRemoved() && endCrystal.isAlive() && endCrystal.valid) { - enderCrystals.add(((EnderCrystal) endCrystal.getBukkitEntity())); + final List crystals = new ArrayList<>(); + for (final net.minecraft.world.entity.EntityReference ref : this.handle.respawnCrystals) { + final EndCrystal crystal = ref.getEntity(this.handle.level, EndCrystal.class); + if (crystal != null && !crystal.isRemoved() && crystal.isAlive() && crystal.valid) { + crystals.add(((EnderCrystal) crystal.getBukkitEntity())); } } - return Collections.unmodifiableList(enderCrystals); + return Collections.unmodifiableList(crystals); } @Override public List getHealingCrystals() { final List enderCrystals = new ArrayList<>(); - for (final net.minecraft.world.entity.boss.enderdragon.EndCrystal endCrystal : this.handle.getSpikeCrystals()) { - if (!endCrystal.isRemoved() && endCrystal.isAlive() && endCrystal.valid) { - enderCrystals.add(((EnderCrystal) endCrystal.getBukkitEntity())); + for (final EndCrystal crystal : this.handle.getSpikeCrystals()) { + if (!crystal.isRemoved() && crystal.isAlive() && crystal.valid) { + enderCrystals.add(((EnderCrystal) crystal.getBukkitEntity())); } } return Collections.unmodifiableList(enderCrystals); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftKeyedBossbar.java b/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftKeyedBossbar.java index e380793e3436..8ea17c380fd6 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftKeyedBossbar.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/boss/CraftKeyedBossbar.java @@ -13,7 +13,7 @@ public CraftKeyedBossbar(CustomBossEvent bossBattleCustom) { @Override public NamespacedKey getKey() { - return CraftNamespacedKey.fromMinecraft(this.getHandle().getTextId()); + return CraftNamespacedKey.fromMinecraft(this.getHandle().customId()); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/command/CraftBlockCommandSender.java b/paper-server/src/main/java/org/bukkit/craftbukkit/command/CraftBlockCommandSender.java index 872acd855e45..46691547832c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/command/CraftBlockCommandSender.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/command/CraftBlockCommandSender.java @@ -28,12 +28,12 @@ public void setOp(boolean value) { throw new UnsupportedOperationException("Cannot change operator status of a block"); } }); - private final CommandSourceStack block; + private final CommandSourceStack sourceStack; private final BlockEntity blockEntity; - public CraftBlockCommandSender(CommandSourceStack commandBlockListenerAbstract, BlockEntity blockEntity) { + public CraftBlockCommandSender(CommandSourceStack sourceStack, BlockEntity blockEntity) { super(CraftBlockCommandSender.SHARED_PERM); - this.block = commandBlockListenerAbstract; + this.sourceStack = sourceStack; this.blockEntity = blockEntity; } @@ -45,7 +45,7 @@ public Block getBlock() { @Override public void sendMessage(String message) { for (Component component : CraftChatMessage.fromString(message)) { - this.block.source.sendSystemMessage(component); + this.sourceStack.source.sendSystemMessage(component); } } @@ -58,17 +58,17 @@ public void sendMessage(String... messages) { @Override public String getName() { - return this.block.getTextName(); + return this.sourceStack.getTextName(); } @Override public void sendMessage(net.kyori.adventure.identity.Identity identity, net.kyori.adventure.text.Component message, net.kyori.adventure.audience.MessageType type) { - this.block.source.sendSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message)); + this.sourceStack.source.sendSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message)); } @Override public net.kyori.adventure.text.Component name() { - return io.papermc.paper.adventure.PaperAdventure.asAdventure(this.block.getDisplayName()); + return io.papermc.paper.adventure.PaperAdventure.asAdventure(this.sourceStack.getDisplayName()); } @Override @@ -81,7 +81,7 @@ public void setOp(boolean value) { CraftBlockCommandSender.SHARED_PERM.setOp(value); } - public CommandSourceStack getWrapper() { - return this.block; + public CommandSourceStack getSourceStack() { + return this.sourceStack; } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/paper-server/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java index 1eb5ad45103a..b7914a60edb1 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java @@ -87,7 +87,7 @@ public static CommandSourceStack getListener(CommandSender sender) { return entity.getHandle().createCommandSourceStackForNameResolution((ServerLevel) entity.getHandle().level()); } if (sender instanceof BlockCommandSender) { - return ((CraftBlockCommandSender) sender).getWrapper(); + return ((CraftBlockCommandSender) sender).getSourceStack(); } if (sender instanceof RemoteConsoleCommandSender) { return ((CraftRemoteConsoleCommandSender) sender).getListener().createCommandSourceStack(); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/configuration/ConfigSerializationUtil.java b/paper-server/src/main/java/org/bukkit/craftbukkit/configuration/ConfigSerializationUtil.java index 38b07b508205..ffbcf189b870 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/configuration/ConfigSerializationUtil.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/configuration/ConfigSerializationUtil.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.configuration; +import com.google.common.collect.ImmutableMap; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -50,6 +51,12 @@ public static void setHolderSet(Map result, String key, HolderSe .ifRight(list -> result.put(key, list.stream().map((entry) -> entry.unwrapKey().orElseThrow().identifier().toString()).toList())); // List } + public static void setHolderSet(ImmutableMap.Builder result, String key, HolderSet holders) { + holders.unwrap() + .ifLeft(tag -> result.put(key, "#" + tag.location().toString())) // Tag + .ifRight(list -> result.put(key, list.stream().map((entry) -> entry.unwrapKey().orElseThrow().identifier().toString()).toList())); // List + } + public static HolderSet getHolderSet(Object from, ResourceKey> registryKey) { Registry registry = CraftRegistry.getMinecraftRegistry(registryKey); if (from instanceof String parseString && parseString.startsWith("#")) { // Tag diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java index 46902cbbaa9e..72863de5d5a1 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java @@ -63,7 +63,14 @@ public void hitEntity(org.bukkit.entity.Entity entity, org.bukkit.util.Vector ve @Override public final org.bukkit.projectiles.ProjectileSource getShooter() { - this.getHandle().refreshProjectileSource(true); // Paper - Refresh ProjectileSource for projectiles + net.minecraft.world.entity.projectile.Projectile handle = this.getHandle(); + + // Refresh the source if unset + Entity owner = handle.getOwner(); + if (owner != null && handle.projectileSource == null && owner.getBukkitEntity() instanceof org.bukkit.projectiles.ProjectileSource projSource) { + handle.projectileSource = projSource; + } + return this.getHandle().projectileSource; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAgeable.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAgeable.java index 809639b79db4..32c9fa700a58 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAgeable.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAgeable.java @@ -1,6 +1,7 @@ package org.bukkit.craftbukkit.entity; import net.minecraft.world.entity.AgeableMob; +import net.minecraft.world.entity.Mob; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.entity.Ageable; @@ -26,34 +27,35 @@ public void setAge(int age) { @Override public void setAgeLock(boolean lock) { - this.getHandle().ageLocked = lock; + this.getHandle().setAgeLocked(lock); } @Override public boolean getAgeLock() { - return this.getHandle().ageLocked; + return this.getHandle().isAgeLocked(); } @Override public void setBaby() { - if (this.isAdult()) { - this.setAge(AgeableMob.BABY_START_AGE); - } + setBaby(this.getHandle(), true); } @Override public void setAdult() { - if (!this.isAdult()) { - this.setAge(0); + setBaby(this.getHandle(), false); + } + + public static void setBaby(Mob mob, boolean baby) { + if (baby != mob.isBaby()) { + mob.setBaby(baby); } } @Override public boolean isAdult() { - return this.getAge() >= 0; + return !this.getHandle().isBaby(); } - @Override public boolean canBreed() { return this.getAge() == 0; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAllay.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAllay.java index 3c1f25507b50..dc87bacd4c6a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAllay.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAllay.java @@ -58,9 +58,9 @@ public boolean isDancing() { @Override public void startDancing(Location location) { - Preconditions.checkArgument(location != null, "Location cannot be null"); - Preconditions.checkArgument(location.getBlock().getType().equals(Material.JUKEBOX), "The Block in the Location need to be a JukeBox"); - this.getHandle().setJukeboxPlaying(CraftLocation.toBlockPosition(location), true); + Preconditions.checkArgument(location != null, "location cannot be null"); + Preconditions.checkArgument(location.getBlock().getType() == Material.JUKEBOX, "The Block at the location needs to be a jukebox"); + this.getHandle().setJukeboxPlaying(CraftLocation.toBlockPos(location), true); } @Override @@ -73,17 +73,17 @@ public void startDancing() { public void stopDancing() { this.getHandle().forceDancing = false; this.getHandle().jukeboxPos = null; - this.getHandle().setDancing(false); // Paper - Directly modify set dancing to avoid NPE + this.getHandle().setDancing(false); } @Override public org.bukkit.entity.Allay duplicateAllay() { - Allay nmsAllay = this.getHandle().duplicateAllay(); - return (nmsAllay != null) ? (org.bukkit.entity.Allay) nmsAllay.getBukkitEntity() : null; + Allay allay = this.getHandle().duplicateAllay(); + return (allay != null) ? (org.bukkit.entity.Allay) allay.getBukkitEntity() : null; } public Location getJukebox() { - BlockPos nmsJukeboxPos = this.getHandle().jukeboxPos; - return (nmsJukeboxPos != null) ? CraftLocation.toBukkit(nmsJukeboxPos, this.getWorld()) : null; + BlockPos jukeboxPos = this.getHandle().jukeboxPos; + return (jukeboxPos != null) ? CraftLocation.toBukkit(jukeboxPos, this.getWorld()) : null; } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java index 7b29d31272ac..c600f4798761 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java @@ -38,7 +38,7 @@ public org.bukkit.Location getTargetLocation() { public void setTargetLocation(org.bukkit.Location location) { net.minecraft.core.BlockPos pos = null; if (location != null) { - pos = CraftLocation.toBlockPosition(location); + pos = CraftLocation.toBlockPos(location); } this.getHandle().targetPosition = pos; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBee.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBee.java index 30e805017922..27155539258c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBee.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBee.java @@ -27,7 +27,7 @@ public Location getHive() { @Override public void setHive(Location location) { Preconditions.checkArgument(location == null || this.getWorld().equals(location.getWorld()), "Hive must be in same world"); - this.getHandle().hivePos = (location == null) ? null : CraftLocation.toBlockPosition(location); + this.getHandle().hivePos = (location == null) ? null : CraftLocation.toBlockPos(location); } @Override @@ -39,7 +39,7 @@ public Location getFlower() { @Override public void setFlower(Location location) { Preconditions.checkArgument(location == null || this.getWorld().equals(location.getWorld()), "Flower must be in same world"); - this.getHandle().setSavedFlowerPos(location == null ? null : CraftLocation.toBlockPosition(location)); + this.getHandle().setSavedFlowerPos(location == null ? null : CraftLocation.toBlockPos(location)); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBlockDisplay.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBlockDisplay.java index 6502c0622e6e..173a6cce4593 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBlockDisplay.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBlockDisplay.java @@ -19,7 +19,7 @@ public net.minecraft.world.entity.Display.BlockDisplay getHandle() { @Override public BlockData getBlock() { - return CraftBlockData.fromData(this.getHandle().getBlockState()); + return this.getHandle().getBlockState().asBlockData(); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBoat.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBoat.java index 02a4a8641050..e4a0df348522 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBoat.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBoat.java @@ -44,7 +44,7 @@ public double getMaxSpeed() { @Override public void setMaxSpeed(double speed) { - if (speed >= 0D) { + if (speed >= 0) { this.getHandle().maxSpeed = speed; } } @@ -56,7 +56,7 @@ public double getOccupiedDeceleration() { @Override public void setOccupiedDeceleration(double speed) { - if (speed >= 0D) { + if (speed >= 0) { this.getHandle().occupiedDeceleration = speed; } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBreeze.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBreeze.java index f71864abb9ef..3daecbbcc55d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBreeze.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftBreeze.java @@ -1,9 +1,7 @@ package org.bukkit.craftbukkit.entity; -import net.minecraft.world.entity.ai.memory.MemoryModuleType; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.entity.Breeze; -import org.bukkit.entity.LivingEntity; public class CraftBreeze extends CraftMonster implements Breeze { @@ -16,10 +14,12 @@ public net.minecraft.world.entity.monster.breeze.Breeze getHandle() { return (net.minecraft.world.entity.monster.breeze.Breeze) this.entity; } + /* // TODO - snapshot - reimplement? but without reintroducing MC-199589 @Override public void setTarget(LivingEntity target) { super.setTarget(target); - net.minecraft.world.entity.LivingEntity entityLivingTarget = (target instanceof CraftLivingEntity craftLivingEntity) ? craftLivingEntity.getHandle() : null; - this.getHandle().getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, entityLivingTarget); // SPIGOT-7957: We need override memory for set target and trigger attack behaviours + net.minecraft.world.entity.LivingEntity attackTarget = (target instanceof CraftLivingEntity craftLivingEntity) ? craftLivingEntity.getHandle() : null; + this.getHandle().getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, attackTarget); // SPIGOT-7957: We need override memory for set target and trigger attack behaviours } + */ } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCamelHusk.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCamelHusk.java index e0008d4708bc..930d1110e27d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCamelHusk.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCamelHusk.java @@ -1,7 +1,6 @@ package org.bukkit.craftbukkit.entity; import net.minecraft.world.entity.animal.camel.CamelHusk; -import net.minecraft.world.entity.animal.nautilus.ZombieNautilus; import org.bukkit.craftbukkit.CraftServer; public class CraftCamelHusk extends CraftCamel implements org.bukkit.entity.CamelHusk { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java index 0ff5f7417259..8eb1d46ec221 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java @@ -1,9 +1,11 @@ package org.bukkit.craftbukkit.entity; import com.google.common.base.Preconditions; +import io.papermc.paper.registry.HolderableBase; import io.papermc.paper.util.OldEnumHolderable; import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.animal.feline.CatSoundVariant; import net.minecraft.world.entity.animal.feline.CatVariant; import org.bukkit.DyeColor; import org.bukkit.craftbukkit.CraftRegistry; @@ -33,6 +35,18 @@ public void setCatType(Type type) { this.getHandle().setVariant(CraftType.bukkitToMinecraftHolder(type)); } + @Override + public SoundVariant getSoundVariant() { + return CraftSoundVariant.minecraftHolderToBukkit(this.getHandle().getSoundVariant()); + } + + @Override + public void setSoundVariant(final SoundVariant variant) { + Preconditions.checkArgument(variant != null, "variant cannot be null"); + + this.getHandle().setSoundVariant(CraftSoundVariant.bukkitToMinecraftHolder(variant)); + } + @Override public DyeColor getCollarColor() { return DyeColor.getByWoolData((byte) this.getHandle().getCollarColor().getId()); @@ -59,6 +73,21 @@ public CraftType(final Holder holder) { } } + public static class CraftSoundVariant extends HolderableBase implements SoundVariant { + + public static SoundVariant minecraftHolderToBukkit(Holder minecraft) { + return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.CAT_SOUND_VARIANT); + } + + public static Holder bukkitToMinecraftHolder(SoundVariant bukkit) { + return CraftRegistry.bukkitToMinecraftHolder(bukkit); + } + + public CraftSoundVariant(final Holder holder) { + super(holder); + } + } + @Override public void setLyingDown(boolean lyingDown) { this.getHandle().setLying(lyingDown); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java index 1e015dc87c62..855ddda60c1a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java @@ -4,6 +4,7 @@ import io.papermc.paper.registry.HolderableBase; import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.animal.chicken.ChickenSoundVariant; import net.minecraft.world.entity.animal.chicken.ChickenVariant; import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.CraftServer; @@ -34,6 +35,18 @@ public void setVariant(Variant variant) { this.getHandle().setVariant(CraftVariant.bukkitToMinecraftHolder(variant)); } + @Override + public SoundVariant getSoundVariant() { + return CraftSoundVariant.minecraftHolderToBukkit(this.getHandle().getSoundVariant()); + } + + @Override + public void setSoundVariant(final SoundVariant variant) { + Preconditions.checkArgument(variant != null, "variant cannot be null"); + + this.getHandle().setSoundVariant(CraftSoundVariant.bukkitToMinecraftHolder(variant)); + } + public static class CraftVariant extends HolderableBase implements Variant { public static Variant minecraftHolderToBukkit(Holder minecraft) { @@ -44,12 +57,26 @@ public static Holder bukkitToMinecraftHolder(Variant bukkit) { return CraftRegistry.bukkitToMinecraftHolder(bukkit); } - public CraftVariant(Holder holder) { super(holder); } } + public static class CraftSoundVariant extends HolderableBase implements SoundVariant { + + public static SoundVariant minecraftHolderToBukkit(Holder minecraft) { + return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.CHICKEN_SOUND_VARIANT); + } + + public static Holder bukkitToMinecraftHolder(SoundVariant bukkit) { + return CraftRegistry.bukkitToMinecraftHolder(bukkit); + } + + public CraftSoundVariant(final Holder holder) { + super(holder); + } + } + @Override public boolean isChickenJockey() { return this.getHandle().isChickenJockey(); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCow.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCow.java index e0adc2c33c26..8403fb7872e3 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCow.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftCow.java @@ -4,6 +4,7 @@ import io.papermc.paper.registry.HolderableBase; import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.animal.cow.CowSoundVariant; import net.minecraft.world.entity.animal.cow.CowVariant; import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.CraftServer; @@ -34,6 +35,18 @@ public void setVariant(Variant variant) { this.getHandle().setVariant(CraftVariant.bukkitToMinecraftHolder(variant)); } + @Override + public SoundVariant getSoundVariant() { + return CraftSoundVariant.minecraftHolderToBukkit(this.getHandle().getSoundVariant()); + } + + @Override + public void setSoundVariant(SoundVariant variant) { + Preconditions.checkArgument(variant != null, "variant cannot be null"); + + this.getHandle().setSoundVariant(CraftSoundVariant.bukkitToMinecraftHolder(variant)); + } + public static class CraftVariant extends HolderableBase implements Variant { public static Variant minecraftHolderToBukkit(Holder minecraft) { @@ -48,4 +61,19 @@ public CraftVariant(final Holder holder) { super(holder); } } + + public static class CraftSoundVariant extends HolderableBase implements SoundVariant { + + public static SoundVariant minecraftHolderToBukkit(Holder minecraft) { + return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.COW_SOUND_VARIANT); + } + + public static Holder bukkitToMinecraftHolder(SoundVariant bukkit) { + return CraftRegistry.bukkitToMinecraftHolder(bukkit); + } + + public CraftSoundVariant(final Holder holder) { + super(holder); + } + } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftDisplay.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftDisplay.java index 831d710bef17..69e5088b13b7 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftDisplay.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftDisplay.java @@ -21,9 +21,8 @@ public net.minecraft.world.entity.Display getHandle() { @Override public Transformation getTransformation() { - com.mojang.math.Transformation nms = net.minecraft.world.entity.Display.createTransformation(this.getHandle().getEntityData()); - - return new Transformation(new Vector3f(nms.getTranslation()), new Quaternionf(nms.getLeftRotation()), new Vector3f(nms.getScale()), new Quaternionf(nms.getRightRotation())); + com.mojang.math.Transformation transformation = net.minecraft.world.entity.Display.createTransformation(this.getHandle().getEntityData()); + return new Transformation(new Vector3f(transformation.translation()), new Quaternionf(transformation.leftRotation()), new Vector3f(transformation.scale()), new Quaternionf(transformation.rightRotation())); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java index 880127650213..d844cdae338a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java @@ -43,6 +43,6 @@ public org.bukkit.Location getTreasureLocation() { @Override public void setTreasureLocation(org.bukkit.Location location) { - this.getHandle().treasurePos = location == null ? null : CraftLocation.toBlockPosition(location); + this.getHandle().treasurePos = location == null ? null : CraftLocation.toBlockPos(location); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderCrystal.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderCrystal.java index 2229a03fda7e..ee3f6c78ef8d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderCrystal.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderCrystal.java @@ -41,7 +41,7 @@ public void setBeamTarget(Location location) { } else if (location.getWorld() != this.getWorld()) { throw new IllegalArgumentException("Cannot set beam target location to different world"); } else { - this.getHandle().setBeamTarget(CraftLocation.toBlockPosition(location)); + this.getHandle().setBeamTarget(CraftLocation.toBlockPos(location)); } } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java index a1854acb8a15..f5caff5c2644 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java @@ -82,7 +82,7 @@ public void setPodium(org.bukkit.Location location) { this.getHandle().setPodium(null); } else { Preconditions.checkArgument(location.getWorld() == null || location.getWorld().equals(getWorld()), "You cannot set a podium in a different world to where the dragon is"); - this.getHandle().setPodium(CraftLocation.toBlockPosition(location)); + this.getHandle().setPodium(CraftLocation.toBlockPos(location)); } } // Paper end - Allow changing the EnderDragon podium diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java index 52a5240b7a69..3cfcd01107b3 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java @@ -30,14 +30,14 @@ public boolean teleportRandomly() { @Override public MaterialData getCarriedMaterial() { - BlockState blockData = this.getHandle().getCarriedBlock(); - return (blockData == null) ? Material.AIR.getNewData((byte) 0) : CraftMagicNumbers.getMaterial(blockData); + BlockState carried = this.getHandle().getCarriedBlock(); + return (carried == null) ? Material.AIR.getNewData((byte) 0) : CraftMagicNumbers.getMaterial(carried); } @Override public BlockData getCarriedBlock() { - BlockState blockData = this.getHandle().getCarriedBlock(); - return (blockData == null) ? null : CraftBlockData.fromData(blockData); + BlockState carried = this.getHandle().getCarriedBlock(); + return (carried == null) ? null : carried.asBlockData(); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 2a572ef0ba74..2f40476da09c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -909,7 +909,7 @@ public void setPortalCooldown(int cooldown) { @Override public Set getScoreboardTags() { - return this.getHandle().getTags(); + return this.getHandle().entityTags(); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityType.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityType.java index d842c2c21570..bf59ea6025ab 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityType.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityType.java @@ -33,6 +33,7 @@ public static net.minecraft.world.entity.EntityType bukkitToMinecraft(EntityT .getOptional(KEY_CACHE.computeIfAbsent(bukkit, type -> net.minecraft.resources.ResourceKey.create(Registries.ENTITY_TYPE, CraftNamespacedKey.toMinecraft(type.getKey())))).orElseThrow(); } + @Deprecated // for now until this is a proper registry type public static Holder> bukkitToMinecraftHolder(EntityType bukkit) { Preconditions.checkArgument(bukkit != null); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java index db1a81d06613..148de096f809 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java @@ -16,9 +16,9 @@ import net.minecraft.world.entity.item.FallingBlockEntity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.item.PrimedTnt; -import net.minecraft.world.entity.projectile.hurtingprojectile.AbstractHurtingProjectile; import net.minecraft.world.entity.projectile.EyeOfEnder; import net.minecraft.world.entity.projectile.FireworkRocketEntity; +import net.minecraft.world.entity.projectile.hurtingprojectile.AbstractHurtingProjectile; import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownEgg; import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownLingeringPotion; import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownSplashPotion; @@ -383,21 +383,18 @@ Level minecraftWorld() { // Hanging register(new EntityTypeData<>(EntityType.PAINTING, Painting.class, CraftPainting::new, createHanging(Painting.class, (spawnData, hangingData) -> { - if (spawnData.normalWorld && hangingData.randomize()) { - // Paper start - if randomizeData fails, force it - final net.minecraft.world.entity.decoration.painting.Painting entity = net.minecraft.world.entity.decoration.painting.Painting.create(spawnData.minecraftWorld(), hangingData.position(), hangingData.direction()).orElse(null); - if (entity != null) { - return entity; - } - } /*else*/ { - // Paper end - if randomizeData fails, force it - net.minecraft.world.entity.decoration.painting.Painting entity = new net.minecraft.world.entity.decoration.painting.Painting(net.minecraft.world.entity.EntityType.PAINTING, spawnData.minecraftWorld()); - entity.absSnapTo(spawnData.x(), spawnData.y(), spawnData.z(), spawnData.yaw(), spawnData.pitch()); - entity.setDirection(hangingData.direction()); - return entity; - } + if (spawnData.normalWorld && hangingData.randomize()) { + final net.minecraft.world.entity.decoration.painting.Painting entity = net.minecraft.world.entity.decoration.painting.Painting.create(spawnData.minecraftWorld(), hangingData.position(), hangingData.direction()).orElse(null); + if (entity != null) { + return entity; } - ))); + } + + net.minecraft.world.entity.decoration.painting.Painting entity = new net.minecraft.world.entity.decoration.painting.Painting(net.minecraft.world.entity.EntityType.PAINTING, spawnData.minecraftWorld()); + entity.absSnapTo(spawnData.x(), spawnData.y(), spawnData.z(), spawnData.yaw(), spawnData.pitch()); + entity.setDirection(hangingData.direction()); + return entity; + }))); register(new EntityTypeData<>(EntityType.ITEM_FRAME, ItemFrame.class, CraftItemFrame::new, createHanging(ItemFrame.class, (spawnData, hangingData) -> new net.minecraft.world.entity.decoration.ItemFrame(spawnData.minecraftWorld(), hangingData.position(), hangingData.direction())))); register(new EntityTypeData<>(EntityType.GLOW_ITEM_FRAME, GlowItemFrame.class, CraftGlowItemFrame::new, createHanging(GlowItemFrame.class, (spawnData, hangingData) -> new net.minecraft.world.entity.decoration.GlowItemFrame(spawnData.minecraftWorld(), hangingData.position(), hangingData.direction())))); @@ -449,7 +446,7 @@ Level minecraftWorld() { // and that the item stack should probably be changed. net.minecraft.world.item.ItemStack itemStack = new net.minecraft.world.item.ItemStack(Items.STONE); ItemEntity item = new ItemEntity(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), itemStack); - item.setPickUpDelay(10); + item.setDefaultPickUpDelay(); CLEAR_MOVE_IF_NOT_RANDOMIZED.accept(spawnData, item); // Paper - respect randomizeData return item; @@ -524,7 +521,7 @@ private static Function createMinecar if (spawnData.normalWorld()) { return AbstractMinecart.createMinecart(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), entityTypes, EntitySpawnReason.TRIGGERED, ItemStack.EMPTY, null); } else { - return CraftEntityTypes.combine(CraftEntityTypes.fromEntityType(entityTypes), (spawnData2, entity) -> entity.setInitialPos(spawnData.x(), spawnData.y(), spawnData.z())).apply(spawnData); + return CraftEntityTypes.combine(CraftEntityTypes.fromEntityType(entityTypes), (_, entity) -> entity.setInitialPos(spawnData.x(), spawnData.y(), spawnData.z())).apply(spawnData); } }; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java index eacdc2467fae..ded2f19ab8bd 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java @@ -26,7 +26,7 @@ public Material getMaterial() { @Override public BlockData getBlockData() { - return CraftBlockData.fromData(this.getHandle().getBlockState()); + return this.getHandle().getBlockState().asBlockData(); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGuardian.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGuardian.java index 2f68d8708150..ca5974335e19 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGuardian.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGuardian.java @@ -21,6 +21,7 @@ public net.minecraft.world.entity.monster.Guardian getHandle() { @Override public void setTarget(LivingEntity target) { super.setTarget(target); + target = super.getTarget(); // target might fail so update the reference // clean up laser target, when target is removed if (target == null) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java index 5a4e98531031..293754eeb3ed 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java @@ -10,6 +10,7 @@ import io.papermc.paper.adventure.PaperAdventure; import net.kyori.adventure.key.Key; import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ClientboundMountScreenOpenPacket; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; @@ -55,6 +56,7 @@ import org.bukkit.craftbukkit.inventory.CraftRecipe; import org.bukkit.craftbukkit.inventory.util.CraftMenus; import org.bukkit.craftbukkit.util.CraftLocation; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.entity.Firework; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Item; @@ -193,7 +195,7 @@ public boolean sleep(Location location, boolean force) { Preconditions.checkArgument(location.getWorld() != null, "Location needs to be in a world"); Preconditions.checkArgument(location.getWorld().equals(this.getWorld()), "Cannot sleep across worlds"); - BlockPos pos = CraftLocation.toBlockPosition(location); + BlockPos pos = CraftLocation.toBlockPos(location); BlockState state = this.getHandle().level().getBlockState(pos); if (!(state.getBlock() instanceof BedBlock)) { return false; @@ -398,7 +400,7 @@ public InventoryView openWorkbench(Location location, boolean force) { return null; } } - this.getHandle().openMenu(Blocks.CRAFTING_TABLE.defaultBlockState().getMenuProvider(this.getHandle().level(), CraftLocation.toBlockPosition(location))); + this.getHandle().openMenu(Blocks.CRAFTING_TABLE.defaultBlockState().getMenuProvider(this.getHandle().level(), CraftLocation.toBlockPos(location))); if (force) { this.getHandle().containerMenu.checkReachable = false; } @@ -418,7 +420,7 @@ public InventoryView openEnchanting(Location location, boolean force) { } // If there isn't an enchant table we can force create one, won't be very useful though. - BlockPos pos = CraftLocation.toBlockPosition(location); + BlockPos pos = CraftLocation.toBlockPos(location); // Paper start MenuProvider menuProvider = Blocks.ENCHANTING_TABLE.defaultBlockState().getMenuProvider(this.getHandle().level(), pos); if (menuProvider == null) { @@ -726,7 +728,7 @@ private Collection> bukkitKeysToMinecraftRecipes(Collection> recipe = manager.byKey(CraftRecipe.toMinecraft(recipeKey)); + Optional> recipe = manager.byKey(CraftNamespacedKey.toResourceKey(Registries.RECIPE, recipeKey)); if (recipe.isEmpty()) { continue; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java index d849e22060d0..7c017df81f1a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java @@ -41,14 +41,13 @@ public int getPickupDelay() { @Override public void setPickupDelay(int delay) { - this.getHandle().pickupDelay = Math.min(delay, Short.MAX_VALUE); + this.getHandle().pickupDelay = Math.min(delay, NO_PICKUP_TIME); } @Override public void setUnlimitedLifetime(boolean unlimited) { if (unlimited) { - // See EntityItem#INFINITE_LIFETIME - this.getHandle().age = Short.MIN_VALUE; + this.getHandle().age = NO_AGE_TIME; } else { this.getHandle().age = this.getTicksLived(); } @@ -56,14 +55,14 @@ public void setUnlimitedLifetime(boolean unlimited) { @Override public boolean isUnlimitedLifetime() { - return this.getHandle().age == Short.MIN_VALUE; + return this.getHandle().age == NO_AGE_TIME; } @Override public void setTicksLived(int value) { super.setTicksLived(value); - // Second field for EntityItem (don't set if lifetime is unlimited) + // Second field for ItemEntity (don't set if lifetime is unlimited) if (!this.isUnlimitedLifetime()) { this.getHandle().age = value; } @@ -99,14 +98,13 @@ public void setWillAge(boolean willAge) { this.getHandle().age = willAge ? 0 : NO_AGE_TIME; } - @org.jetbrains.annotations.NotNull @Override public net.kyori.adventure.util.TriState getFrictionState() { return this.getHandle().frictionState; } @Override - public void setFrictionState(@org.jetbrains.annotations.NotNull net.kyori.adventure.util.TriState state) { + public void setFrictionState(net.kyori.adventure.util.TriState state) { Preconditions.checkArgument(state != null, "state may not be null"); this.getHandle().frictionState = state; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java index b0987314d263..ec1b31e78b55 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java @@ -21,6 +21,7 @@ import net.minecraft.server.waypoints.ServerWaypointManager; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; +import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.effect.MobEffectInstance; @@ -631,21 +632,21 @@ public T launchProjectile(Class projectile, launch = new FireworkRocketEntity(world, FireworkRocketEntity.getDefaultItem(), this.getHandle(), location.getX(), location.getY() - 0.15F, location.getZ(), true); // Paper - pass correct default to rocket for data storage & see CrossbowItem for regular launch without elytra boost // Lifted from net.minecraft.world.item.ProjectileWeaponItem.shoot - float f2 = /* net.minecraft.world.item.enchantment.EnchantmentHelper.processProjectileSpread((ServerLevel) world, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.CROSSBOW), this.getHandle(), 0.0F); */ 0; // Just shortcut this to 0, no need to do any calculations on a non existing stack int projectileSize = 1; int i = 0; - float f3 = projectileSize == 1 ? 0.0F : 2.0F * f2 / (float) (projectileSize - 1); - float f4 = (float) ((projectileSize - 1) % 2) * f3 / 2.0F; - float f5 = 1.0F; - float yaw = f4 + f5 * (float) ((i + 1) / 2) * f3; + float maxAngle = /* net.minecraft.world.item.enchantment.EnchantmentHelper.processProjectileSpread((ServerLevel) world, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.CROSSBOW), this.getHandle(), 0.0F); */ 0; // Just shortcut this to 0, no need to do any calculations on a non existing stack + float angleStep = projectileSize == 1 ? 0.0F : 2.0F * maxAngle / (float) (projectileSize - 1); + float angleOffset = (float) ((projectileSize - 1) % 2) * angleStep / 2.0F; + float direction = 1.0F; + float angle = angleOffset + direction * ((i + 1) / 2) * angleStep; // Lifted from net.minecraft.world.item.CrossbowItem.shootProjectile - Vec3 vec3 = this.getHandle().getUpVector(1.0F); - org.joml.Quaternionf quaternionf = new org.joml.Quaternionf().setAngleAxis((double)(yaw * (float) (Math.PI / 180.0)), vec3.x, vec3.y, vec3.z); - Vec3 vec32 = this.getHandle().getViewVector(1.0F); - org.joml.Vector3f vector3f = vec32.toVector3f().rotate(quaternionf); - ((FireworkRocketEntity) launch).shoot((double)vector3f.x(), (double)vector3f.y(), (double)vector3f.z(), net.minecraft.world.item.CrossbowItem.FIREWORK_POWER, 1.0F); + Vec3 upVector = this.getHandle().getUpVector(1.0F); + org.joml.Quaternionf upQuaternion = new org.joml.Quaternionf().setAngleAxis((double)(angle * (float) (Math.PI / 180.0)), upVector.x, upVector.y, upVector.z); + Vec3 viewVec = this.getHandle().getViewVector(1.0F); + org.joml.Vector3f shotVector = viewVec.toVector3f().rotate(upQuaternion); + ((FireworkRocketEntity) launch).shoot(shotVector.x(), shotVector.y(), shotVector.z(), net.minecraft.world.item.CrossbowItem.FIREWORK_POWER, 1.0F); // Paper end } @@ -677,7 +678,7 @@ public boolean hasLineOfSight(Location loc) { net.minecraft.world.phys.Vec3 start = new net.minecraft.world.phys.Vec3(this.getHandle().getX(), this.getHandle().getEyeY(), this.getHandle().getZ()); net.minecraft.world.phys.Vec3 end = new net.minecraft.world.phys.Vec3(loc.getX(), loc.getY(), loc.getZ()); - if (end.distanceToSqr(start) > 128D * 128D) { + if (end.distanceToSqr(start) > Mth.square(128.0)) { return false; // Return early if the distance is greater than 128 blocks } @@ -686,13 +687,13 @@ public boolean hasLineOfSight(Location loc) { @Override public boolean getRemoveWhenFarAway() { - return this.getHandle() instanceof Mob && !((Mob) this.getHandle()).isPersistenceRequired(); + return this.getHandle() instanceof Mob mob && !mob.isPersistenceRequired(); } @Override public void setRemoveWhenFarAway(boolean remove) { - if (this.getHandle() instanceof Mob) { - ((Mob) this.getHandle()).setPersistenceRequired(!remove); + if (this.getHandle() instanceof Mob mob) { + mob.persistenceRequired = !remove; } } @@ -703,8 +704,8 @@ public void setRemoveWhenFarAway(boolean remove) { @Override public void setCanPickupItems(boolean pickup) { - if (this.getHandle() instanceof Mob) { - ((Mob) this.getHandle()).setCanPickUpLoot(pickup); + if (this.getHandle() instanceof Mob mob) { + mob.setCanPickUpLoot(pickup); } else { this.getHandle().bukkitPickUpLoot = pickup; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecart.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecart.java index 78149d1adc3a..adf72e8c981b 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecart.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecart.java @@ -42,7 +42,7 @@ public double getMaxSpeed() { @Override public void setMaxSpeed(double speed) { - if (speed >= 0D) { + if (speed >= 0) { this.getHandle().maxSpeed = speed; } } @@ -102,8 +102,7 @@ public MaterialData getDisplayBlock() { @Override public BlockData getDisplayBlockData() { - BlockState state = this.getHandle().getDisplayBlockState(); - return CraftBlockData.fromData(state); + return this.getHandle().getDisplayBlockState().asBlockData(); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java index 959fea246014..d30f9b4a8896 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java @@ -3,7 +3,9 @@ import com.google.common.base.Preconditions; import java.util.Optional; import net.kyori.adventure.util.TriState; +import net.minecraft.Optionull; import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.entity.Entity; import org.bukkit.Sound; import org.bukkit.craftbukkit.CraftLootTable; import org.bukkit.craftbukkit.CraftServer; @@ -66,10 +68,8 @@ public void setTarget(LivingEntity target) { } @Override - public CraftLivingEntity getTarget() { - if (this.getHandle().getTarget() == null) return null; - - return (CraftLivingEntity) this.getHandle().getTarget().getBukkitEntity(); + public LivingEntity getTarget() { + return (LivingEntity) Optionull.map(this.getHandle().getTarget(), Entity::getBukkitEntity); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java index cabdcbef0e6f..db0e625d8ba7 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java @@ -50,6 +50,6 @@ public Location getAnchorLocation() { @Override public void setAnchorLocation(Location location) { - this.getHandle().anchorPoint = location == null ? null : CraftLocation.toBlockPosition(location); + this.getHandle().anchorPoint = location == null ? null : CraftLocation.toBlockPos(location); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPig.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPig.java index b127a2b4b36d..b82683cf8867 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPig.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPig.java @@ -5,6 +5,7 @@ import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.animal.pig.PigSoundVariant; import net.minecraft.world.entity.animal.pig.PigVariant; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -82,6 +83,18 @@ public void setVariant(Variant variant) { this.getHandle().setVariant(CraftVariant.bukkitToMinecraftHolder(variant)); } + @Override + public SoundVariant getSoundVariant() { + return CraftSoundVariant.minecraftHolderToBukkit(this.getHandle().getSoundVariant()); + } + + @Override + public void setSoundVariant(SoundVariant variant) { + Preconditions.checkArgument(variant != null, "variant cannot be null"); + + this.getHandle().setSoundVariant(CraftSoundVariant.bukkitToMinecraftHolder(variant)); + } + public static class CraftVariant extends HolderableBase implements Variant { public static Variant minecraftHolderToBukkit(Holder minecraft) { @@ -96,4 +109,19 @@ public CraftVariant(final Holder holder) { super(holder); } } + + public static class CraftSoundVariant extends HolderableBase implements SoundVariant { + + public static SoundVariant minecraftHolderToBukkit(Holder minecraft) { + return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.PIG_SOUND_VARIANT); + } + + public static Holder bukkitToMinecraftHolder(SoundVariant bukkit) { + return CraftRegistry.bukkitToMinecraftHolder(bukkit); + } + + public CraftSoundVariant(final Holder holder) { + super(holder); + } + } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPiglinAbstract.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPiglinAbstract.java index a30bbedafdbf..cb6a367deb2a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPiglinAbstract.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPiglinAbstract.java @@ -53,8 +53,8 @@ public boolean isBaby() { } @Override - public void setBaby(boolean flag) { - this.getHandle().setBaby(flag); + public void setBaby(boolean baby) { + CraftAgeable.setBaby(this.getHandle(), baby); } @Override @@ -63,12 +63,12 @@ public int getAge() { } @Override - public void setAge(int i) { - this.getHandle().setBaby(i < 0); + public void setAge(int age) { + this.getHandle().setBaby(age < 0); } @Override - public void setAgeLock(boolean b) { + public void setAgeLock(boolean lock) { } @Override @@ -78,12 +78,12 @@ public boolean getAgeLock() { @Override public void setBaby() { - this.getHandle().setBaby(true); + CraftAgeable.setBaby(this.getHandle(), true); } @Override public void setAdult() { - this.getHandle().setBaby(false); + CraftAgeable.setBaby(this.getHandle(), false); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index d86ebc4cd182..47f88ea7304d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -58,9 +58,11 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.SectionPos; +import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.PlayerChatMessage; +import net.minecraft.network.chat.ResolutionContext; import net.minecraft.network.protocol.common.ClientboundClearDialogPacket; import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; import net.minecraft.network.protocol.common.ClientboundResourcePackPopPacket; @@ -95,6 +97,7 @@ import net.minecraft.network.protocol.game.ClientboundSetHealthPacket; import net.minecraft.network.protocol.game.ClientboundSetPlayerInventoryPacket; import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket; +import net.minecraft.network.protocol.game.ClientboundSetTimePacket; import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket; import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket; import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; @@ -115,6 +118,8 @@ import net.minecraft.sounds.SoundEvent; import net.minecraft.util.ProblemReporter; import net.minecraft.world.InteractionHand; +import net.minecraft.world.clock.ClockNetworkState; +import net.minecraft.world.clock.WorldClock; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.ai.attributes.AttributeInstance; @@ -127,6 +132,7 @@ import net.minecraft.world.level.block.entity.SignBlockEntity; import net.minecraft.world.level.block.entity.SignText; import net.minecraft.world.level.border.BorderChangeListener; +import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.saveddata.maps.MapDecoration; import net.minecraft.world.level.saveddata.maps.MapId; import net.minecraft.world.level.saveddata.maps.MapItemSavedData; @@ -360,21 +366,21 @@ public void transfer(String host, int port) { // Paper start - Implement NetworkClient @Override public int getProtocolVersion() { - if (getHandle().connection == null) return -1; - return getHandle().connection.connection.protocolVersion; + if (this.getHandle().connection == null) return -1; + return this.getHandle().connection.connection.protocolVersion; } @Override public InetSocketAddress getVirtualHost() { - if (getHandle().connection == null) return null; - return getHandle().connection.connection.virtualHost; + if (this.getHandle().connection == null) return null; + return this.getHandle().connection.connection.virtualHost; } // Paper end @Override public double getEyeHeight(boolean ignorePose) { if (ignorePose) { - return 1.62D; + return 1.62; } else { return this.getEyeHeight(); } @@ -723,7 +729,7 @@ public void setCompassTarget(Location loc) { this.getHandle().connection.send(new ClientboundSetDefaultSpawnPositionPacket( LevelData.RespawnData.of( ((CraftWorld) loc.getWorld()).getHandle().dimension(), - CraftLocation.toBlockPosition(loc), + CraftLocation.toBlockPos(loc), loc.getYaw(), loc.getPitch() ) @@ -775,7 +781,7 @@ public void playNote(Location loc, Instrument instrument, Note note) { if (instrumentSound == null) return; // Paper start - use correct pitch (modeled off of NoteBlock) - final net.minecraft.world.level.block.state.properties.NoteBlockInstrument noteBlockInstrument = CraftBlockData.toNMS(instrument, net.minecraft.world.level.block.state.properties.NoteBlockInstrument.class); + final net.minecraft.world.level.block.state.properties.NoteBlockInstrument noteBlockInstrument = CraftBlockData.toVanilla(instrument, net.minecraft.world.level.block.state.properties.NoteBlockInstrument.class); final float pitch = noteBlockInstrument.isTunable() ? note.getPitch() : 1.0f; // Paper end this.getHandle().connection.send(new ClientboundSoundPacket(CraftSound.bukkitToMinecraftHolder(instrumentSound), net.minecraft.sounds.SoundSource.RECORDS, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, pitch, this.getHandle().getRandom().nextLong())); @@ -879,7 +885,7 @@ public void playEffect(Location loc, Effect effect, int data) { if (this.getHandle().connection == null) return; int packetData = effect.getId(); - ClientboundLevelEventPacket packet = new ClientboundLevelEventPacket(packetData, CraftLocation.toBlockPosition(loc), data, false); + ClientboundLevelEventPacket packet = new ClientboundLevelEventPacket(packetData, CraftLocation.toBlockPos(loc), data, false); this.getHandle().connection.send(packet); } @@ -910,7 +916,7 @@ public boolean breakBlock(Block block) { public void sendBlockChange(Location loc, Material material, byte data) { if (this.getHandle().connection == null) return; - ClientboundBlockUpdatePacket packet = new ClientboundBlockUpdatePacket(CraftLocation.toBlockPosition(loc), CraftMagicNumbers.getBlock(material, data)); + ClientboundBlockUpdatePacket packet = new ClientboundBlockUpdatePacket(CraftLocation.toBlockPos(loc), CraftMagicNumbers.getBlock(material, data)); this.getHandle().connection.send(packet); } @@ -918,7 +924,7 @@ public void sendBlockChange(Location loc, Material material, byte data) { public void sendBlockChange(Location loc, BlockData block) { if (this.getHandle().connection == null) return; - ClientboundBlockUpdatePacket packet = new ClientboundBlockUpdatePacket(CraftLocation.toBlockPosition(loc), ((CraftBlockData) block).getState()); + ClientboundBlockUpdatePacket packet = new ClientboundBlockUpdatePacket(CraftLocation.toBlockPos(loc), ((CraftBlockData) block).getState()); this.getHandle().connection.send(packet); } @@ -1002,7 +1008,7 @@ public void sendBlockDamage(Location loc, float progress, int sourceId) { stage = -1; // The protocol states that any other value will reset the damage, which this API promises } - ClientboundBlockDestructionPacket packet = new ClientboundBlockDestructionPacket(sourceId, CraftLocation.toBlockPosition(loc), stage); + ClientboundBlockDestructionPacket packet = new ClientboundBlockDestructionPacket(sourceId, CraftLocation.toBlockPos(loc), stage); this.getHandle().connection.send(packet); } @@ -1054,7 +1060,7 @@ public void sendSignChange(Location loc, @Nullable String @Nullable [] lines, Dy private void sendSignChange0(Component[] components, Location loc, DyeColor dyeColor, boolean hasGlowingText) { // Paper end - SignBlockEntity sign = new SignBlockEntity(CraftLocation.toBlockPosition(loc), Blocks.OAK_SIGN.defaultBlockState()); + SignBlockEntity sign = new SignBlockEntity(CraftLocation.toBlockPos(loc), Blocks.OAK_SIGN.defaultBlockState()); SignText text = sign.getFrontText(); text = text.setColor(net.minecraft.world.item.DyeColor.byId(dyeColor.getWoolData())); text = text.setHasGlowingText(hasGlowingText); @@ -1167,27 +1173,27 @@ public void setWorldBorder(WorldBorder border) { private BorderChangeListener createWorldBorderListener() { return new BorderChangeListener() { @Override - public void onSetSize(net.minecraft.world.level.border.WorldBorder border, double size) { + public void onSetSize(net.minecraft.world.level.border.WorldBorder border, double newSize) { CraftPlayer.this.getHandle().connection.send(new ClientboundSetBorderSizePacket(border)); } @Override - public void onLerpSize(net.minecraft.world.level.border.WorldBorder border, double fromSize, double toSize, long time, long gameTime) { + public void onLerpSize(net.minecraft.world.level.border.WorldBorder border, double fromSize, double targetSize, long ticks, long gameTime) { CraftPlayer.this.getHandle().connection.send(new ClientboundSetBorderLerpSizePacket(border)); } @Override - public void onSetCenter(net.minecraft.world.level.border.WorldBorder border, double centerX, double centerZ) { + public void onSetCenter(net.minecraft.world.level.border.WorldBorder border, double x, double z) { CraftPlayer.this.getHandle().connection.send(new ClientboundSetBorderCenterPacket(border)); } @Override - public void onSetWarningTime(net.minecraft.world.level.border.WorldBorder border, int warningTime) { + public void onSetWarningTime(net.minecraft.world.level.border.WorldBorder border, int time) { CraftPlayer.this.getHandle().connection.send(new ClientboundSetBorderWarningDelayPacket(border)); } @Override - public void onSetWarningBlocks(net.minecraft.world.level.border.WorldBorder border, int warningBlockDistance) { + public void onSetWarningBlocks(net.minecraft.world.level.border.WorldBorder border, int blocks) { CraftPlayer.this.getHandle().connection.send(new ClientboundSetBorderWarningDistancePacket(border)); } @@ -1195,7 +1201,7 @@ public void onSetWarningBlocks(net.minecraft.world.level.border.WorldBorder bord public void onSetDamagePerBlock(net.minecraft.world.level.border.WorldBorder border, double damagePerBlock) {} // NO OP @Override - public void onSetSafeZone(net.minecraft.world.level.border.WorldBorder border, double safeZoneRadius) {} // NO OP + public void onSetSafeZone(net.minecraft.world.level.border.WorldBorder border, double safeZone) {} // NO OP }; } @@ -1392,7 +1398,7 @@ public void setRespawnLocation(Location location, boolean override) { new ServerPlayer.RespawnConfig( LevelData.RespawnData.of( ((CraftWorld) location.getWorld()).getHandle().dimension(), - CraftLocation.toBlockPosition(location), + CraftLocation.toBlockPos(location), location.getYaw(), location.getPitch() ), @@ -1425,7 +1431,7 @@ public Location getBedLocation() { @Override public boolean hasDiscoveredRecipe(NamespacedKey recipe) { Preconditions.checkArgument(recipe != null, "recipe cannot be null"); - return this.getHandle().getRecipeBook().contains(CraftRecipe.toMinecraft(recipe)); + return this.getHandle().getRecipeBook().contains(CraftNamespacedKey.toResourceKey(Registries.RECIPE, recipe)); } @Override @@ -1526,18 +1532,25 @@ public void setStatistic(Statistic statistic, EntityType entityType, int newValu } @Override - public void setPlayerTime(long time, boolean relative) { + public void setPlayerTime(long time, boolean tickTime) { this.getHandle().timeOffset = time; - this.getHandle().relativeTime = relative; + this.getHandle().relativeTime = tickTime; - if (this.getHandle().connection == null) { + final ServerLevel level = this.getHandle().level(); + if (this.getHandle().connection == null || level.dimensionType().defaultClock().isEmpty()) { return; } - final long gameTime = this.getHandle().level().getGameTime(); - final long dayTime = this.getHandle().getPlayerTime(); - final boolean tickDayTime = this.getHandle().relativeTime && this.getHandle().level().getGameRules().get(net.minecraft.world.level.gamerules.GameRules.ADVANCE_TIME); - this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundSetTimePacket(gameTime, dayTime, tickDayTime)); + final long gameTime = level.getGameTime(); + final long playerClockTime = this.getHandle().getPlayerTime(); + final Holder worldClock = level.dimensionType().defaultClock().get(); + final boolean paused = !this.getHandle().relativeTime || !level.getGameRules().get(GameRules.ADVANCE_TIME) || level.clockManager().isPaused(worldClock); + final ClockNetworkState clockState = new ClockNetworkState( + playerClockTime, + level.clockManager().partialTick(worldClock), + paused ? 0.0F : level.clockManager().rate(worldClock) + ); + this.getHandle().connection.send(new ClientboundSetTimePacket(gameTime, Map.of(worldClock, clockState))); } @Override @@ -2975,7 +2988,7 @@ public void openBook(ItemStack book) { net.minecraft.world.item.ItemStack bookItem = CraftItemStack.asNMSCopy(book); ServerPlayer serverPlayer = this.getHandle(); - net.minecraft.world.item.component.WrittenBookContent.resolveForItem(bookItem, serverPlayer.createCommandSourceStack(), serverPlayer); + net.minecraft.world.item.component.WrittenBookContent.resolveForItem(bookItem, ResolutionContext.create(serverPlayer.createCommandSourceStack()), serverPlayer.registryAccess()); sendBookOpen(bookItem); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSniffer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSniffer.java index 808b037b660e..f7a2168e37f8 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSniffer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftSniffer.java @@ -30,7 +30,7 @@ public Collection getExploredLocations() { public void removeExploredLocation(Location location) { Preconditions.checkArgument(location != null, "location cannot be null"); - BlockPos pos = CraftLocation.toBlockPosition(location); + BlockPos pos = CraftLocation.toBlockPos(location); net.minecraft.world.level.Level level = location.getWorld() != null ? ((org.bukkit.craftbukkit.CraftWorld) location.getWorld()).getHandle() : this.getHandle().level(); net.minecraft.core.GlobalPos globalPos = net.minecraft.core.GlobalPos.of(level.dimension(), pos); this.getHandle().getBrain().setMemory(MemoryModuleType.SNIFFER_EXPLORED_POSITIONS, this.getHandle().getExploredPositions().filter(blockPositionExplored -> !blockPositionExplored.equals(globalPos)).collect(Collectors.toList())); @@ -43,7 +43,7 @@ public void addExploredLocation(Location location) { return; } - this.getHandle().storeExploredPosition(CraftLocation.toBlockPosition(location)); + this.getHandle().storeExploredPosition(CraftLocation.toBlockPos(location)); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java index 4b391fdd35d4..eec4a8a2992a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java @@ -72,6 +72,6 @@ public void setBlockData(org.bukkit.block.data.BlockData data) { @Override public org.bukkit.block.data.BlockData getBlockData() { - return org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(this.getHandle().getBlockState()); + return this.getHandle().getBlockState().asBlockData(); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTadpole.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTadpole.java index 1efb6f720f34..537b3590e036 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTadpole.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTadpole.java @@ -26,11 +26,11 @@ public void setAge(int age) { @Override public void setAgeLock(boolean lock) { - this.getHandle().ageLocked = lock; + this.getHandle().setAgeLocked(lock); } @Override public boolean getAgeLock() { - return this.getHandle().ageLocked; + return this.getHandle().isAgeLocked(); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java index 5d8dd7a9ec44..3a9a9677465d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java @@ -32,7 +32,7 @@ public org.bukkit.Location getHome() { @Override public void setHome(org.bukkit.Location location) { - this.getHandle().homePos = CraftLocation.toBlockPosition(location); + this.getHandle().homePos = CraftLocation.toBlockPos(location); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java index 72583e24c54e..80cc06f91f80 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java @@ -72,7 +72,7 @@ public void setBound(Location location) { this.getHandle().setBoundOrigin(null); } else { Preconditions.checkArgument(this.getWorld().equals(location.getWorld()), "The bound world cannot be different to the entity's world."); - this.getHandle().setBoundOrigin(CraftLocation.toBlockPosition(location)); + this.getHandle().setBoundOrigin(CraftLocation.toBlockPos(location)); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java index 22704813d84f..d37de00e5b9a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java @@ -1,15 +1,20 @@ package org.bukkit.craftbukkit.entity; import com.destroystokyo.paper.entity.villager.Reputation; +import com.destroystokyo.paper.entity.villager.ReputationType; import com.google.common.base.Preconditions; import io.papermc.paper.util.OldEnumHolderable; +import java.util.EnumMap; import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.ai.gossip.GossipContainer; import net.minecraft.world.entity.monster.zombie.Zombie; +import net.minecraft.world.entity.npc.villager.VillagerData; import net.minecraft.world.entity.npc.villager.VillagerProfession; import net.minecraft.world.entity.npc.villager.VillagerType; import net.minecraft.world.level.block.BedBlock; @@ -70,7 +75,11 @@ public int getVillagerLevel() { @Override public void setVillagerLevel(int level) { - Preconditions.checkArgument(1 <= level && level <= 5, "level (%s) must be between [1, 5]", level); + Preconditions.checkArgument( + VillagerData.MIN_VILLAGER_LEVEL <= level && level <= VillagerData.MAX_VILLAGER_LEVEL, + "level (%s) must be between [%s, %s]", + level, VillagerData.MIN_VILLAGER_LEVEL, VillagerData.MAX_VILLAGER_LEVEL + ); this.getHandle().setVillagerData(this.getHandle().getVillagerData().withLevel(level)); } @@ -87,21 +96,15 @@ public void setVillagerExperience(int experience) { this.getHandle().setVillagerXp(experience); } - // Paper start @Override public boolean increaseLevel(int amount) { Preconditions.checkArgument(amount > 0, "Level earned must be positive"); - int supposedFinalLevel = this.getVillagerLevel() + amount; - Preconditions.checkArgument(net.minecraft.world.entity.npc.villager.VillagerData.MIN_VILLAGER_LEVEL <= supposedFinalLevel && supposedFinalLevel <= net.minecraft.world.entity.npc.villager.VillagerData.MAX_VILLAGER_LEVEL, - "Final level reached after the donation (%d) must be between [%d, %d]".formatted(supposedFinalLevel, net.minecraft.world.entity.npc.villager.VillagerData.MIN_VILLAGER_LEVEL, net.minecraft.world.entity.npc.villager.VillagerData.MAX_VILLAGER_LEVEL)); - - it.unimi.dsi.fastutil.ints.Int2ObjectMap trades = - net.minecraft.world.entity.npc.villager.VillagerTrades.TRADES.get((this.getHandle().getVillagerData().profession().unwrapKey().orElseThrow())); - - if (trades == null || trades.isEmpty()) { - this.getHandle().setVillagerData(this.getHandle().getVillagerData().withLevel(supposedFinalLevel)); + int currentLevel = this.getVillagerLevel(); + int newLevel = Math.clamp(currentLevel + amount, VillagerData.MIN_VILLAGER_LEVEL, VillagerData.MAX_VILLAGER_LEVEL); + if (currentLevel == newLevel) { return false; } + amount = newLevel - currentLevel; while (amount > 0) { this.getHandle().increaseMerchantCareer((ServerLevel) this.getHandle().level()); @@ -113,7 +116,7 @@ public boolean increaseLevel(int amount) { @Override public boolean addTrades(int amount) { Preconditions.checkArgument(amount > 0, "Number of trades unlocked must be positive"); - return this.getHandle().updateTrades(amount); + return this.getHandle().updateTrades((ServerLevel) this.getHandle().level(), amount); } @Override @@ -125,7 +128,6 @@ public int getRestocksToday() { public void setRestocksToday(int restocksToday) { getHandle().numberOfRestocksToday = restocksToday; } - // Paper end @Override public boolean sleep(Location location) { @@ -134,7 +136,7 @@ public boolean sleep(Location location) { Preconditions.checkArgument(location.getWorld().equals(this.getWorld()), "Cannot sleep across worlds"); Preconditions.checkState(!this.getHandle().generation, "Cannot sleep during world generation"); - BlockPos position = CraftLocation.toBlockPosition(location); + BlockPos position = CraftLocation.toBlockPos(location); BlockState state = this.getHandle().level().getBlockState(position); if (!(state.getBlock() instanceof BedBlock)) { return false; @@ -159,8 +161,8 @@ public void shakeHead() { @Override public ZombieVillager zombify() { - net.minecraft.world.entity.monster.zombie.ZombieVillager entityzombievillager = Zombie.convertVillagerToZombieVillager(this.getHandle().level().getMinecraftWorld(), this.getHandle(), this.getHandle().blockPosition(), this.isSilent(), EntityTransformEvent.TransformReason.INFECTION, CreatureSpawnEvent.SpawnReason.CUSTOM); - return (entityzombievillager != null) ? (ZombieVillager) entityzombievillager.getBukkitEntity() : null; + net.minecraft.world.entity.monster.zombie.ZombieVillager zombie = Zombie.convertVillagerToZombieVillager(this.getHandle().level().getMinecraftWorld(), this.getHandle(), this.getHandle().blockPosition(), this.isSilent(), EntityTransformEvent.TransformReason.INFECTION, CreatureSpawnEvent.SpawnReason.CUSTOM); + return (zombie != null) ? (ZombieVillager) zombie.getBukkitEntity() : null; } public static class CraftType extends OldEnumHolderable implements Type { @@ -197,50 +199,48 @@ public CraftProfession(final Holder holder) { @Override public Reputation getReputation(UUID uniqueId) { - net.minecraft.world.entity.ai.gossip.GossipContainer.EntityGossips rep = getHandle().getGossips().gossips.get(uniqueId); - if (rep == null) { - return new Reputation(new java.util.EnumMap<>(com.destroystokyo.paper.entity.villager.ReputationType.class)); + GossipContainer.EntityGossips gossips = this.getHandle().getGossips().gossips.get(uniqueId); + if (gossips == null) { + return new Reputation(new EnumMap<>(ReputationType.class)); } - return rep.getPaperReputation(); + return gossips.asReputation(); } @Override public Map getReputations() { - return getHandle().getGossips().gossips.entrySet() + return this.getHandle().getGossips().gossips.entrySet() .stream() - .collect(java.util.stream.Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getPaperReputation())); + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().asReputation())); } @Override public void setReputation(UUID uniqueId, Reputation reputation) { - net.minecraft.world.entity.ai.gossip.GossipContainer.EntityGossips nmsReputation = - getHandle().getGossips().gossips.computeIfAbsent( - uniqueId, - key -> new net.minecraft.world.entity.ai.gossip.GossipContainer.EntityGossips() - ); - nmsReputation.assignFromPaperReputation(reputation); + GossipContainer.EntityGossips gossips = this.getHandle().getGossips().gossips.computeIfAbsent( + uniqueId, _ -> new GossipContainer.EntityGossips() + ); + gossips.assignFromReputation(reputation); } @Override public void setReputations(Map reputations) { for (Map.Entry entry : reputations.entrySet()) { - setReputation(entry.getKey(), entry.getValue()); + this.setReputation(entry.getKey(), entry.getValue()); } } @Override public void clearReputations() { - getHandle().getGossips().gossips.clear(); + this.getHandle().getGossips().gossips.clear(); } @Override public void updateDemand() { - getHandle().updateDemand(); + this.getHandle().updateDemand(); } @Override public void restock() { - getHandle().restock(); + this.getHandle().restock(); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java index 60d158ddb6e4..411cd8b9c33b 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java @@ -59,7 +59,7 @@ public org.bukkit.Location getWanderingTowards() { public void setWanderingTowards(org.bukkit.Location location) { net.minecraft.core.BlockPos pos = null; if (location != null) { - pos = CraftLocation.toBlockPosition(location); + pos = CraftLocation.toBlockPos(location); } this.getHandle().setWanderTarget(pos); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftZoglin.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftZoglin.java index 583af956161d..442d5fa7bc34 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftZoglin.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftZoglin.java @@ -20,8 +20,8 @@ public boolean isBaby() { } @Override - public void setBaby(boolean flag) { - this.getHandle().setBaby(flag); + public void setBaby(boolean baby) { + CraftAgeable.setBaby(this.getHandle(), baby); } @Override @@ -30,12 +30,12 @@ public int getAge() { } @Override - public void setAge(int i) { - this.getHandle().setBaby(i < 0); + public void setAge(int age) { + this.getHandle().setBaby(age < 0); } @Override - public void setAgeLock(boolean b) { + public void setAgeLock(boolean lock) { } @Override @@ -45,12 +45,12 @@ public boolean getAgeLock() { @Override public void setBaby() { - this.getHandle().setBaby(true); + CraftAgeable.setBaby(this.getHandle(), true); } @Override public void setAdult() { - this.getHandle().setBaby(false); + CraftAgeable.setBaby(this.getHandle(), false); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java index 58f0b6987122..def476715af2 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java @@ -23,8 +23,8 @@ public boolean isBaby() { } @Override - public void setBaby(boolean flag) { - this.getHandle().setBaby(flag); + public void setBaby(boolean baby) { + CraftAgeable.setBaby(this.getHandle(), baby); } @Override @@ -75,52 +75,47 @@ public int getAge() { } @Override - public void setAge(int i) { - this.getHandle().setBaby(i < 0); + public void setAge(int age) { + this.getHandle().setBaby(age < 0); } @Override - public void setAgeLock(boolean b) { + public void setAgeLock(boolean lock) { } @Override public boolean isDrowning() { - return getHandle().isUnderWaterConverting(); + return this.getHandle().isUnderWaterConverting(); } @Override public void startDrowning(int drownedConversionTime) { - getHandle().startUnderWaterConversion(drownedConversionTime); + this.getHandle().startUnderWaterConversion(drownedConversionTime); } @Override public void stopDrowning() { - getHandle().stopDrowning(); + this.getHandle().stopDrowning(); } @Override public boolean shouldBurnInDay() { - return getHandle().isSunSensitive(); + return this.getHandle().isSunSensitive(); } @Override public boolean isArmsRaised() { - return getHandle().isAggressive(); + return this.getHandle().isAggressive(); } @Override public void setArmsRaised(final boolean raised) { - getHandle().setAggressive(raised); + this.getHandle().setAggressive(raised); } @Override public void setShouldBurnInDay(boolean shouldBurnInDay) { - getHandle().setShouldBurnInDay(shouldBurnInDay); - } - - @Override - public boolean supportsBreakingDoors() { - return true; // All zombies are now capable of breaking doors, see https://bugs.mojang.com/browse/MC-137053 + this.getHandle().setShouldBurnInDay(shouldBurnInDay); } @Override @@ -130,12 +125,12 @@ public boolean getAgeLock() { @Override public void setBaby() { - this.getHandle().setBaby(true); + CraftAgeable.setBaby(this.getHandle(), true); } @Override public void setAdult() { - this.getHandle().setBaby(false); + CraftAgeable.setBaby(this.getHandle(), false); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index cd83ca2ace1d..8321fcec2344 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -6,15 +6,6 @@ import com.google.common.collect.Lists; import com.mojang.authlib.GameProfile; import com.mojang.datafixers.util.Either; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import io.papermc.paper.adventure.PaperAdventure; import io.papermc.paper.block.bed.BedEnterProblem; import io.papermc.paper.connection.HorriblePlayerLoginEventHack; @@ -23,6 +14,14 @@ import io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent; import io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent; import io.papermc.paper.event.player.PlayerBedFailEnterEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.Connection; @@ -45,14 +44,9 @@ import net.minecraft.world.entity.Leashable; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.PathfinderMob; -import net.minecraft.world.entity.animal.fish.AbstractFish; -import net.minecraft.world.entity.animal.golem.AbstractGolem; import net.minecraft.world.entity.animal.Animal; -import net.minecraft.world.entity.animal.fish.WaterAnimal; +import net.minecraft.world.entity.animal.fish.AbstractFish; import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.monster.Ghast; -import net.minecraft.world.entity.monster.Monster; -import net.minecraft.world.entity.monster.Slime; import net.minecraft.world.entity.monster.illager.SpellcasterIllager; import net.minecraft.world.entity.projectile.FireworkRocketEntity; import net.minecraft.world.entity.raid.Raid; @@ -103,7 +97,6 @@ import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.craftbukkit.block.CraftBlockState; import org.bukkit.craftbukkit.block.CraftBlockStates; -import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.damage.CraftDamageSource; import org.bukkit.craftbukkit.entity.CraftEntity; import org.bukkit.craftbukkit.entity.CraftLivingEntity; @@ -163,6 +156,7 @@ import org.bukkit.event.block.BlockRedstoneEvent; import org.bukkit.event.block.BlockShearEntityEvent; import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.block.CauldronLevelChangeEvent; import org.bukkit.event.block.CrafterCraftEvent; import org.bukkit.event.block.EntityBlockFormEvent; import org.bukkit.event.block.FluidLevelChangeEvent; @@ -282,8 +276,8 @@ public class CraftEventFactory { // helper methods - private static boolean canBuild(Level world, Player player, int x, int z) { - return world.mayInteract(((CraftPlayer) player).getHandle(), new BlockPos(x, 0, z)); + private static boolean canBuild(Level level, Player player, int x, int z) { + return level.mayInteract(((CraftPlayer) player).getHandle(), new BlockPos(x, 0, z)); } public static boolean callPlayerSignOpenEvent(net.minecraft.world.entity.player.Player player, SignBlockEntity signBlockEntity, boolean front, PlayerSignOpenEvent.Cause cause) { @@ -451,9 +445,9 @@ public static EntityEnterLoveModeEvent callEntityEnterLoveModeEvent(net.minecraf return entityEnterLoveModeEvent; } - public static PlayerHarvestBlockEvent callPlayerHarvestBlockEvent(Level world, BlockPos pos, net.minecraft.world.entity.player.Player player, InteractionHand hand, List itemsToHarvest) { + public static PlayerHarvestBlockEvent callPlayerHarvestBlockEvent(Level level, BlockPos pos, net.minecraft.world.entity.player.Player player, InteractionHand hand, List itemsToHarvest) { List bukkitItemsToHarvest = new ArrayList<>(itemsToHarvest.stream().map(CraftItemStack::asBukkitCopy).collect(Collectors.toList())); - PlayerHarvestBlockEvent playerHarvestBlockEvent = new PlayerHarvestBlockEvent((Player) player.getBukkitEntity(), CraftBlock.at(world, pos), CraftEquipmentSlot.getHand(hand), bukkitItemsToHarvest); + PlayerHarvestBlockEvent playerHarvestBlockEvent = new PlayerHarvestBlockEvent((Player) player.getBukkitEntity(), CraftBlock.at(level, pos), CraftEquipmentSlot.getHand(hand), bukkitItemsToHarvest); Bukkit.getPluginManager().callEvent(playerHarvestBlockEvent); return playerHarvestBlockEvent; } @@ -479,16 +473,16 @@ public static TradeSelectEvent callTradeSelectEvent(int newIndex, MerchantMenu m } @SuppressWarnings("deprecation") // Paper use deprecated event to maintain compat (it extends modern event) - public static boolean handleBellRingEvent(Level world, BlockPos position, Direction direction, Entity entity) { - Block block = CraftBlock.at(world, position); + public static boolean handleBellRingEvent(Level level, BlockPos position, Direction direction, Entity entity) { + Block block = CraftBlock.at(level, position); BlockFace bukkitDirection = CraftBlock.notchToBlockFace(direction); BellRingEvent event = new io.papermc.paper.event.block.BellRingEvent(block, bukkitDirection, (entity != null) ? entity.getBukkitEntity() : null); // Paper - deprecated BellRingEvent Bukkit.getPluginManager().callEvent(event); return !event.isCancelled(); } - public static Stream handleBellResonateEvent(Level world, BlockPos position, List bukkitEntities) { - Block block = CraftBlock.at(world, position); + public static Stream handleBellResonateEvent(Level level, BlockPos position, List bukkitEntities) { + Block block = CraftBlock.at(level, position); BellResonateEvent event = new BellResonateEvent(block, bukkitEntities); Bukkit.getPluginManager().callEvent(event); return event.getResonatedEntities().stream().map((bukkitEntity) -> ((CraftLivingEntity) bukkitEntity).getHandle()); @@ -513,16 +507,16 @@ public static BlockMultiPlaceEvent callBlockMultiPlaceEvent(ServerLevel level, n return event; } - public static BlockPlaceEvent callBlockPlaceEvent(ServerLevel level, net.minecraft.world.entity.player.Player player, InteractionHand hand, BlockState replacedSnapshot, BlockPos clickedPos) { + public static BlockPlaceEvent callBlockPlaceEvent(ServerLevel level, net.minecraft.world.entity.player.Player player, InteractionHand hand, BlockState replacedState, BlockPos clickedPos) { Player cplayer = (Player) player.getBukkitEntity(); Block clickedBlock = CraftBlock.at(level, clickedPos); - Block placedBlock = replacedSnapshot.getBlock(); + Block placedBlock = replacedState.getBlock(); boolean canBuild = CraftEventFactory.canBuild(level, cplayer, placedBlock.getX(), placedBlock.getZ()); EquipmentSlot handSlot = CraftEquipmentSlot.getHand(hand); - BlockPlaceEvent event = new BlockPlaceEvent(placedBlock, replacedSnapshot, clickedBlock, cplayer.getInventory().getItem(handSlot), cplayer, canBuild, handSlot); + BlockPlaceEvent event = new BlockPlaceEvent(placedBlock, replacedState, clickedBlock, cplayer.getInventory().getItem(handSlot), cplayer, canBuild, handSlot); event.callEvent(); return event; @@ -557,9 +551,9 @@ public static EntityPlaceEvent callEntityPlaceEvent(UseOnContext context, Entity return CraftEventFactory.callEntityPlaceEvent(context.getLevel(), context.getClickedPos(), context.getClickedFace(), context.getPlayer(), entity, context.getHand()); } - public static EntityPlaceEvent callEntityPlaceEvent(Level world, BlockPos clickedPos, Direction clickedFace, net.minecraft.world.entity.player.Player player, Entity entity, InteractionHand hand) { + public static EntityPlaceEvent callEntityPlaceEvent(Level level, BlockPos clickedPos, Direction clickedFace, net.minecraft.world.entity.player.Player player, Entity entity, InteractionHand hand) { Player cplayer = (player == null) ? null : (Player) player.getBukkitEntity(); - org.bukkit.block.Block clickedBlock = CraftBlock.at(world, clickedPos); + org.bukkit.block.Block clickedBlock = CraftBlock.at(level, clickedPos); org.bukkit.block.BlockFace blockFace = org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(clickedFace); EntityPlaceEvent event = new EntityPlaceEvent(entity.getBukkitEntity(), cplayer, clickedBlock, blockFace, CraftEquipmentSlot.getHand(hand)); @@ -568,31 +562,31 @@ public static EntityPlaceEvent callEntityPlaceEvent(Level world, BlockPos clicke return event; } - public static PlayerBucketEmptyEvent callPlayerBucketEmptyEvent(Level world, net.minecraft.world.entity.player.Player player, BlockPos changed, BlockPos clicked, Direction clickedFace, ItemStack itemInHand, InteractionHand hand) { - return (PlayerBucketEmptyEvent) CraftEventFactory.getPlayerBucketEvent(false, world, player, changed, clicked, clickedFace, itemInHand, Items.BUCKET, hand); + public static PlayerBucketEmptyEvent callPlayerBucketEmptyEvent(Level level, net.minecraft.world.entity.player.Player player, BlockPos changed, BlockPos clicked, Direction clickedFace, ItemStack itemInHand, InteractionHand hand) { + return (PlayerBucketEmptyEvent) CraftEventFactory.getPlayerBucketEvent(false, level, player, changed, clicked, clickedFace, itemInHand, Items.BUCKET, hand); } - public static PlayerBucketFillEvent callPlayerBucketFillEvent(Level world, net.minecraft.world.entity.player.Player player, BlockPos changed, BlockPos clicked, Direction clickedFace, ItemStack itemInHand, net.minecraft.world.item.Item bucket, InteractionHand hand) { - return (PlayerBucketFillEvent) CraftEventFactory.getPlayerBucketEvent(true, world, player, clicked, changed, clickedFace, itemInHand, bucket, hand); + public static PlayerBucketFillEvent callPlayerBucketFillEvent(Level level, net.minecraft.world.entity.player.Player player, BlockPos changed, BlockPos clicked, Direction clickedFace, ItemStack itemInHand, net.minecraft.world.item.Item bucket, InteractionHand hand) { + return (PlayerBucketFillEvent) CraftEventFactory.getPlayerBucketEvent(true, level, player, clicked, changed, clickedFace, itemInHand, bucket, hand); } - private static PlayerEvent getPlayerBucketEvent(boolean isFilling, Level world, net.minecraft.world.entity.player.Player player, BlockPos changed, BlockPos clicked, Direction clickedFace, ItemStack bucket, net.minecraft.world.item.Item item, InteractionHand hand) { + private static PlayerEvent getPlayerBucketEvent(boolean isFilling, Level level, net.minecraft.world.entity.player.Player player, BlockPos changed, BlockPos clicked, Direction clickedFace, ItemStack bucket, net.minecraft.world.item.Item item, InteractionHand hand) { Player cplayer = (Player) player.getBukkitEntity(); CraftItemStack itemInHand = CraftItemStack.asNewCraftStack(item); Material bucketItem = CraftItemType.minecraftToBukkit(bucket.getItem()); - Block block = CraftBlock.at(world, changed); - Block clickedBlock = CraftBlock.at(world, clicked); + Block block = CraftBlock.at(level, changed); + Block clickedBlock = CraftBlock.at(level, clicked); BlockFace blockFace = CraftBlock.notchToBlockFace(clickedFace); EquipmentSlot handSlot = CraftEquipmentSlot.getHand(hand); PlayerEvent event; if (isFilling) { event = new PlayerBucketFillEvent(cplayer, block, clickedBlock, blockFace, bucketItem, itemInHand, handSlot); - ((PlayerBucketFillEvent) event).setCancelled(!CraftEventFactory.canBuild(world, cplayer, changed.getX(), changed.getZ())); + ((PlayerBucketFillEvent) event).setCancelled(!CraftEventFactory.canBuild(level, cplayer, changed.getX(), changed.getZ())); } else { event = new PlayerBucketEmptyEvent(cplayer, block, clickedBlock, blockFace, bucketItem, itemInHand, handSlot); - ((PlayerBucketEmptyEvent) event).setCancelled(!CraftEventFactory.canBuild(world, cplayer, changed.getX(), changed.getZ())); + ((PlayerBucketEmptyEvent) event).setCancelled(!CraftEventFactory.canBuild(level, cplayer, changed.getX(), changed.getZ())); } event.callEvent(); @@ -656,17 +650,17 @@ public static PlayerInteractEvent callPlayerInteractEvent(net.minecraft.world.en return event; } - public static EntityTransformEvent callEntityTransformEvent(net.minecraft.world.entity.LivingEntity original, net.minecraft.world.entity.LivingEntity converted, EntityTransformEvent.TransformReason transformReason) { - return CraftEventFactory.callEntityTransformEvent(original, Collections.singletonList(converted), transformReason); + public static EntityTransformEvent callEntityTransformEvent(net.minecraft.world.entity.LivingEntity original, net.minecraft.world.entity.LivingEntity newEntity, EntityTransformEvent.TransformReason transformReason) { + return CraftEventFactory.callEntityTransformEvent(original, Collections.singletonList(newEntity), transformReason); } - public static EntityTransformEvent callEntityTransformEvent(net.minecraft.world.entity.LivingEntity original, List convertedList, EntityTransformEvent.TransformReason convertType) { - List list = new ArrayList<>(); - for (net.minecraft.world.entity.LivingEntity entityLiving : convertedList) { - list.add(entityLiving.getBukkitEntity()); + public static EntityTransformEvent callEntityTransformEvent(net.minecraft.world.entity.LivingEntity original, List newEntities, EntityTransformEvent.TransformReason convertType) { + List transformedEntities = new ArrayList<>(); + for (net.minecraft.world.entity.LivingEntity entity : newEntities) { + transformedEntities.add(entity.getBukkitEntity()); } - EntityTransformEvent event = new EntityTransformEvent(original.getBukkitEntity(), list, convertType); + EntityTransformEvent event = new EntityTransformEvent(original.getBukkitEntity(), transformedEntities, convertType); Bukkit.getPluginManager().callEvent(event); return event; @@ -715,7 +709,7 @@ public static BlockDamageAbortEvent callBlockDamageAbortEvent(ServerPlayer playe return event; } - public static boolean doEntityAddEventCalling(Level world, Entity entity, SpawnReason spawnReason) { + public static boolean doEntityAddEventCalling(Level level, Entity entity, SpawnReason spawnReason) { if (entity == null) return false; org.bukkit.event.Cancellable event = null; @@ -758,15 +752,15 @@ public static boolean doEntityAddEventCalling(Level world, Entity entity, SpawnR // Spigot start - SPIGOT-7523: Merge after spawn event and only merge if the event was not cancelled (gets checked above) if (entity instanceof net.minecraft.world.entity.ExperienceOrb xp) { - double radius = world.spigotConfig.expMerge; + double radius = level.spigotConfig.expMerge; event = CraftEventFactory.callEntitySpawnEvent(entity); // Call spawn event for ExperienceOrb entities if (radius > 0 && !event.isCancelled() && !entity.isRemoved()) { // Paper start - Maximum exp value when merging; Whole section has been tweaked, see comments for specifics - final long maxValue = world.paperConfig().entities.behavior.experienceMergeMaxValue; + final long maxValue = level.paperConfig().entities.behavior.experienceMergeMaxValue; final boolean mergeUnconditionally = maxValue <= 0; if (mergeUnconditionally || xp.getValue() < maxValue) { // Paper - Skip iteration if unnecessary - List entities = world.getEntities(entity, entity.getBoundingBox().inflate(radius, radius, radius)); + List entities = level.getEntities(entity, entity.getBoundingBox().inflate(radius, radius, radius)); for (Entity e : entities) { if (e instanceof net.minecraft.world.entity.ExperienceOrb loopItem) { // Paper start @@ -799,40 +793,31 @@ public static EntitySpawnEvent callEntitySpawnEvent(Entity entity) { return event; } - public static CreatureSpawnEvent callCreatureSpawnEvent(net.minecraft.world.entity.LivingEntity entityliving, SpawnReason spawnReason) { - LivingEntity entity = (LivingEntity) entityliving.getBukkitEntity(); - CraftServer craftServer = (CraftServer) entity.getServer(); - - CreatureSpawnEvent event = new CreatureSpawnEvent(entity, spawnReason); - craftServer.getPluginManager().callEvent(event); + public static CreatureSpawnEvent callCreatureSpawnEvent(net.minecraft.world.entity.LivingEntity entity, SpawnReason spawnReason) { + CreatureSpawnEvent event = new CreatureSpawnEvent((LivingEntity) entity.getBukkitEntity(), spawnReason); + event.callEvent(); return event; } public static EntityTameEvent callEntityTameEvent(Mob entity, net.minecraft.world.entity.player.Player tamer) { - org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity(); - org.bukkit.entity.AnimalTamer bukkitTamer = (tamer != null ? tamer.getBukkitEntity() : null); - CraftServer craftServer = (CraftServer) bukkitEntity.getServer(); - - EntityTameEvent event = new EntityTameEvent((LivingEntity) bukkitEntity, bukkitTamer); - craftServer.getPluginManager().callEvent(event); + EntityTameEvent event = new EntityTameEvent((LivingEntity) entity.getBukkitEntity(), tamer.getBukkitEntity()); + event.callEvent(); return event; } - public static ItemSpawnEvent callItemSpawnEvent(ItemEntity entityitem) { - org.bukkit.entity.Item entity = (org.bukkit.entity.Item) entityitem.getBukkitEntity(); + public static ItemSpawnEvent callItemSpawnEvent(ItemEntity item) { + org.bukkit.entity.Item entity = (org.bukkit.entity.Item) item.getBukkitEntity(); CraftServer craftServer = (CraftServer) entity.getServer(); ItemSpawnEvent event = new ItemSpawnEvent(entity); - craftServer.getPluginManager().callEvent(event); return event; } - public static ItemDespawnEvent callItemDespawnEvent(ItemEntity entityitem) { - org.bukkit.entity.Item entity = (org.bukkit.entity.Item) entityitem.getBukkitEntity(); + public static ItemDespawnEvent callItemDespawnEvent(ItemEntity item) { + org.bukkit.entity.Item entity = (org.bukkit.entity.Item) item.getBukkitEntity(); ItemDespawnEvent event = new ItemDespawnEvent(entity, entity.getLocation()); - entity.getServer().getPluginManager().callEvent(event); return event; } @@ -916,18 +901,18 @@ public static io.papermc.paper.event.entity.WaterBottleSplashEvent callWaterBott } // Paper end - Fix potions splash events - public static BlockFadeEvent callBlockFadeEvent(LevelAccessor world, BlockPos pos, net.minecraft.world.level.block.state.BlockState state) { - CraftBlockState snapshot = CraftBlockStates.getBlockState(world, pos); - snapshot.setData(state); + public static BlockFadeEvent callBlockFadeEvent(LevelAccessor level, BlockPos pos, net.minecraft.world.level.block.state.BlockState state) { + CraftBlockState snapshot = CraftBlockStates.getBlockState(level, pos); + snapshot.setBlock(state); BlockFadeEvent event = new BlockFadeEvent(snapshot.getBlock(), snapshot); Bukkit.getPluginManager().callEvent(event); return event; } - public static boolean handleMoistureChangeEvent(Level world, BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags) { - CraftBlockState snapshot = CraftBlockStates.getBlockState(world, pos); - snapshot.setData(state); + public static boolean handleMoistureChangeEvent(Level level, BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags) { + CraftBlockState snapshot = CraftBlockStates.getBlockState(level, pos); + snapshot.setBlock(state); MoistureChangeEvent event = new MoistureChangeEvent(snapshot.getBlock(), snapshot); if (event.callEvent()) { @@ -939,21 +924,21 @@ public static boolean handleMoistureChangeEvent(Level world, BlockPos pos, net.m public static BlockPos sourceBlockOverride = null; // SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPos up to five methods deep. - public static boolean handleBlockSpreadEvent(LevelAccessor world, BlockPos source, BlockPos target, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags) { - return handleBlockSpreadEvent(world, source, target, state, flags, false); + public static boolean handleBlockSpreadEvent(LevelAccessor level, BlockPos source, BlockPos target, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags) { + return handleBlockSpreadEvent(level, source, target, state, flags, false); } - public static boolean handleBlockSpreadEvent(LevelAccessor world, BlockPos source, BlockPos target, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags, boolean checkSetResult) { + public static boolean handleBlockSpreadEvent(LevelAccessor level, BlockPos source, BlockPos target, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags, boolean checkSetResult) { // Suppress during worldgen - if (!(world instanceof Level)) { - boolean result = world.setBlock(target, state, flags); + if (!(level instanceof Level)) { + boolean result = level.setBlock(target, state, flags); return !checkSetResult || result; } - CraftBlockState snapshot = CraftBlockStates.getBlockState(world, target); - snapshot.setData(state); + CraftBlockState snapshot = CraftBlockStates.getBlockState(level, target); + snapshot.setBlock(state); - BlockSpreadEvent event = new BlockSpreadEvent(snapshot.getBlock(), CraftBlock.at(world, CraftEventFactory.sourceBlockOverride != null ? CraftEventFactory.sourceBlockOverride : source), snapshot); + BlockSpreadEvent event = new BlockSpreadEvent(snapshot.getBlock(), CraftBlock.at(level, CraftEventFactory.sourceBlockOverride != null ? CraftEventFactory.sourceBlockOverride : source), snapshot); if (event.callEvent()) { boolean result = snapshot.place(flags); return !checkSetResult || result; @@ -1291,9 +1276,9 @@ public static PlayerExpChangeEvent callPlayerExpChangeEvent(net.minecraft.world. return event; } - public static boolean handleBlockGrowEvent(Level world, BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags) { - CraftBlockState snapshot = CraftBlockStates.getBlockState(world, pos); - snapshot.setData(state); + public static boolean handleBlockGrowEvent(Level level, BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags) { + CraftBlockState snapshot = CraftBlockStates.getBlockState(level, pos); + snapshot.setBlock(state); BlockGrowEvent event = new BlockGrowEvent(snapshot.getBlock(), snapshot); if (event.callEvent()) { @@ -1304,9 +1289,25 @@ public static boolean handleBlockGrowEvent(Level world, BlockPos pos, net.minecr return false; } - public static FluidLevelChangeEvent callFluidLevelChangeEvent(Level world, BlockPos block, net.minecraft.world.level.block.state.BlockState newData) { - FluidLevelChangeEvent event = new FluidLevelChangeEvent(CraftBlock.at(world, block), CraftBlockData.fromData(newData)); - world.getCraftServer().getPluginManager().callEvent(event); + public static boolean handleCauldronLevelChangeEvent(Level level, BlockPos pos, net.minecraft.world.level.block.state.BlockState newState, @Nullable Entity entity, CauldronLevelChangeEvent.ChangeReason reason) { + CraftBlockState snapshot = CraftBlockStates.getBlockState(level, pos); + snapshot.setBlock(newState); + + CauldronLevelChangeEvent event = new CauldronLevelChangeEvent( + CraftBlock.at(level, pos), + (entity == null) ? null : entity.getBukkitEntity(), reason, snapshot + ); + if (event.callEvent()) { + snapshot.place(net.minecraft.world.level.block.Block.UPDATE_ALL); + return true; + } + + return false; + } + + public static FluidLevelChangeEvent callFluidLevelChangeEvent(Level level, BlockPos block, net.minecraft.world.level.block.state.BlockState newData) { + FluidLevelChangeEvent event = new FluidLevelChangeEvent(CraftBlock.at(level, block), newData.asBlockData()); + level.getCraftServer().getPluginManager().callEvent(event); return event; } @@ -1347,7 +1348,7 @@ public static boolean callEntityChangeBlockEvent(Entity entity, BlockPos pos, ne public static boolean callEntityChangeBlockEvent(Entity entity, BlockPos pos, net.minecraft.world.level.block.state.BlockState newState, boolean cancelled) { Block block = CraftBlock.at(entity.level(), pos); - EntityChangeBlockEvent event = new EntityChangeBlockEvent(entity.getBukkitEntity(), block, CraftBlockData.fromData(newState)); + EntityChangeBlockEvent event = new EntityChangeBlockEvent(entity.getBukkitEntity(), block, newState.asBlockData()); event.setCancelled(cancelled); event.getEntity().getServer().getPluginManager().callEvent(event); return !event.isCancelled(); @@ -1365,17 +1366,27 @@ public static EntityTargetEvent callEntityTargetEvent(Entity entity, Entity targ return event; } + public static EntityTargetEvent.TargetReason getForgotTargetReason(Mob body, net.minecraft.world.entity.@Nullable LivingEntity previousTarget, boolean wasInvalid) { + if (previousTarget != null && !previousTarget.isAlive()) { + return EntityTargetEvent.TargetReason.TARGET_DIED; + } else if (wasInvalid || (previousTarget != null && !body.canAttack(previousTarget))) { + return EntityTargetEvent.TargetReason.TARGET_INVALID; + } else { + return EntityTargetEvent.TargetReason.FORGOT_TARGET; + } + } + public static EntityTargetLivingEntityEvent callEntityTargetLivingEvent(Entity entity, net.minecraft.world.entity.LivingEntity target, EntityTargetEvent.TargetReason reason) { EntityTargetLivingEntityEvent event = new EntityTargetLivingEntityEvent(entity.getBukkitEntity(), (target == null) ? null : (LivingEntity) target.getBukkitEntity(), reason); entity.getBukkitEntity().getServer().getPluginManager().callEvent(event); return event; } - public static EntityBreakDoorEvent callEntityBreakDoorEvent(Entity entity, BlockPos pos, net.minecraft.world.level.block.state.BlockState newState) { // Paper + public static EntityBreakDoorEvent callEntityBreakDoorEvent(Entity entity, BlockPos pos, net.minecraft.world.level.block.state.BlockState newState) { org.bukkit.entity.Entity entity1 = entity.getBukkitEntity(); Block block = CraftBlock.at(entity.level(), pos); - EntityBreakDoorEvent event = new EntityBreakDoorEvent((LivingEntity) entity1, block, newState.createCraftBlockData()); // Paper + EntityBreakDoorEvent event = new EntityBreakDoorEvent((LivingEntity) entity1, block, newState.asBlockData()); entity1.getServer().getPluginManager().callEvent(event); return event; @@ -1423,8 +1434,8 @@ public static ItemStack callPreCraftEvent(CraftingContainer matrix, Container re return CraftItemStack.asNMSCopy(event.getInventory().getResult()); } - public static CrafterCraftEvent callCrafterCraftEvent(BlockPos pos, Level world, ItemStack result, RecipeHolder holder) { - CraftBlock block = CraftBlock.at(world, pos); + public static CrafterCraftEvent callCrafterCraftEvent(BlockPos pos, Level level, ItemStack result, RecipeHolder holder) { + CraftBlock block = CraftBlock.at(level, pos); CraftItemStack itemStack = CraftItemStack.asCraftMirror(result); CraftingRecipe craftingRecipe = (CraftingRecipe) holder.toBukkitRecipe(); @@ -1522,9 +1533,9 @@ public static BlockRedstoneEvent callRedstoneChange(LevelAccessor level, BlockPo return event; } - public static NotePlayEvent callNotePlayEvent(Level world, BlockPos pos, NoteBlockInstrument instrument, int note) { - NotePlayEvent event = new NotePlayEvent(CraftBlock.at(world, pos), org.bukkit.Instrument.getByType((byte) instrument.ordinal()), new org.bukkit.Note(note)); - world.getCraftServer().getPluginManager().callEvent(event); + public static NotePlayEvent callNotePlayEvent(Level level, BlockPos pos, NoteBlockInstrument instrument, int note) { + NotePlayEvent event = new NotePlayEvent(CraftBlock.at(level, pos), org.bukkit.Instrument.values()[instrument.ordinal()], new org.bukkit.Note(note)); + level.getCraftServer().getPluginManager().callEvent(event); return event; } @@ -1534,8 +1545,8 @@ public static void callPlayerItemBreakEvent(ServerPlayer human, ItemStack broken Bukkit.getPluginManager().callEvent(event); } - public static BlockIgniteEvent callBlockIgniteEvent(Level world, BlockPos pos, BlockPos sourcePos) { - Block igniter = CraftBlock.at(world, sourcePos); + public static BlockIgniteEvent callBlockIgniteEvent(Level level, BlockPos pos, BlockPos sourcePos) { + Block igniter = CraftBlock.at(level, sourcePos); final IgniteCause cause; switch (igniter.getType()) { case LAVA: @@ -1549,12 +1560,12 @@ public static BlockIgniteEvent callBlockIgniteEvent(Level world, BlockPos pos, B cause = IgniteCause.SPREAD; } - BlockIgniteEvent event = new BlockIgniteEvent(CraftBlock.at(world, pos), cause, igniter); - world.getCraftServer().getPluginManager().callEvent(event); + BlockIgniteEvent event = new BlockIgniteEvent(CraftBlock.at(level, pos), cause, igniter); + level.getCraftServer().getPluginManager().callEvent(event); return event; } - public static BlockIgniteEvent callBlockIgniteEvent(Level world, BlockPos pos, Entity igniter) { + public static BlockIgniteEvent callBlockIgniteEvent(Level level, BlockPos pos, Entity igniter) { org.bukkit.entity.Entity bukkitIgniter = igniter.getBukkitEntity(); IgniteCause cause = switch (bukkitIgniter.getType()) { case END_CRYSTAL -> IgniteCause.ENDER_CRYSTAL; @@ -1571,22 +1582,22 @@ public static BlockIgniteEvent callBlockIgniteEvent(Level world, BlockPos pos, E } } - BlockIgniteEvent event = new BlockIgniteEvent(CraftBlock.at(world, pos), cause, bukkitIgniter); - world.getCraftServer().getPluginManager().callEvent(event); + BlockIgniteEvent event = new BlockIgniteEvent(CraftBlock.at(level, pos), cause, bukkitIgniter); + level.getCraftServer().getPluginManager().callEvent(event); return event; } - public static BlockIgniteEvent callBlockIgniteEvent(Level world, BlockPos pos, Explosion explosion) { + public static BlockIgniteEvent callBlockIgniteEvent(Level level, BlockPos pos, Explosion explosion) { org.bukkit.entity.Entity igniter = explosion.getDirectSourceEntity() == null ? null : explosion.getDirectSourceEntity().getBukkitEntity(); - BlockIgniteEvent event = new BlockIgniteEvent(CraftBlock.at(world, pos), IgniteCause.EXPLOSION, igniter); - world.getCraftServer().getPluginManager().callEvent(event); + BlockIgniteEvent event = new BlockIgniteEvent(CraftBlock.at(level, pos), IgniteCause.EXPLOSION, igniter); + level.getCraftServer().getPluginManager().callEvent(event); return event; } - public static BlockIgniteEvent callBlockIgniteEvent(Level world, BlockPos pos, IgniteCause cause, Entity igniter) { - BlockIgniteEvent event = new BlockIgniteEvent(CraftBlock.at(world, pos), cause, igniter.getBukkitEntity()); - world.getCraftServer().getPluginManager().callEvent(event); + public static BlockIgniteEvent callBlockIgniteEvent(Level level, BlockPos pos, IgniteCause cause, Entity igniter) { + BlockIgniteEvent event = new BlockIgniteEvent(CraftBlock.at(level, pos), cause, igniter.getBukkitEntity()); + level.getCraftServer().getPluginManager().callEvent(event); return event; } @@ -1870,11 +1881,11 @@ public static EntityBreedEvent callEntityBreedEvent(net.minecraft.world.entity.L return event; } - public static BlockPhysicsEvent callBlockPhysicsEvent(LevelAccessor world, BlockPos pos) { - org.bukkit.block.Block block = CraftBlock.at(world, pos); + public static BlockPhysicsEvent callBlockPhysicsEvent(LevelAccessor level, BlockPos pos) { + org.bukkit.block.Block block = CraftBlock.at(level, pos); BlockPhysicsEvent event = new BlockPhysicsEvent(block, block.getBlockData()); // Suppress during worldgen - if (world instanceof Level) { + if (level instanceof Level) { event.callEvent(); } return event; @@ -1911,17 +1922,17 @@ public static EntityPotionEffectEvent callEntityPotionEffectChangeEvent(net.mine return event; } - public static boolean handleBlockFormEvent(Level world, BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags) { - return CraftEventFactory.handleBlockFormEvent(world, pos, state, flags, null); + public static boolean handleBlockFormEvent(Level level, BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags) { + return CraftEventFactory.handleBlockFormEvent(level, pos, state, flags, null); } - public static boolean handleBlockFormEvent(Level world, BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags, @Nullable Entity entity) { - return CraftEventFactory.handleBlockFormEvent(world, pos, state, flags, entity, false); + public static boolean handleBlockFormEvent(Level level, BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags, @Nullable Entity entity) { + return CraftEventFactory.handleBlockFormEvent(level, pos, state, flags, entity, false); } - public static boolean handleBlockFormEvent(Level world, BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags, @Nullable Entity entity, boolean checkSetResult) { - CraftBlockState snapshot = CraftBlockStates.getBlockState(world, pos); - snapshot.setData(state); + public static boolean handleBlockFormEvent(Level level, BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @net.minecraft.world.level.block.Block.UpdateFlags int flags, @Nullable Entity entity, boolean checkSetResult) { + CraftBlockState snapshot = CraftBlockStates.getBlockState(level, pos); + snapshot.setBlock(state); BlockFormEvent event = (entity == null) ? new BlockFormEvent(snapshot.getBlock(), snapshot) : new EntityBlockFormEvent(entity.getBukkitEntity(), snapshot.getBlock(), snapshot); if (event.callEvent()) { @@ -2031,23 +2042,23 @@ public static PiglinBarterEvent callPiglinBarterEvent(net.minecraft.world.entity return event; } - public static void callEntitiesLoadEvent(Level world, ChunkPos coords, List entities) { - List bukkitEntities = Collections.unmodifiableList(entities.stream().map(Entity::getBukkitEntity).collect(Collectors.toList())); - EntitiesLoadEvent event = new EntitiesLoadEvent(new CraftChunk((ServerLevel) world, coords.x, coords.z), bukkitEntities); + public static void callEntitiesLoadEvent(Level level, ChunkPos pos, List entities) { + List bukkitEntities = entities.stream().map(Entity::getBukkitEntity).collect(Collectors.toUnmodifiableList()); + EntitiesLoadEvent event = new EntitiesLoadEvent(new CraftChunk((ServerLevel) level, pos.x(), pos.z()), bukkitEntities); Bukkit.getPluginManager().callEvent(event); } - public static void callEntitiesUnloadEvent(Level world, ChunkPos coords, List entities) { - List bukkitEntities = Collections.unmodifiableList(entities.stream().map(Entity::getBukkitEntity).collect(Collectors.toList())); - EntitiesUnloadEvent event = new EntitiesUnloadEvent(new CraftChunk((ServerLevel) world, coords.x, coords.z), bukkitEntities); + public static void callEntitiesUnloadEvent(Level level, ChunkPos pos, List entities) { + List bukkitEntities = entities.stream().map(Entity::getBukkitEntity).collect(Collectors.toUnmodifiableList()); + EntitiesUnloadEvent event = new EntitiesUnloadEvent(new CraftChunk((ServerLevel) level, pos.x(), pos.z()), bukkitEntities); Bukkit.getPluginManager().callEvent(event); } - public static boolean callTNTPrimeEvent(Level world, BlockPos pos, TNTPrimeEvent.PrimeCause cause, Entity causingEntity, BlockPos causePosition) { + public static boolean callTNTPrimeEvent(Level level, BlockPos pos, TNTPrimeEvent.PrimeCause cause, Entity causingEntity, BlockPos causePosition) { org.bukkit.entity.Entity bukkitEntity = (causingEntity == null) ? null : causingEntity.getBukkitEntity(); - org.bukkit.block.Block bukkitBlock = (causePosition == null) ? null : CraftBlock.at(world, causePosition); + org.bukkit.block.Block bukkitBlock = (causePosition == null) ? null : CraftBlock.at(level, causePosition); - TNTPrimeEvent event = new TNTPrimeEvent(CraftBlock.at(world, pos), cause, bukkitEntity, bukkitBlock); + TNTPrimeEvent event = new TNTPrimeEvent(CraftBlock.at(level, pos), cause, bukkitEntity, bukkitBlock); Bukkit.getPluginManager().callEvent(event); return !event.isCancelled(); @@ -2215,9 +2226,9 @@ public static void callEntityRemoveEvent(Entity entity, EntityRemoveEvent.Cause Bukkit.getPluginManager().callEvent(new EntityRemoveEvent(entity.getBukkitEntity(), cause)); } - public static void callPlayerUseUnknownEntityEvent(net.minecraft.world.entity.player.Player player, net.minecraft.network.protocol.game.ServerboundInteractPacket packet, InteractionHand hand, net.minecraft.world.phys.@Nullable Vec3 vector) { + public static void callPlayerUseUnknownEntityEvent(net.minecraft.world.entity.player.Player player, int entityId, boolean attack, InteractionHand hand, net.minecraft.world.phys.@Nullable Vec3 vector) { new com.destroystokyo.paper.event.player.PlayerUseUnknownEntityEvent( - (Player) player.getBukkitEntity(), packet.getEntityId(), packet.isAttack(), + (Player) player.getBukkitEntity(), entityId, attack, CraftEquipmentSlot.getHand(hand), vector != null ? CraftVector.toBukkit(vector) : null ).callEvent(); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java index e45d3f271c71..f90d1929ce93 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java @@ -3,6 +3,7 @@ import com.google.common.base.Preconditions; import java.lang.ref.WeakReference; import net.minecraft.core.BlockPos; +import net.minecraft.core.QuartPos; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; @@ -28,22 +29,21 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { private final int minHeight; private final WeakReference weakChunk; - public CraftChunkData(World world, ChunkAccess chunkAccess) { - this(world.getMaxHeight(), world.getMinHeight(), chunkAccess); + public CraftChunkData(World world, ChunkAccess chunk) { + this(world.getMaxHeight(), world.getMinHeight(), chunk); } - CraftChunkData(int maxHeight, int minHeight, ChunkAccess chunkAccess) { + CraftChunkData(int maxHeight, int minHeight, ChunkAccess chunk) { this.maxHeight = maxHeight; this.minHeight = minHeight; - this.weakChunk = new WeakReference<>(chunkAccess); + this.weakChunk = new WeakReference<>(chunk); } public ChunkAccess getHandle() { - ChunkAccess access = this.weakChunk.get(); + ChunkAccess chunk = this.weakChunk.get(); + Preconditions.checkState(chunk != null, "ChunkAccess no longer present, are you using it in a different tick?"); - Preconditions.checkState(access != null, "IChunkAccess no longer present, are you using it in a different tick?"); - - return access; + return chunk; } public void breakLink() { @@ -62,7 +62,9 @@ public int getMinHeight() { @Override public Biome getBiome(int x, int y, int z) { - return CraftBiome.minecraftHolderToBukkit(this.getHandle().getNoiseBiome(x >> 2, y >> 2, z >> 2)); + return CraftBiome.minecraftHolderToBukkit(this.getHandle().getNoiseBiome( + QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z)) + ); } @Override @@ -107,7 +109,7 @@ public MaterialData getTypeAndData(int x, int y, int z) { @Override public BlockData getBlockData(int x, int y, int z) { - return CraftBlockData.fromData(this.getTypeId(x, y, z)); + return this.getTypeId(x, y, z).asBlockData(); } public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockState type) { @@ -150,8 +152,8 @@ public BlockState getTypeId(int x, int y, int z) { return Blocks.AIR.defaultBlockState(); } - ChunkAccess access = this.getHandle(); - return access.getBlockState(new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z)); + ChunkAccess chunk = this.getHandle(); + return chunk.getBlockState(new BlockPos(chunk.getPos().getMinBlockX() + x, y, chunk.getPos().getMinBlockZ() + z)); } @Override @@ -159,26 +161,26 @@ public byte getData(int x, int y, int z) { return CraftMagicNumbers.toLegacyData(this.getTypeId(x, y, z)); } - private void setBlock(int x, int y, int z, BlockState type) { + private void setBlock(int x, int y, int z, BlockState state) { if (x != (x & 0xf) || y < this.minHeight || y >= this.maxHeight || z != (z & 0xf)) { return; } - ChunkAccess access = this.getHandle(); - BlockPos pos = new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z); - BlockState oldBlockState = access.setBlockState(pos, type); + ChunkAccess chunk = this.getHandle(); + BlockPos pos = new BlockPos(chunk.getPos().getMinBlockX() + x, y, chunk.getPos().getMinBlockZ() + z); + BlockState oldBlockState = chunk.setBlockState(pos, state); - if (type.hasBlockEntity()) { - BlockEntity blockEntity = ((EntityBlock) type.getBlock()).newBlockEntity(pos, type); + if (state.hasBlockEntity()) { + BlockEntity blockEntity = ((EntityBlock) state.getBlock()).newBlockEntity(pos, state); - // newBlockEntity can return null, currently only the case with material MOVING_PISTON + // newBlockEntity can return null, currently only the case with MovingPistonBlock if (blockEntity == null) { - access.removeBlockEntity(pos); + chunk.removeBlockEntity(pos); } else { - access.setBlockEntity(blockEntity); + chunk.setBlockEntity(blockEntity); } } else if (oldBlockState != null && oldBlockState.hasBlockEntity()) { - access.removeBlockEntity(pos); + chunk.removeBlockEntity(pos); } } @@ -187,6 +189,6 @@ public int getHeight(final HeightMap heightMap, final int x, final int z) { Preconditions.checkArgument(heightMap != null, "HeightMap cannot be null"); Preconditions.checkArgument(x >= 0 && x <= 15 && z >= 0 && z <= 15, "Cannot get height outside of a chunks bounds, must be between 0 and 15, got x: %s, z: %s", x, z); - return getHandle().getHeight(CraftHeightMap.toNMS(heightMap), x, z); + return this.getHandle().getHeight(CraftHeightMap.toNMS(heightMap), x, z); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java index eb39073213ff..9e6cf5abc55c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java @@ -1,15 +1,17 @@ package org.bukkit.craftbukkit.generator; import com.google.common.base.Preconditions; +import com.mojang.logging.LogUtils; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Random; import java.util.function.Consumer; -import com.mojang.logging.LogUtils; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; +import net.minecraft.core.QuartPos; +import net.minecraft.core.SectionPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.ProblemReporter; import net.minecraft.world.entity.EntitySpawnReason; @@ -39,7 +41,7 @@ public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRe private static final Logger LOGGER = LogUtils.getLogger(); - private final WeakReference weakAccess; + private final WeakReference weakLevel; private final int centerChunkX; private final int centerChunkZ; // Buffer is one chunk (16 blocks), can be seen in ChunkStatus#q @@ -55,12 +57,12 @@ public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRe // Prevents crash for chunks which are converting from 1.17 to 1.18 private final List outsideEntities = new ArrayList<>(); - public CraftLimitedRegion(WorldGenLevel access, ChunkPos center) { - this.weakAccess = new WeakReference<>(access); - this.centerChunkX = center.x; - this.centerChunkZ = center.z; + public CraftLimitedRegion(WorldGenLevel level, ChunkPos center) { + this.weakLevel = new WeakReference<>(level); + this.centerChunkX = center.x(); + this.centerChunkZ = center.z(); - World world = access.getMinecraftWorld().getWorld(); + World world = level.getMinecraftWorld().getWorld(); int xCenter = this.centerChunkX << 4; int zCenter = this.centerChunkZ << 4; int xMin = xCenter - this.getBuffer(); @@ -72,9 +74,8 @@ public CraftLimitedRegion(WorldGenLevel access, ChunkPos center) { } public WorldGenLevel getHandle() { - WorldGenLevel handle = this.weakAccess.get(); - - Preconditions.checkState(handle != null, "GeneratorAccessSeed no longer present, are you using it in a different tick?"); + WorldGenLevel handle = this.weakLevel.get(); + Preconditions.checkState(handle != null, "WorldGenLevel no longer present, are you using it in a different tick?"); return handle; } @@ -132,7 +133,7 @@ public void saveEntities() { } public void breakLink() { - this.weakAccess.clear(); + this.weakLevel.clear(); } @Override @@ -181,10 +182,10 @@ public Biome getComputedBiome(int x, int y, int z) { // Paper end @Override - public void setBiome(int x, int y, int z, Holder biomeBase) { + public void setBiome(int x, int y, int z, Holder biome) { Preconditions.checkArgument(this.isInRegion(x, y, z), "Coordinates %s, %s, %s are not in the region", x, y, z); - ChunkAccess chunk = this.getHandle().getChunk(x >> 4, z >> 4, ChunkStatus.EMPTY); - chunk.setBiome(x >> 2, y >> 2, z >> 2, biomeBase); + ChunkAccess chunk = this.getHandle().getChunk(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z), ChunkStatus.EMPTY); + chunk.setNoiseBiome(QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z), biome); } @Override @@ -274,8 +275,8 @@ public void addEntityWithPassengers(net.minecraft.world.entity.Entity entity, Cr @Override public void setBlockState(int x, int y, int z, BlockState state) { BlockPos pos = new BlockPos(x, y, z); - if (!state.getBlockData().matches(getHandle().getBlockState(pos).createCraftBlockData())) { - throw new IllegalArgumentException("BlockData does not match! Expected " + state.getBlockData().getAsString(false) + ", got " + getHandle().getBlockState(pos).createCraftBlockData().getAsString(false)); + if (!state.getBlockData().matches(getHandle().getBlockState(pos).asBlockData())) { + throw new IllegalArgumentException("BlockData does not match! Expected " + state.getBlockData().getAsString(false) + ", got " + getHandle().getBlockState(pos).asBlockData().getAsString(false)); } try (final ProblemReporter.ScopedCollector problemReporter = new ProblemReporter.ScopedCollector( diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java index 62bcd2e9dc32..cf3d785013a2 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java @@ -1,11 +1,13 @@ package org.bukkit.craftbukkit.generator; import java.util.UUID; +import net.minecraft.core.RegistryAccess; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.PrimaryLevelData; +import net.minecraft.world.level.storage.LevelDataAndDimensions; import org.bukkit.World; -import org.bukkit.craftbukkit.util.WorldUUID; +import org.bukkit.craftbukkit.block.CraftBiome; import org.bukkit.generator.WorldInfo; public class CraftWorldInfo implements WorldInfo { @@ -16,22 +18,29 @@ public class CraftWorldInfo implements WorldInfo { private final long seed; private final int minHeight; private final int maxHeight; - private final net.minecraft.world.flag.FeatureFlagSet enabledFeatures; // Paper - feature flag API - // Paper start - private final net.minecraft.world.level.chunk.ChunkGenerator vanillaChunkGenerator; - private final net.minecraft.core.RegistryAccess.Frozen registryAccess; + private final FeatureFlagSet enabledFeatures; + private final ChunkGenerator vanillaChunkGenerator; + private final RegistryAccess.Frozen registryAccess; - public CraftWorldInfo(PrimaryLevelData worldDataServer, LevelStorageSource.LevelStorageAccess session, World.Environment environment, DimensionType dimensionManager, net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator, net.minecraft.core.RegistryAccess.Frozen registryAccess) { - this.registryAccess = registryAccess; - this.vanillaChunkGenerator = chunkGenerator; - // Paper end - this.name = worldDataServer.getLevelName(); - this.uuid = WorldUUID.getOrCreate(session.levelDirectory.path().toFile()); + public CraftWorldInfo( + String name, + long seed, + FeatureFlagSet enabledFeatures, + World.Environment environment, + DimensionType dimensionManager, + ChunkGenerator vanillaChunkGenerator, + RegistryAccess.Frozen registryAccess, + UUID uuid + ) { + this.name = name; + this.seed = seed; + this.enabledFeatures = enabledFeatures; this.environment = environment; - this.seed = worldDataServer.worldGenOptions().seed(); this.minHeight = dimensionManager.minY(); this.maxHeight = dimensionManager.minY() + dimensionManager.height(); - this.enabledFeatures = worldDataServer.enabledFeatures(); // Paper - feature flag API + this.vanillaChunkGenerator = vanillaChunkGenerator; + this.registryAccess = registryAccess; + this.uuid = uuid; } @Override @@ -64,7 +73,6 @@ public int getMaxHeight() { return this.maxHeight; } - // Paper start @Override public org.bukkit.generator.BiomeProvider vanillaBiomeProvider() { final net.minecraft.world.level.levelgen.RandomState randomState; @@ -77,7 +85,7 @@ public org.bukkit.generator.BiomeProvider vanillaBiomeProvider() { } final java.util.List possibleBiomes = CraftWorldInfo.this.vanillaChunkGenerator.getBiomeSource().possibleBiomes().stream() - .map(biome -> org.bukkit.craftbukkit.block.CraftBiome.minecraftHolderToBukkit(biome)) + .map(CraftBiome::minecraftHolderToBukkit) .toList(); return new org.bukkit.generator.BiomeProvider() { @Override @@ -92,12 +100,9 @@ public java.util.List getBiomes(final org.bukkit.generat } }; } - // Paper end - // Paper start - feature flag API @Override public java.util.Set getFeatureFlags() { return io.papermc.paper.world.flag.PaperFeatureFlagProviderImpl.fromNms(this.enabledFeatures); } - // Paper end - feature flag API } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java index 73c51c66857f..9de9b760d111 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java @@ -5,9 +5,12 @@ import java.util.List; import java.util.Random; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; +import net.minecraft.core.QuartPos; import net.minecraft.core.RegistryAccess; +import net.minecraft.core.SectionPos; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.WorldGenRegion; @@ -44,6 +47,8 @@ public class CustomChunkGenerator extends InternalChunkGenerator { + private static final long INITIAL_SEED = 0; + private final net.minecraft.world.level.chunk.ChunkGenerator delegate; private final ChunkGenerator generator; private final ServerLevel world; @@ -54,10 +59,10 @@ public class CustomChunkGenerator extends InternalChunkGenerator { @Deprecated private class CustomBiomeGrid implements BiomeGrid { - private final ChunkAccess biome; + private final ChunkAccess chunk; - public CustomBiomeGrid(ChunkAccess biome) { - this.biome = biome; + public CustomBiomeGrid(ChunkAccess chunk) { + this.chunk = chunk; } @Override @@ -66,21 +71,22 @@ public Biome getBiome(int x, int z) { } @Override - public void setBiome(int x, int z, Biome bio) { + public void setBiome(int x, int z, Biome biome) { for (int y = CustomChunkGenerator.this.world.getWorld().getMinHeight(); y < CustomChunkGenerator.this.world.getWorld().getMaxHeight(); y += 4) { - this.setBiome(x, y, z, bio); + this.setBiome(x, y, z, biome); } } @Override public Biome getBiome(int x, int y, int z) { - return CraftBiome.minecraftHolderToBukkit(this.biome.getNoiseBiome(x >> 2, y >> 2, z >> 2)); + return CraftBiome.minecraftHolderToBukkit(this.chunk.getNoiseBiome(QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z))); } @Override - public void setBiome(int x, int y, int z, Biome bio) { - Preconditions.checkArgument(bio != Biome.CUSTOM, "Cannot set the biome to %s", bio); - this.biome.setBiome(x >> 2, y >> 2, z >> 2, CraftBiome.bukkitToMinecraftHolder(bio)); + public void setBiome(int x, int y, int z, Biome biome) { + Holder b = CraftBiome.bukkitToMinecraftHolder(biome); + Preconditions.checkArgument(b != null, "Cannot set the biome to %s", biome); + this.chunk.setNoiseBiome(QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z), b); } } @@ -97,7 +103,7 @@ public net.minecraft.world.level.chunk.ChunkGenerator getDelegate() { } private static WorldgenRandom getSeededRandom() { - return new WorldgenRandom(new LegacyRandomSource(0)); + return new WorldgenRandom(new LegacyRandomSource(INITIAL_SEED)); } @Override @@ -116,41 +122,41 @@ public int getSeaLevel() { } @Override - public void createStructures(RegistryAccess registryManager, ChunkGeneratorStructureState placementCalculator, StructureManager structureAccessor, ChunkAccess chunk, StructureTemplateManager structureTemplateManager, ResourceKey dimension) { + public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState state, StructureManager structureManager, ChunkAccess centerChunk, StructureTemplateManager structureTemplateManager, ResourceKey level) { WorldgenRandom random = CustomChunkGenerator.getSeededRandom(); - int x = chunk.getPos().x; - int z = chunk.getPos().z; + int x = centerChunk.getPos().x(); + int z = centerChunk.getPos().z(); random.setSeed(Mth.getSeed(x, "should-structures".hashCode(), z) ^ this.world.getSeed()); if (this.generator.shouldGenerateStructures(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z)) { - super.createStructures(registryManager, placementCalculator, structureAccessor, chunk, structureTemplateManager, dimension); + super.createStructures(registryAccess, state, structureManager, centerChunk, structureTemplateManager, level); } } @Override - public void buildSurface(WorldGenRegion region, StructureManager structures, RandomState noiseConfig, ChunkAccess chunk) { - WorldgenRandom random = CustomChunkGenerator.getSeededRandom(); - int x = chunk.getPos().x; - int z = chunk.getPos().z; + public void buildSurface(WorldGenRegion level, StructureManager structureManager, RandomState randomState, ChunkAccess protoChunk) { + WorldgenRandom random = getSeededRandom(); + int x = protoChunk.getPos().x(); + int z = protoChunk.getPos().z(); - random.setSeed(Mth.getSeed(x, "should-surface".hashCode(), z) ^ region.getSeed()); + random.setSeed(Mth.getSeed(x, "should-surface".hashCode(), z) ^ level.getSeed()); if (this.generator.shouldGenerateSurface(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z)) { - this.delegate.buildSurface(region, structures, noiseConfig, chunk); + this.delegate.buildSurface(level, structureManager, randomState, protoChunk); } - CraftChunkData chunkData = new CraftChunkData(this.world.getWorld(), chunk); + CraftChunkData chunkData = new CraftChunkData(this.world.getWorld(), protoChunk); - random.setSeed((long) x * 341873128712L + (long) z * 132897987541L); + random.setLargeFeatureWithSalt(INITIAL_SEED, x, z, 0); this.generator.generateSurface(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z, chunkData); - if (this.generator.shouldGenerateBedrock()) { - random = CustomChunkGenerator.getSeededRandom(); - random.setSeed((long) x * 341873128712L + (long) z * 132897987541L); - // delegate.buildBedrock(ichunkaccess, random); + if (false && this.generator.shouldGenerateBedrock()) { + random = getSeededRandom(); + random.setLargeFeatureWithSalt(INITIAL_SEED, x, z, 0); + // this.delegate.buildBedrock(protoChunk, random); } - random = CustomChunkGenerator.getSeededRandom(); - random.setSeed((long) x * 341873128712L + (long) z * 132897987541L); + random = getSeededRandom(); + random.setLargeFeatureWithSalt(INITIAL_SEED, x, z, 0); this.generator.generateBedrock(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z, chunkData); chunkData.breakLink(); @@ -164,15 +170,15 @@ public void buildSurface(WorldGenRegion region, StructureManager structures, Ran this.random.setSeed((long) x * 341873128712L + (long) z * 132897987541L); // Get default biome data for chunk - CustomBiomeGrid biomegrid = new CustomBiomeGrid(chunk); + CustomBiomeGrid biomeGrid = new CustomBiomeGrid(protoChunk); ChunkData data; try { if (this.generator.isParallelCapable()) { - data = this.generator.generateChunkData(this.world.getWorld(), this.random, x, z, biomegrid); + data = this.generator.generateChunkData(this.world.getWorld(), this.random, x, z, biomeGrid); } else { synchronized (this) { - data = this.generator.generateChunkData(this.world.getWorld(), this.random, x, z, biomegrid); + data = this.generator.generateChunkData(this.world.getWorld(), this.random, x, z, biomeGrid); } } } catch (UnsupportedOperationException exception) { @@ -184,28 +190,28 @@ public void buildSurface(WorldGenRegion region, StructureManager structures, Ran OldCraftChunkData craftData = (OldCraftChunkData) data; LevelChunkSection[] sections = craftData.getRawChunkData(); - LevelChunkSection[] csect = chunk.getSections(); - int scnt = Math.min(csect.length, sections.length); + LevelChunkSection[] protoSections = protoChunk.getSections(); + int sectionCount = Math.min(protoSections.length, sections.length); // Loop through returned sections - for (int sec = 0; sec < scnt; sec++) { - if (sections[sec] == null) { + for (int sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++) { + if (sections[sectionIndex] == null) { continue; } - LevelChunkSection section = sections[sec]; + LevelChunkSection section = sections[sectionIndex]; // SPIGOT-6843: Copy biomes over to new section. // Not the most performant way, but has a small footprint and developer should move to the new api anyway - LevelChunkSection oldSection = csect[sec]; + LevelChunkSection oldSection = protoSections[sectionIndex]; for (int biomeX = 0; biomeX < 4; biomeX++) { for (int biomeY = 0; biomeY < 4; biomeY++) { for (int biomeZ = 0; biomeZ < 4; biomeZ++) { - section.setBiome(biomeX, biomeY, biomeZ, oldSection.getNoiseBiome(biomeX, biomeY, biomeZ)); + section.setNoiseBiome(biomeX, biomeY, biomeZ, oldSection.getNoiseBiome(biomeX, biomeY, biomeZ)); } } } - csect[sec] = section; + protoSections[sectionIndex] = section; } if (craftData.getTiles() != null) { @@ -213,25 +219,27 @@ public void buildSurface(WorldGenRegion region, StructureManager structures, Ran int tx = pos.getX(); int ty = pos.getY(); int tz = pos.getZ(); - BlockState block = craftData.getTypeId(tx, ty, tz); - if (block.hasBlockEntity()) { - BlockEntity blockEntity = ((EntityBlock) block.getBlock()).newBlockEntity(new BlockPos((x << 4) + tx, ty, (z << 4) + tz), block); - chunk.setBlockEntity(blockEntity); + BlockState state = craftData.getBlockState(tx, ty, tz); + if (state.hasBlockEntity()) { + BlockEntity blockEntity = ((EntityBlock) state.getBlock()).newBlockEntity(new BlockPos((x << 4) + tx, ty, (z << 4) + tz), state); + if (blockEntity != null) { + protoChunk.setBlockEntity(blockEntity); + } } } } } @Override - public void applyCarvers(WorldGenRegion chunkRegion, long seed, RandomState noiseConfig, BiomeManager biomeAccess, StructureManager structureAccessor, ChunkAccess chunk) { - WorldgenRandom random = CustomChunkGenerator.getSeededRandom(); - int x = chunk.getPos().x; - int z = chunk.getPos().z; + public void applyCarvers(WorldGenRegion region, long seed, RandomState randomState, BiomeManager biomeManager, StructureManager structureManager, ChunkAccess chunk) { + WorldgenRandom random = getSeededRandom(); + int x = chunk.getPos().x(); + int z = chunk.getPos().z(); - random.setSeed(Mth.getSeed(x, "should-caves".hashCode(), z) ^ chunkRegion.getSeed()); + random.setSeed(Mth.getSeed(x, "should-caves".hashCode(), z) ^ region.getSeed()); if (this.generator.shouldGenerateCaves(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z)) { - this.delegate.applyCarvers(chunkRegion, seed, noiseConfig, biomeAccess, structureAccessor, chunk); + this.delegate.applyCarvers(region, seed, randomState, biomeManager, structureManager, chunk); } // Minecraft removed the LIQUID_CARVERS stage from world generation, without removing the LIQUID Carving enum. @@ -244,82 +252,80 @@ public void applyCarvers(WorldGenRegion chunkRegion, long seed, RandomState nois } @Override - public CompletableFuture fillFromNoise(Blender blender, RandomState noiseConfig, StructureManager structureAccessor, ChunkAccess chunk) { + public CompletableFuture fillFromNoise(Blender blender, RandomState randomState, StructureManager structureManager, ChunkAccess centerChunk) { CompletableFuture future = null; - WorldgenRandom random = CustomChunkGenerator.getSeededRandom(); - int x = chunk.getPos().x; - int z = chunk.getPos().z; + WorldgenRandom random = getSeededRandom(); + int x = centerChunk.getPos().x(); + int z = centerChunk.getPos().z(); random.setSeed(Mth.getSeed(x, "should-noise".hashCode(), z) ^ this.world.getSeed()); if (this.generator.shouldGenerateNoise(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z)) { - future = this.delegate.fillFromNoise(blender, noiseConfig, structureAccessor, chunk); + future = this.delegate.fillFromNoise(blender, randomState, structureManager, centerChunk); } - java.util.function.Function function = (ichunkaccess1) -> { - CraftChunkData chunkData = new CraftChunkData(this.world.getWorld(), ichunkaccess1); - random.setSeed((long) x * 341873128712L + (long) z * 132897987541L); + Function work = chunk -> { + CraftChunkData chunkData = new CraftChunkData(this.world.getWorld(), chunk); + random.setLargeFeatureWithSalt(INITIAL_SEED, x, z, 0); this.generator.generateNoise(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z, chunkData); chunkData.breakLink(); - return ichunkaccess1; + return chunk; }; - return future == null ? CompletableFuture.supplyAsync(() -> function.apply(chunk), io.papermc.paper.FeatureHooks.getWorldgenExecutor()) : future.thenApply(function); // Paper - chunk system + return future == null ? CompletableFuture.supplyAsync(() -> work.apply(centerChunk), io.papermc.paper.FeatureHooks.getWorldgenExecutor()) : future.thenApply(work); // Paper - chunk system } @Override - public int getBaseHeight(int x, int z, Heightmap.Types heightmap, LevelHeightAccessor world, RandomState noiseConfig) { + public int getBaseHeight(int x, int z, Heightmap.Types type, LevelHeightAccessor heightAccessor, RandomState randomState) { if (this.implementBaseHeight) { try { - WorldgenRandom random = CustomChunkGenerator.getSeededRandom(); - int xChunk = x >> 4; - int zChunk = z >> 4; - random.setSeed((long) xChunk * 341873128712L + (long) zChunk * 132897987541L); + WorldgenRandom random = getSeededRandom(); + random.setLargeFeatureWithSalt(INITIAL_SEED, SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z), 0); - return this.generator.getBaseHeight(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z, CraftHeightMap.fromNMS(heightmap)); + return this.generator.getBaseHeight(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z, CraftHeightMap.fromNMS(type)); } catch (UnsupportedOperationException exception) { this.implementBaseHeight = false; } } - return this.delegate.getBaseHeight(x, z, heightmap, world, noiseConfig); + return this.delegate.getBaseHeight(x, z, type, heightAccessor, randomState); } @Override - public WeightedList getMobsAt(Holder biome, StructureManager accessor, MobCategory group, BlockPos pos) { - return this.delegate.getMobsAt(biome, accessor, group, pos); + public WeightedList getMobsAt(Holder biome, StructureManager structureManager, MobCategory mobCategory, BlockPos pos) { + return this.delegate.getMobsAt(biome, structureManager, mobCategory, pos); } @Override public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) { - WorldgenRandom random = CustomChunkGenerator.getSeededRandom(); - int x = chunk.getPos().x; - int z = chunk.getPos().z; + WorldgenRandom random = getSeededRandom(); + int x = chunk.getPos().x(); + int z = chunk.getPos().z(); random.setSeed(Mth.getSeed(x, "should-decoration".hashCode(), z) ^ level.getSeed()); super.applyBiomeDecoration(level, chunk, structureManager, this.generator.shouldGenerateDecorations(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z)); } @Override - public void addDebugScreenInfo(List text, RandomState noiseConfig, BlockPos pos) { - this.delegate.addDebugScreenInfo(text, noiseConfig, pos); + public void addDebugScreenInfo(List result, RandomState randomState, BlockPos feetPos) { + this.delegate.addDebugScreenInfo(result, randomState, feetPos); } @Override - public void spawnOriginalMobs(WorldGenRegion region) { - WorldgenRandom random = CustomChunkGenerator.getSeededRandom(); - int x = region.getCenter().x; - int z = region.getCenter().z; + public void spawnOriginalMobs(WorldGenRegion worldGenRegion) { + WorldgenRandom random = getSeededRandom(); + int x = worldGenRegion.getCenter().x(); + int z = worldGenRegion.getCenter().z(); - random.setSeed(Mth.getSeed(x, "should-mobs".hashCode(), z) ^ region.getSeed()); + random.setSeed(Mth.getSeed(x, "should-mobs".hashCode(), z) ^ worldGenRegion.getSeed()); if (this.generator.shouldGenerateMobs(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z)) { - this.delegate.spawnOriginalMobs(region); + this.delegate.spawnOriginalMobs(worldGenRegion); } } @Override - public int getSpawnHeight(LevelHeightAccessor world) { - return this.delegate.getSpawnHeight(world); + public int getSpawnHeight(LevelHeightAccessor heightAccessor) { + return this.delegate.getSpawnHeight(heightAccessor); } @Override @@ -328,8 +334,8 @@ public int getGenDepth() { } @Override - public NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor world, RandomState noiseConfig) { - return this.delegate.getBaseColumn(x, z, world, noiseConfig); + public NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor heightAccessor, RandomState randomState) { + return this.delegate.getBaseColumn(x, z, heightAccessor, randomState); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java index 0bac128d6faf..f1b3286a83c7 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java @@ -2,14 +2,11 @@ import com.google.common.base.Preconditions; import com.mojang.serialization.MapCodec; -import java.util.ArrayList; -import java.util.List; import java.util.stream.Stream; import net.minecraft.core.Holder; -import net.minecraft.core.Registry; +import net.minecraft.core.QuartPos; import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.biome.Climate; -import org.bukkit.block.Biome; import org.bukkit.craftbukkit.block.CraftBiome; import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.WorldInfo; @@ -18,27 +15,12 @@ public class CustomWorldChunkManager extends BiomeSource { private final WorldInfo worldInfo; private final BiomeProvider biomeProvider; - private final Registry registry; - - private static List> biomeListToBiomeBaseList(List biomes, Registry registry) { - List> biomeBases = new ArrayList<>(); - - for (Biome biome : biomes) { - Preconditions.checkArgument(biome != Biome.CUSTOM, "Cannot use the biome %s", biome); - biomeBases.add(CraftBiome.bukkitToMinecraftHolder(biome)); - } - - return biomeBases; - } - - // Paper start - add vanillaBiomeProvider public final BiomeSource vanillaBiomeSource; - public CustomWorldChunkManager(WorldInfo worldInfo, BiomeProvider biomeProvider, Registry registry, BiomeSource vanillaBiomeSource) { - this.vanillaBiomeSource = vanillaBiomeSource; - // Paper end - add vanillaBiomeProvider + + public CustomWorldChunkManager(WorldInfo worldInfo, BiomeProvider biomeProvider, BiomeSource vanillaBiomeSource) { this.worldInfo = worldInfo; this.biomeProvider = biomeProvider; - this.registry = registry; + this.vanillaBiomeSource = vanillaBiomeSource; } @Override @@ -48,14 +30,20 @@ protected MapCodec codec() { @Override public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler noise) { - Biome biome = this.biomeProvider.getBiome(this.worldInfo, x << 2, y << 2, z << 2, CraftBiomeParameterPoint.createBiomeParameterPoint(noise, noise.sample(x, y, z))); - Preconditions.checkArgument(biome != Biome.CUSTOM, "Cannot set the biome to %s", biome); + Holder biome = CraftBiome.bukkitToMinecraftHolder( + this.biomeProvider.getBiome(this.worldInfo, QuartPos.toBlock(x), QuartPos.toBlock(y), QuartPos.toBlock(z), CraftBiomeParameterPoint.createBiomeParameterPoint(noise, noise.sample(x, y, z))) + ); + Preconditions.checkArgument(biome != null, "Cannot set the biome to %s", biome); - return CraftBiome.bukkitToMinecraftHolder(biome); + return biome; } @Override protected Stream> collectPossibleBiomes() { - return CustomWorldChunkManager.biomeListToBiomeBaseList(this.biomeProvider.getBiomes(this.worldInfo), this.registry).stream(); + return this.biomeProvider.getBiomes(this.worldInfo).stream().map(biome -> { + Holder b = CraftBiome.bukkitToMinecraftHolder(biome); + Preconditions.checkArgument(b != null, "Cannot use the biome %s", biome); + return b; + }); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java index 7b4bad15d6c3..06277f44a061 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java @@ -6,7 +6,7 @@ import net.minecraft.world.level.biome.BiomeGenerationSettings; import net.minecraft.world.level.biome.BiomeSource; -// Do not implement functions to this class, add to NormalChunkGenerator +// Do not implement functions to this class, add to CustomChunkGenerator public abstract class InternalChunkGenerator extends net.minecraft.world.level.chunk.ChunkGenerator { public InternalChunkGenerator(BiomeSource biomeSource, Function, BiomeGenerationSettings> generationSettingsGetter) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java index b4feddacbd64..463a3234d3da 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java @@ -36,10 +36,10 @@ public OldCraftChunkData(int minHeight, int maxHeight, PalettedContainerFactory } public OldCraftChunkData(int minHeight, int maxHeight, PalettedContainerFactory palettedContainerFactory, org.bukkit.World world) { - this.world = world; this.minHeight = minHeight; this.maxHeight = maxHeight; this.palettedContainerFactory = palettedContainerFactory; + this.world = world; this.sections = new LevelChunkSection[(((maxHeight - 1) >> 4) + 1) - (minHeight >> 4)]; } @@ -90,17 +90,17 @@ public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax @Override public Material getType(int x, int y, int z) { - return CraftBlockType.minecraftToBukkit(this.getTypeId(x, y, z).getBlock()); + return CraftBlockType.minecraftToBukkit(this.getBlockState(x, y, z).getBlock()); } @Override public MaterialData getTypeAndData(int x, int y, int z) { - return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z)); + return CraftMagicNumbers.getMaterial(this.getBlockState(x, y, z)); } @Override public BlockData getBlockData(int x, int y, int z) { - return CraftBlockData.fromData(this.getTypeId(x, y, z)); + return this.getBlockState(x, y, z).asBlockData(); } public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockState type) { @@ -140,7 +140,7 @@ public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax } } - public BlockState getTypeId(int x, int y, int z) { + public BlockState getBlockState(int x, int y, int z) { if (x != (x & 0xf) || y < this.minHeight || y >= this.maxHeight || z != (z & 0xf)) { return Blocks.AIR.defaultBlockState(); } @@ -154,7 +154,7 @@ public BlockState getTypeId(int x, int y, int z) { @Override public byte getData(int x, int y, int z) { - return CraftMagicNumbers.toLegacyData(this.getTypeId(x, y, z)); + return CraftMagicNumbers.toLegacyData(this.getBlockState(x, y, z)); } private void setBlock(int x, int y, int z, BlockState type) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftBlastingRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftBlastingRecipe.java index 24edf4f02e54..3e39a5172c06 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftBlastingRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftBlastingRecipe.java @@ -1,8 +1,10 @@ package org.bukkit.craftbukkit.inventory; +import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; import net.minecraft.world.item.crafting.RecipeHolder; import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.BlastingRecipe; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.RecipeChoice; @@ -23,9 +25,15 @@ public static CraftBlastingRecipe fromBukkitRecipe(BlastingRecipe recipe) { } @Override - public void addToCraftingManager() { - ItemStack result = this.getResult(); - - MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.BlastingRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), this.toNMS(this.getInputChoice(), true), CraftItemStack.asNMSCopy(result), this.getExperience(), this.getCookingTime()))); + public void addToRecipeManager() { + net.minecraft.world.item.crafting.BlastingRecipe recipe = new net.minecraft.world.item.crafting.BlastingRecipe( + new net.minecraft.world.item.crafting.Recipe.CommonInfo(true), + new net.minecraft.world.item.crafting.AbstractCookingRecipe.CookingBookInfo(CraftRecipe.getCategory(this.getCategory()), this.getGroup()), + CraftRecipe.toIngredient(this.getInputChoice(), true), + CraftItemStack.asTemplate(this.getResult()), + this.getExperience(), + this.getCookingTime() + ); + MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toResourceKey(Registries.RECIPE, this.getKey()), recipe)); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftCampfireRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftCampfireRecipe.java index c675facb0cae..00a65754b649 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftCampfireRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftCampfireRecipe.java @@ -1,8 +1,11 @@ package org.bukkit.craftbukkit.inventory; +import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; +import net.minecraft.world.item.crafting.CampfireCookingRecipe; import net.minecraft.world.item.crafting.RecipeHolder; import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.CampfireRecipe; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.RecipeChoice; @@ -23,9 +26,15 @@ public static CraftCampfireRecipe fromBukkitRecipe(CampfireRecipe recipe) { } @Override - public void addToCraftingManager() { - ItemStack result = this.getResult(); - - MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.CampfireCookingRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), this.toNMS(this.getInputChoice(), true), CraftItemStack.asNMSCopy(result), this.getExperience(), this.getCookingTime()))); + public void addToRecipeManager() { + CampfireCookingRecipe recipe = new net.minecraft.world.item.crafting.CampfireCookingRecipe( + new net.minecraft.world.item.crafting.Recipe.CommonInfo(true), + new net.minecraft.world.item.crafting.AbstractCookingRecipe.CookingBookInfo(CraftRecipe.getCategory(this.getCategory()), this.getGroup()), + CraftRecipe.toIngredient(this.getInputChoice(), true), + CraftItemStack.asTemplate(this.getResult()), + this.getExperience(), + this.getCookingTime() + ); + MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toResourceKey(Registries.RECIPE, this.getKey()), recipe)); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftComplexRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftComplexRecipe.java index 13e879ecd5dd..0040ae7cd63c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftComplexRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftComplexRecipe.java @@ -1,24 +1,25 @@ package org.bukkit.craftbukkit.inventory; +import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; -import net.minecraft.world.item.crafting.CustomRecipe; import net.minecraft.world.item.crafting.RecipeHolder; import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.ComplexRecipe; import org.bukkit.inventory.CraftingRecipe; import org.bukkit.inventory.ItemStack; public class CraftComplexRecipe extends CraftingRecipe implements CraftRecipe, ComplexRecipe { - private final CustomRecipe recipe; + private final net.minecraft.world.item.crafting.CraftingRecipe recipe; - public CraftComplexRecipe(NamespacedKey key, ItemStack result, CustomRecipe recipe) { + public CraftComplexRecipe(NamespacedKey key, ItemStack result, net.minecraft.world.item.crafting.CraftingRecipe recipe) { super(key, result); this.recipe = recipe; } @Override - public void addToCraftingManager() { - MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), this.recipe)); + public void addToRecipeManager() { + MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toResourceKey(Registries.RECIPE, this.getKey()), this.recipe)); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftFurnaceRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftFurnaceRecipe.java index 875d6793b4f3..658af5f3aeaa 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftFurnaceRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftFurnaceRecipe.java @@ -1,8 +1,11 @@ package org.bukkit.craftbukkit.inventory; +import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.SmeltingRecipe; import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.FurnaceRecipe; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.RecipeChoice; @@ -23,9 +26,15 @@ public static CraftFurnaceRecipe fromBukkitRecipe(FurnaceRecipe recipe) { } @Override - public void addToCraftingManager() { - ItemStack result = this.getResult(); - - MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.SmeltingRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), this.toNMS(this.getInputChoice(), true), CraftItemStack.asNMSCopy(result), this.getExperience(), this.getCookingTime()))); + public void addToRecipeManager() { + SmeltingRecipe recipe = new net.minecraft.world.item.crafting.SmeltingRecipe( + new net.minecraft.world.item.crafting.Recipe.CommonInfo(true), + new net.minecraft.world.item.crafting.AbstractCookingRecipe.CookingBookInfo(CraftRecipe.getCategory(this.getCategory()), this.getGroup()), + CraftRecipe.toIngredient(this.getInputChoice(), true), + CraftItemStack.asTemplate(this.getResult()), + this.getExperience(), + this.getCookingTime() + ); + MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toResourceKey(Registries.RECIPE, this.getKey()), recipe)); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java index 680468e6c16f..290c4229c86a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java @@ -6,7 +6,6 @@ import java.util.List; import net.minecraft.core.NonNullList; import net.minecraft.world.Container; -import net.minecraft.world.entity.ContainerUser; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import org.bukkit.Location; @@ -209,19 +208,6 @@ public InventoryHolder getOwner() { return this.owner; } - @Override - public boolean canPlaceItem(int slot, ItemStack stack) { - return true; - } - - @Override - public void startOpen(ContainerUser player) { - } - - @Override - public void stopOpen(ContainerUser player) { - } - @Override public void clearContent() { this.items.clear(); @@ -242,8 +228,7 @@ public String getTitle() { @Override public boolean isEmpty() { - Iterator iterator = this.items.iterator(); - + Iterator iterator = this.items.iterator(); ItemStack itemstack; do { @@ -251,7 +236,7 @@ public boolean isEmpty() { return true; } - itemstack = (ItemStack) iterator.next(); + itemstack = iterator.next(); } while (itemstack.isEmpty()); return false; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java index 5d9dc50e88e2..5b764e9caf85 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java @@ -1,25 +1,29 @@ package org.bukkit.craftbukkit.inventory; import com.google.common.base.Preconditions; +import net.minecraft.network.chat.Component; import net.minecraft.world.CompoundContainer; import net.minecraft.world.MenuProvider; -import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; import org.bukkit.Location; import org.bukkit.block.DoubleChest; import org.bukkit.inventory.DoubleChestInventory; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.jspecify.annotations.Nullable; public class CraftInventoryDoubleChest extends CraftInventory implements DoubleChestInventory { + public MenuProvider provider; private final CraftInventory left; private final CraftInventory right; - public CraftInventoryDoubleChest(ChestBlock.DoubleInventory inventory) { - super(inventory.container); - this.provider = inventory; - this.left = new CraftInventory(inventory.container.container1); - this.right = new CraftInventory(inventory.container.container2); + public CraftInventoryDoubleChest(Provider provider) { + super(provider.container); + this.provider = provider; + this.left = new CraftInventory(provider.container.container1); + this.right = new CraftInventory(provider.container.container2); } public CraftInventoryDoubleChest(CompoundContainer largeChest) { @@ -74,4 +78,30 @@ public DoubleChest getHolder(boolean useSnapshot) { public Location getLocation() { return this.getLeftSide().getLocation().add(this.getRightSide().getLocation()).multiply(0.5); } + + public static class Provider implements MenuProvider { + + private final MenuProvider delegate; + public final CompoundContainer container; // expose to api + + private Provider(MenuProvider delegate, CompoundContainer container) { + this.delegate = delegate; + this.container = container; + } + + public static Provider wrap(MenuProvider delegate, CompoundContainer container) { + return new Provider(delegate, container); + } + + @Nullable + @Override + public AbstractContainerMenu createMenu(int containerId, net.minecraft.world.entity.player.Inventory inventory, Player player) { + return this.delegate.createMenu(containerId, inventory, player); + } + + @Override + public Component getDisplayName() { + return this.delegate.getDisplayName(); + } + } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java index d8aae267a038..825cfd83982a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java @@ -1,7 +1,6 @@ package org.bukkit.craftbukkit.inventory; import com.google.common.base.Preconditions; -import java.util.List; import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; import net.minecraft.network.protocol.game.ClientboundSetHeldSlotPacket; import net.minecraft.network.protocol.game.ClientboundSetPlayerInventoryPacket; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemCraftResult.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemCraftResult.java index 447efe5e3bcb..03a6720da283 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemCraftResult.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemCraftResult.java @@ -2,8 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import org.bukkit.Material; import org.bukkit.inventory.ItemCraftResult; import org.bukkit.inventory.ItemStack; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java index 4abee7b6d922..70e21e1fc337 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java @@ -3,12 +3,11 @@ import com.google.common.base.Preconditions; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import java.util.Optional; import io.papermc.paper.registry.data.util.Conversions; +import java.util.Optional; import net.minecraft.commands.arguments.item.ItemParser; import net.minecraft.core.HolderSet; import net.minecraft.core.RegistryAccess; -import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.registries.Registries; import net.minecraft.resources.Identifier; import net.minecraft.tags.EnchantmentTags; @@ -154,18 +153,11 @@ public Color getDefaultLeatherColor() { public ItemStack createItemStack(String input) throws IllegalArgumentException { try { StringReader reader = new StringReader(input); - ItemParser.ItemResult arg = new ItemParser(CraftRegistry.getMinecraftRegistry()).parse(reader); - Preconditions.checkArgument(!reader.canRead(), "Trailing input found when parsing ItemStack: %s", input); - - Item item = arg.item().value(); - net.minecraft.world.item.ItemStack nmsItemStack = new net.minecraft.world.item.ItemStack(item); - - DataComponentPatch nbt = arg.components(); - if (nbt != null) { - nmsItemStack.applyComponents(nbt); + net.minecraft.commands.arguments.item.ItemInput in = new ItemParser(CraftRegistry.getMinecraftRegistry()).parse(reader); + if (reader.canRead()) { + throw new IllegalArgumentException("Trailing input found when parsing ItemStack: " + reader.getRemaining()); } - - return CraftItemStack.asCraftMirror(nmsItemStack); + return CraftItemStack.asCraftMirror(in.createItemStack(1)); } catch (CommandSyntaxException ex) { throw new IllegalArgumentException("Could not parse ItemStack: " + input, ex); } @@ -177,32 +169,26 @@ public Material getSpawnEgg(EntityType type) { return null; } net.minecraft.world.entity.EntityType nmsType = CraftEntityType.bukkitToMinecraft(type); - Item nmsItem = SpawnEggItem.byId(nmsType); - - if (nmsItem == null) { - return null; - } - - return CraftItemType.minecraftToBukkit(nmsItem); + return SpawnEggItem.byId(nmsType).map(item -> CraftItemType.minecraftHolderToBukkitNew(item).asMaterial()).orElse(null); } @Override public ItemStack enchantItem(Entity entity, ItemStack itemStack, int level, boolean allowTreasures) { Preconditions.checkArgument(entity != null, "The entity must not be null"); - return CraftItemFactory.enchantItem(((CraftEntity) entity).getHandle().random, itemStack, level, allowTreasures); + return enchantItem(((CraftEntity) entity).getHandle().getRandom(), itemStack, level, allowTreasures); } @Override public ItemStack enchantItem(final World world, final ItemStack itemStack, final int level, final boolean allowTreasures) { Preconditions.checkArgument(world != null, "The world must not be null"); - return CraftItemFactory.enchantItem(((CraftWorld) world).getHandle().random, itemStack, level, allowTreasures); + return enchantItem(((CraftWorld) world).getHandle().getRandom(), itemStack, level, allowTreasures); } @Override public ItemStack enchantItem(final ItemStack itemStack, final int level, final boolean allowTreasures) { - return CraftItemFactory.enchantItem(CraftItemFactory.randomSource, itemStack, level, allowTreasures); + return enchantItem(CraftItemFactory.randomSource, itemStack, level, allowTreasures); } private static ItemStack enchantItem(RandomSource source, ItemStack itemStack, int level, boolean allowTreasures) { @@ -304,8 +290,7 @@ public ItemStack getSpawnEgg0(org.bukkit.entity.EntityType type) { String typeId = type.getKey().toString(); net.minecraft.resources.Identifier typeKey = Identifier.parse(typeId); net.minecraft.world.entity.EntityType nmsType = net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getValue(typeKey); - net.minecraft.world.item.SpawnEggItem eggItem = net.minecraft.world.item.SpawnEggItem.byId(nmsType); - return eggItem == null ? null : new net.minecraft.world.item.ItemStack(eggItem).asBukkitMirror(); + return net.minecraft.world.item.SpawnEggItem.byId(nmsType).map(net.minecraft.world.item.ItemStack::new).map(net.minecraft.world.item.ItemStack::asBukkitMirror).orElse(null); } // Paper end - old getSpawnEgg API // Paper start - enchantWithLevels API diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java index 002682049fe8..22f2a8c1ee40 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -1,32 +1,30 @@ package org.bukkit.craftbukkit.inventory; import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMap; import io.papermc.paper.adventure.PaperAdventure; -import java.util.Collections; import java.util.Map; -import java.util.Optional; import java.util.function.Consumer; import java.util.function.Predicate; import net.kyori.adventure.text.Component; import net.minecraft.advancements.criterion.DataComponentMatchers; import net.minecraft.advancements.criterion.ItemPredicate; -import net.minecraft.advancements.criterion.MinMaxBounds; import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; import net.minecraft.core.component.DataComponentExactPredicate; import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; import net.minecraft.core.component.PatchedDataComponentMap; +import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.item.enchantment.ItemEnchantments; import org.bukkit.Material; import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.enchantments.CraftEnchantment; import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; import org.bukkit.craftbukkit.util.CraftMagicNumbers; @@ -36,6 +34,7 @@ import org.bukkit.material.MaterialData; import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @DelegateDeserialization(ItemStack.class) public final class CraftItemStack extends ItemStack { @@ -91,8 +90,8 @@ public static net.minecraft.world.item.ItemStack unwrap(ItemStack bukkit) { // Paper end - re-implement after delegating all api ItemStack calls to CraftItemStack } - public static net.minecraft.world.item.ItemStack getOrCloneOnMutation(ItemStack old, ItemStack newInstance) { - return old == newInstance ? unwrap(old) : asNMSCopy(newInstance); + public static net.minecraft.world.item.ItemStack getOrCloneOnMutation(ItemStack initial, ItemStack current) { + return initial == current ? unwrap(initial) : asNMSCopy(current); } // Paper end - MC Utils @@ -103,7 +102,7 @@ public boolean isEmpty() { } // Paper end - override isEmpty to use vanilla's impl - public static net.minecraft.world.item.ItemStack asNMSCopy(ItemStack original) { + public static net.minecraft.world.item.ItemStack asNMSCopy(@Nullable ItemStack original) { // Paper start - re-implement after delegating all api ItemStack calls to CraftItemStack if (original == null || original.isEmpty()) { return net.minecraft.world.item.ItemStack.EMPTY; @@ -121,6 +120,10 @@ public static java.util.List asNMSCopy(java. return items; } + public static ItemStackTemplate asTemplate(ItemStack bukkit) { + return net.minecraft.world.item.ItemStackTemplate.fromNonEmptyStack(asNMSCopy(bukkit)); + } + public static net.minecraft.world.item.ItemStack copyNMSStack(net.minecraft.world.item.ItemStack original, int amount) { net.minecraft.world.item.ItemStack stack = original.copy(); stack.setCount(amount); @@ -131,10 +134,13 @@ public static net.minecraft.world.item.ItemStack copyNMSStack(net.minecraft.worl * Copies the NMS stack to return as a strictly-Bukkit stack */ public static ItemStack asBukkitCopy(net.minecraft.world.item.ItemStack original) { - // Paper start - no such thing as a "strictly-Bukkit stack" anymore + // no such thing as a "strictly-Bukkit stack" anymore // we copy the stack since it should be a complete copy not a mirror return asCraftMirror(original.copy()); - // Paper end + } + + public static ItemStack asBukkitCopy(net.minecraft.world.item.ItemStackTemplate original) { + return asBukkitCopy(original.create()); } public static CraftItemStack asCraftMirror(net.minecraft.world.item.ItemStack original) { @@ -157,18 +163,21 @@ public static CraftItemStack asNewCraftStack(Item item, int amount) { return new CraftItemStack(CraftItemType.minecraftToBukkit(item), amount, (short) 0, null); } - public static ItemPredicate asCriterionConditionItem(ItemStack original) { - net.minecraft.world.item.ItemStack nms = CraftItemStack.asNMSCopy(original); - DataComponentExactPredicate predicate = DataComponentExactPredicate.allOf(PatchedDataComponentMap.fromPatch(DataComponentMap.EMPTY, nms.getComponentsPatch())); + public static ItemPredicate asCriterionConditionItem(ItemStack key) { + net.minecraft.world.item.ItemStack item = CraftItemStack.unwrap(key); - return new ItemPredicate(Optional.of(HolderSet.direct(nms.getItemHolder())), MinMaxBounds.Ints.ANY, new DataComponentMatchers(predicate, Collections.emptyMap())); + return ItemPredicate.Builder.item() + .of(CraftRegistry.getMinecraftRegistry(Registries.ITEM), item.getItem()) + .withComponents(DataComponentMatchers.Builder.components() + .exact(DataComponentExactPredicate.allOf( + PatchedDataComponentMap.fromPatch(DataComponentMap.EMPTY, item.getComponentsPatch()) + )) + .build()) + .build(); } public net.minecraft.world.item.ItemStack handle; - /** - * Mirror - */ private CraftItemStack(net.minecraft.world.item.ItemStack item) { this.handle = item; } @@ -349,8 +358,8 @@ public void adjustTagForItemMeta(final Material oldType) { if (oldMeta == null) { newMeta = getItemMeta(this.handle); } else { - final java.util.Set> extraHandledDcts = new java.util.HashSet<>(CraftMetaItem.getTopLevelHandledDcts(oldMeta.getClass())); - newMeta = getItemMeta(this.handle, CraftItemType.minecraftToBukkitNew(this.handle.getItem()), extraHandledDcts); + final java.util.Set> extraHandledComponents = new java.util.HashSet<>(CraftMetaItem.getTopLevelHandledComponents(oldMeta.getClass())); + newMeta = getItemMeta(this.handle, CraftItemType.minecraftToBukkitNew(this.handle.getItem()), extraHandledComponents); } this.setItemMeta(newMeta); } @@ -377,14 +386,14 @@ public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item, org. // Paper start - handled tags on type change return getItemMeta(item, metaForType, null); } - public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item, org.bukkit.inventory.ItemType metaForType, final java.util.Set> extraHandledDcts) { + public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item, org.bukkit.inventory.ItemType metaForType, final java.util.Set> extraHandledComponents) { // Paper end - handled tags on type change if (!CraftItemStack.hasItemMeta(item)) { return CraftItemFactory.instance().getItemMeta(CraftItemStack.getType(item)); } - if (metaForType != null) { return ((CraftItemType) metaForType).getItemMeta(item, extraHandledDcts); } // Paper - return ((CraftItemType) CraftItemType.minecraftToBukkitNew(item.getItem())).getItemMeta(item, extraHandledDcts); // Paper + if (metaForType != null) { return ((CraftItemType) metaForType).getItemMeta(item, extraHandledComponents); } // Paper + return ((CraftItemType) CraftItemType.minecraftToBukkitNew(item.getItem())).getItemMeta(item, extraHandledComponents); // Paper } static Material getType(net.minecraft.world.item.ItemStack item) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java index ae571d4de2b4..045613845bcc 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java @@ -13,6 +13,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.component.ItemAttributeModifiers; import net.minecraft.world.level.block.ComposterBlock; import net.minecraft.world.level.block.entity.FuelValues; @@ -58,6 +59,14 @@ public static Item bukkitToMinecraftNew(ItemType bukkit) { return CraftRegistry.bukkitToMinecraft(bukkit); } + public static ItemType minecraftHolderToBukkitNew(Holder minecraft) { + return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.ITEM); + } + + public static Holder bukkitToMinecraftHolderNew(ItemType bukkit) { + return CraftRegistry.bukkitToMinecraftHolder(bukkit); + } + public CraftItemType(final Holder holder) { super(holder); this.itemMetaData = Suppliers.memoize(() -> CraftItemMetas.getItemMetaData(this)); @@ -101,8 +110,8 @@ public ItemStack createItemStack(final int amount, final @Nullable Consumer> extraHandledDcts) { - return this.itemMetaData.get().fromItemStack().apply(itemStack, extraHandledDcts); + public M getItemMeta(net.minecraft.world.item.ItemStack itemStack, final java.util.Set> extraHandledComponents) { + return this.itemMetaData.get().fromItemStack().apply(itemStack, extraHandledComponents); } public M getItemMeta(ItemMeta itemMeta) { @@ -181,8 +190,8 @@ public float getCompostChance() { @Override public @Nullable ItemType getCraftingRemainingItem() { - net.minecraft.world.item.ItemStack expectedItem = this.getHandle().getCraftingRemainder(); - return expectedItem.isEmpty() ? null : CraftItemType.minecraftToBukkitNew(expectedItem.getItem()); + ItemStackTemplate craftingRemainder = this.getHandle().getCraftingRemainder(); + return craftingRemainder == null ? null : CraftItemType.minecraftHolderToBukkitNew(craftingRemainder.item()); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java index 72b5f6724278..989bc9b69da6 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java @@ -2,7 +2,6 @@ import com.google.common.base.Function; import com.google.common.collect.Lists; -import java.util.Collections; import java.util.List; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.trading.MerchantOffers; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java index 71c14b8c4d46..6f8f42d359ff 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java @@ -11,6 +11,7 @@ import net.minecraft.world.item.trading.MerchantOffer; import net.minecraft.world.item.trading.MerchantOffers; import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.jspecify.annotations.Nullable; public class CraftMerchantCustom implements CraftMerchant { @@ -83,7 +84,7 @@ public MerchantOffers getOffers() { // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent @Override - public void processTrade(MerchantOffer offer, @javax.annotation.Nullable io.papermc.paper.event.player.PlayerPurchaseEvent event) { // The MerchantRecipe passed in here is the one set by the PlayerPurchaseEvent + public void processTrade(MerchantOffer offer, io.papermc.paper.event.player.@Nullable PlayerPurchaseEvent event) { // The MerchantRecipe passed in here is the one set by the PlayerPurchaseEvent /* Based on {@link net.minecraft.world.entity.npc.villager.AbstractVillager#processTrade(MerchantOffer, io.papermc.paper.event.player.PlayerPurchaseEvent)} */ if (getTradingPlayer() instanceof net.minecraft.server.level.ServerPlayer) { if (event == null || event.willIncreaseTradeUses()) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantRecipe.java index 4a4e0d629b6b..1d381afe5c2d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantRecipe.java @@ -140,11 +140,11 @@ public net.minecraft.world.item.trading.MerchantOffer toMinecraft() { Preconditions.checkState(!ingredients.isEmpty(), "No offered ingredients"); net.minecraft.world.item.ItemStack baseCostA = CraftItemStack.asNMSCopy(ingredients.get(0)); DataComponentExactPredicate baseCostAPredicate = DataComponentExactPredicate.allOf(PatchedDataComponentMap.fromPatch(DataComponentMap.EMPTY, baseCostA.getComponentsPatch())); - this.handle.baseCostA = new ItemCost(baseCostA.getItemHolder(), baseCostA.getCount(), baseCostAPredicate, baseCostA); + this.handle.baseCostA = new ItemCost(baseCostA.typeHolder(), baseCostA.getCount(), baseCostAPredicate, baseCostA); if (ingredients.size() > 1) { net.minecraft.world.item.ItemStack costB = CraftItemStack.asNMSCopy(ingredients.get(1)); DataComponentExactPredicate costBPredicate = DataComponentExactPredicate.allOf(PatchedDataComponentMap.fromPatch(DataComponentMap.EMPTY, costB.getComponentsPatch())); - this.handle.costB = Optional.of(new ItemCost(costB.getItemHolder(), costB.getCount(), costBPredicate, costB)); + this.handle.costB = Optional.of(new ItemCost(costB.typeHolder(), costB.getCount(), costBPredicate, costB)); } else { this.handle.costB = Optional.empty(); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java index d5afb7bbbe10..a9a9637c1f8d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java @@ -31,12 +31,12 @@ public class CraftMetaArmor extends CraftMetaItem implements ArmorMeta { } } - CraftMetaArmor(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaArmor(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaArmor.TRIM).ifPresent((trimCompound) -> { - TrimMaterial trimMaterial = CraftTrimMaterial.minecraftHolderToBukkit(trimCompound.material()); - TrimPattern trimPattern = CraftTrimPattern.minecraftHolderToBukkit(trimCompound.pattern()); + getOrEmpty(patch, CraftMetaArmor.TRIM).ifPresent((armorTrim) -> { + TrimMaterial trimMaterial = CraftTrimMaterial.minecraftHolderToBukkit(armorTrim.material()); + TrimPattern trimPattern = CraftTrimPattern.minecraftHolderToBukkit(armorTrim.pattern()); this.trim = new ArmorTrim(trimMaterial, trimPattern); }); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java index f9b7198be4d7..72e7092dfd7c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java @@ -36,11 +36,11 @@ public class CraftMetaArmorStand extends CraftMetaItem implements com.destroysto this.entityTag = armorStand.entityTag; } - CraftMetaArmorStand(DataComponentPatch tag, final java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaArmorStand(DataComponentPatch patch, final java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaArmorStand.ENTITY_TAG).ifPresent((nbt) -> { - this.entityTag = nbt.copyTagWithEntityId(); + getOrEmpty(patch, CraftMetaArmorStand.ENTITY_TAG).ifPresent((entityData) -> { + this.entityTag = entityData.copyTagWithEntityId(); }); } @@ -113,7 +113,7 @@ boolean isEmpty() { } boolean isArmorStandEmpty() { - return this.entityTag == null || this.entityTag.size() == 1 && this.entityTag.contains("id"); // consider armor stand empty if tag is empty. + return this.entityTag == null || this.entityTag.isEmpty(); // consider armor stand empty if tag is empty. } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java index d1af96d8649e..13613049438c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java @@ -37,15 +37,15 @@ public class CraftMetaAxolotlBucket extends CraftMetaItem implements AxolotlBuck this.bucketEntityTag = bucketMeta.bucketEntityTag; } - CraftMetaAxolotlBucket(DataComponentPatch tag, final java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaAxolotlBucket(DataComponentPatch patch, final java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaAxolotlBucket.ENTITY_TAG).ifPresent((nbt) -> { - this.entityTag = nbt.copyTagWithEntityId(); + getOrEmpty(patch, CraftMetaAxolotlBucket.ENTITY_TAG).ifPresent((entityData) -> { + this.entityTag = entityData.copyTagWithEntityId(); this.entityTag.getInt(CraftMetaAxolotlBucket.VARIANT.NBT).ifPresent(variant -> this.variant = variant); }); - getOrEmpty(tag, CraftMetaAxolotlBucket.BUCKET_ENTITY_TAG).ifPresent((nbt) -> { - this.bucketEntityTag = nbt.copyTag(); + getOrEmpty(patch, CraftMetaAxolotlBucket.BUCKET_ENTITY_TAG).ifPresent((customData) -> { + this.bucketEntityTag = customData.copyTag(); this.bucketEntityTag.getInt(CraftMetaAxolotlBucket.VARIANT.NBT).ifPresent(variant -> this.variant = variant); }); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java index 4493b77b447e..0d1ae9683c43 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java @@ -20,6 +20,9 @@ @DelegateDeserialization(SerializableMeta.class) public class CraftMetaBanner extends CraftMetaItem implements BannerMeta { + @Deprecated + public static final int ARBITRARY_LIMIT = 20; + static final ItemMetaKeyType PATTERNS = new ItemMetaKeyType<>(DataComponents.BANNER_PATTERNS, "patterns"); private List patterns = new ArrayList<>(); @@ -34,12 +37,12 @@ public class CraftMetaBanner extends CraftMetaItem implements BannerMeta { this.patterns = new ArrayList<>(bannerMeta.patterns); } - CraftMetaBanner(DataComponentPatch tag, final java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaBanner(DataComponentPatch patch, final java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaBanner.PATTERNS).ifPresent((entityTag) -> { - List patterns = entityTag.layers(); - for (int i = 0; i < Math.min(patterns.size(), 20); i++) { + getOrEmpty(patch, CraftMetaBanner.PATTERNS).ifPresent((bannerPattern) -> { + List patterns = bannerPattern.layers(); + for (int i = 0; i < Math.min(patterns.size(), ARBITRARY_LIMIT); i++) { BannerPatternLayers.Layer p = patterns.get(i); DyeColor color = DyeColor.getByWoolData((byte) p.color().getId()); PatternType pattern = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(RegistryKey.BANNER_PATTERN, p.pattern()).orElse(null); // Paper - fix upstream not handling inlined banner pattern diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java index 4a8fbba451af..32a55c327ed3 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java @@ -93,21 +93,21 @@ private Material materialForBlockEntityType() { this.blockEntityTag = metaBlockState.blockEntityTag; } - CraftMetaBlockState(DataComponentPatch tag, Material material, final Set> extraHandledDcts) { // Paper - super(tag, extraHandledDcts); // Paper + CraftMetaBlockState(DataComponentPatch patch, Material material, final Set> extraHandledComponents) { // Paper + super(patch, extraHandledComponents); // Paper this.material = material; // Paper start - move to separate method to be re-called - this.updateBlockState(tag); + this.updateBlockState(patch); } - private void updateBlockState(final DataComponentPatch tag) { + private void updateBlockState(final DataComponentPatch patch) { // Paper end - getOrEmpty(tag, CraftMetaBlockState.BLOCK_ENTITY_TAG).ifPresent((nbt) -> { - this.blockEntityTag = nbt.copyTagWithBlockEntityId(); + getOrEmpty(patch, CraftMetaBlockState.BLOCK_ENTITY_TAG).ifPresent((entityData) -> { + this.blockEntityTag = entityData.copyTagWithBlockEntityId(); }); - if (!tag.isEmpty()) { + if (!patch.isEmpty()) { // Paper start - store data in a DataComponentMap to be used to construct CraftBlockEntityStates final DataComponentMap.Builder map = DataComponentMap.builder(); final net.minecraft.world.level.block.entity.BlockEntity dummyBlockEntity = java.util.Objects.requireNonNull( @@ -126,7 +126,7 @@ private void updateBlockState(final DataComponentPatch tag) { if (!applied.isEmpty()) { for (final DataComponentType type : applied) { if (CraftMetaItem.DEFAULT_HANDLED_DCTS.contains(type)) continue; - getOrEmpty(tag, type).ifPresent(value -> { + getOrEmpty(patch, type).ifPresent(value -> { map.set(type, value); }); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java index 6b248639801a..e5b584764432 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java @@ -2,13 +2,15 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; +import net.md_5.bungee.api.chat.BaseComponent; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; @@ -21,11 +23,6 @@ import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.WritableBookMeta; -// Spigot start -import java.util.AbstractList; -import net.md_5.bungee.api.chat.BaseComponent; -// Spigot end - @DelegateDeserialization(SerializableMeta.class) public class CraftMetaBook extends CraftMetaItem implements BookMeta, WritableBookMeta { @ItemMetaKey.Specific(ItemMetaKey.Specific.To.NBT) @@ -61,11 +58,11 @@ public class CraftMetaBook extends CraftMetaItem implements BookMeta, WritableBo } } - CraftMetaBook(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaBook(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaBook.BOOK_CONTENT).ifPresent((writable) -> { - List> pages = writable.pages(); + getOrEmpty(patch, CraftMetaBook.BOOK_CONTENT).ifPresent((bookContent) -> { + List> pages = bookContent.pages(); this.pages = new ArrayList<>(pages.size()); // Note: We explicitly check for and truncate oversized books and pages, diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java index eb21c7bf833b..af8fb032e11c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java @@ -3,11 +3,13 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; +import net.md_5.bungee.api.chat.BaseComponent; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; @@ -20,11 +22,6 @@ import org.bukkit.craftbukkit.util.CraftChatMessage; import org.bukkit.inventory.meta.BookMeta; -// Spigot start -import java.util.AbstractList; -import net.md_5.bungee.api.chat.BaseComponent; -// Spigot end - @DelegateDeserialization(SerializableMeta.class) public class CraftMetaBookSigned extends CraftMetaItem implements BookMeta { @ItemMetaKey.Specific(ItemMetaKey.Specific.To.NBT) @@ -75,16 +72,16 @@ public class CraftMetaBookSigned extends CraftMetaItem implements BookMeta { } } - CraftMetaBookSigned(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaBookSigned(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaBookSigned.BOOK_CONTENT).ifPresent((written) -> { - this.title = written.title().raw(); - this.author = written.author(); - this.resolved = written.resolved(); - this.generation = written.generation(); + getOrEmpty(patch, CraftMetaBookSigned.BOOK_CONTENT).ifPresent((bookContent) -> { + this.title = bookContent.title().raw(); + this.author = bookContent.author(); + this.resolved = bookContent.resolved(); + this.generation = bookContent.generation(); - List> pages = written.pages(); + List> pages = bookContent.pages(); this.pages = new ArrayList<>(pages.size()); for (Filterable page : pages) { this.pages.add(page.raw()); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java index 374908d448f9..15d82fef7e35 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java @@ -8,6 +8,7 @@ import java.util.Map; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponents; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.component.BundleContents; import org.bukkit.configuration.serialization.DelegateDeserialization; import org.bukkit.inventory.ItemStack; @@ -32,12 +33,12 @@ public class CraftMetaBundle extends CraftMetaItem implements BundleMeta { } } - CraftMetaBundle(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaBundle(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaBundle.ITEMS).ifPresent((bundle) -> { - bundle.items().forEach((item) -> { - ItemStack itemStack = CraftItemStack.asCraftMirror(item); + getOrEmpty(patch, CraftMetaBundle.ITEMS).ifPresent((bundleContents) -> { + bundleContents.items().forEach((item) -> { + ItemStack itemStack = CraftItemStack.asCraftMirror(item.create()); if (!itemStack.isEmpty()) { // SPIGOT-7174 - Avoid adding air this.addItem(itemStack); @@ -64,10 +65,10 @@ void applyToItem(CraftMetaItem.Applicator tag) { super.applyToItem(tag); if (this.hasItems()) { - List list = new ArrayList<>(); + List list = new ArrayList<>(); for (ItemStack item : this.items) { - list.add(CraftItemStack.asNMSCopy(item)); + list.add(CraftItemStack.asTemplate(item)); } tag.put(CraftMetaBundle.ITEMS, new BundleContents(list)); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java index 09a9b752949c..29b1951adc39 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java @@ -30,12 +30,12 @@ class CraftMetaCharge extends CraftMetaItem implements FireworkEffectMeta { this.setEffect(SerializableMeta.getObject(FireworkEffect.class, map, CraftMetaCharge.EXPLOSION.BUKKIT, true)); } - CraftMetaCharge(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaCharge(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaCharge.EXPLOSION).ifPresent((f) -> { + getOrEmpty(patch, CraftMetaCharge.EXPLOSION).ifPresent((explosion) -> { try { - this.effect = CraftMetaFirework.getEffect(f); + this.effect = CraftMetaFirework.getEffect(explosion); } catch (IllegalArgumentException ex) { // Ignore invalid effects } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java index bbdf3baa970f..81871f92fb0f 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java @@ -1,6 +1,5 @@ package org.bukkit.craftbukkit.inventory; -import static org.bukkit.craftbukkit.inventory.CraftItemFactory.*; import com.google.common.collect.ImmutableMap.Builder; import java.util.Map; import java.util.Objects; @@ -9,6 +8,8 @@ import org.bukkit.configuration.serialization.DelegateDeserialization; import org.bukkit.inventory.meta.ColorableArmorMeta; +import static org.bukkit.craftbukkit.inventory.CraftItemFactory.DEFAULT_LEATHER_COLOR; + @DelegateDeserialization(SerializableMeta.class) public class CraftMetaColorableArmor extends CraftMetaArmor implements ColorableArmorMeta { @@ -23,10 +24,10 @@ public class CraftMetaColorableArmor extends CraftMetaArmor implements Colorable this.color = armorMeta.color; } - CraftMetaColorableArmor(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); - getOrEmpty(tag, CraftMetaLeatherArmor.COLOR).ifPresent((dyedItemColor) -> { - this.color = dyedItemColor.rgb(); + CraftMetaColorableArmor(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); + getOrEmpty(patch, CraftMetaLeatherArmor.COLOR).ifPresent((itemColor) -> { + this.color = itemColor.rgb(); }); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java index ded1e398ff3b..b80c0ace5a76 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java @@ -10,8 +10,8 @@ import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.component.LodestoneTracker; @@ -43,10 +43,10 @@ public class CraftMetaCompass extends CraftMetaItem implements CompassMeta { this.tracker = compassMeta.tracker; } - CraftMetaCompass(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); - getOrEmpty(tag, CraftMetaCompass.LODESTONE_TARGET).ifPresent((lodestoneTarget) -> { - this.tracker = lodestoneTarget; + CraftMetaCompass(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); + getOrEmpty(patch, CraftMetaCompass.LODESTONE_TARGET).ifPresent((tracker) -> { + this.tracker = tracker; }); } @@ -54,11 +54,14 @@ public class CraftMetaCompass extends CraftMetaItem implements CompassMeta { super(map); String lodestoneWorldKey = SerializableMeta.getString(map, CraftMetaCompass.LODESTONE_POS_WORLD.BUKKIT, true); if (lodestoneWorldKey != null) { - ResourceKey lodestoneWorld = ResourceKey.create(Registries.DIMENSION, Identifier.tryParse(lodestoneWorldKey)); - int lodestoneX = (Integer) map.get(CraftMetaCompass.LODESTONE_POS_X.BUKKIT); - int lodestoneY = (Integer) map.get(CraftMetaCompass.LODESTONE_POS_Y.BUKKIT); - int lodestoneZ = (Integer) map.get(CraftMetaCompass.LODESTONE_POS_Z.BUKKIT); - this.tracker = new LodestoneTracker(Optional.of(new GlobalPos(lodestoneWorld, new BlockPos(lodestoneX, lodestoneY, lodestoneZ))), true); + Identifier dimensionKey = Identifier.tryParse(lodestoneWorldKey); + if (dimensionKey != null) { + ResourceKey lodestoneDimension = ResourceKey.create(Registries.DIMENSION, dimensionKey); + int lodestoneX = (Integer) map.get(CraftMetaCompass.LODESTONE_POS_X.BUKKIT); + int lodestoneY = (Integer) map.get(CraftMetaCompass.LODESTONE_POS_Y.BUKKIT); + int lodestoneZ = (Integer) map.get(CraftMetaCompass.LODESTONE_POS_Z.BUKKIT); + this.tracker = new LodestoneTracker(Optional.of(new GlobalPos(lodestoneDimension, new BlockPos(lodestoneX, lodestoneY, lodestoneZ))), true); + } } else { // legacy Location lodestone = SerializableMeta.getObject(Location.class, map, CraftMetaCompass.LODESTONE_POS.BUKKIT, true); @@ -121,7 +124,7 @@ public void setLodestone(Location lodestone) { } else { GlobalPos pos = GlobalPos.of( ((CraftWorld) lodestone.getWorld()).getHandle().dimension(), - CraftLocation.toBlockPosition(lodestone) + CraftLocation.toBlockPos(lodestone) ); boolean tracked = this.tracker == null || this.tracker.tracked(); this.tracker = new LodestoneTracker(Optional.of(pos), tracked); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java index 1d389460bddc..c102a0f81a69 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java @@ -33,11 +33,11 @@ public class CraftMetaCrossbow extends CraftMetaItem implements CrossbowMeta { } } - CraftMetaCrossbow(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaCrossbow(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaCrossbow.CHARGED_PROJECTILES).ifPresent((p) -> { - List items = p.getItems(); + getOrEmpty(patch, CraftMetaCrossbow.CHARGED_PROJECTILES).ifPresent((chargedProjectiles) -> { + List items = chargedProjectiles.itemCopies(); if (items.isEmpty()) { return; } @@ -73,7 +73,7 @@ void applyToItem(CraftMetaItem.Applicator tag) { items.add(CraftItemStack.asNMSCopy(item)); } - tag.put(CraftMetaCrossbow.CHARGED_PROJECTILES, ChargedProjectiles.of(items)); + tag.put(CraftMetaCrossbow.CHARGED_PROJECTILES, ChargedProjectiles.ofNonEmpty(items)); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java index ff9204f39486..aa57ad022d3b 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java @@ -29,11 +29,11 @@ class CraftMetaEnchantedBook extends CraftMetaItem implements EnchantmentStorage } } - CraftMetaEnchantedBook(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaEnchantedBook(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaEnchantedBook.STORED_ENCHANTMENTS).ifPresent((itemEnchantments) -> { - this.enchantments = buildEnchantments(itemEnchantments); + getOrEmpty(patch, CraftMetaEnchantedBook.STORED_ENCHANTMENTS).ifPresent((enchantments) -> { + this.enchantments = buildEnchantments(enchantments); }); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java index 855bff96382f..d232940872ce 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java @@ -40,11 +40,11 @@ public class CraftMetaEntityTag extends CraftMetaItem { this.entityTag = entity.entityTag; } - CraftMetaEntityTag(DataComponentPatch tag, final java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaEntityTag(DataComponentPatch patch, final java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaEntityTag.ENTITY_TAG).ifPresent((nbt) -> { - this.entityTag = nbt.copyTagWithEntityId(); + getOrEmpty(patch, CraftMetaEntityTag.ENTITY_TAG).ifPresent((entityData) -> { + this.entityTag = entityData.copyTagWithEntityId(); }); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java index 761ce648a82d..5dc32a521991 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java @@ -60,14 +60,14 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta { } } - CraftMetaFirework(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaFirework(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaFirework.FIREWORKS).ifPresent((fireworks) -> { + getOrEmpty(patch, CraftMetaFirework.FIREWORKS).ifPresent((fireworks) -> { this.power = fireworks.flightDuration(); List fireworkEffects = fireworks.explosions(); - List effects = this.effects = new ArrayList(fireworkEffects.size()); + List effects = this.effects = new ArrayList<>(fireworkEffects.size()); for (int i = 0; i < fireworkEffects.size(); i++) { try { @@ -113,41 +113,27 @@ public static FireworkExplosion getExplosion(FireworkEffect effect) { IntList colors = CraftMetaFirework.addColors(effect.getColors()); IntList fadeColors = CraftMetaFirework.addColors(effect.getFadeColors()); - return new FireworkExplosion(CraftMetaFirework.getNBT(effect.getType()), colors, fadeColors, effect.hasTrail(), effect.hasFlicker()); + return new FireworkExplosion(CraftMetaFirework.getShape(effect.getType()), colors, fadeColors, effect.hasTrail(), effect.hasFlicker()); } - public static FireworkExplosion.Shape getNBT(Type type) { - switch (type) { - case BALL: - return FireworkExplosion.Shape.SMALL_BALL; - case BALL_LARGE: - return FireworkExplosion.Shape.LARGE_BALL; - case STAR: - return FireworkExplosion.Shape.STAR; - case CREEPER: - return FireworkExplosion.Shape.CREEPER; - case BURST: - return FireworkExplosion.Shape.BURST; - default: - throw new IllegalArgumentException("Unknown effect type " + type); - } + public static FireworkExplosion.Shape getShape(Type type) { + return switch (type) { + case BALL -> FireworkExplosion.Shape.SMALL_BALL; + case BALL_LARGE -> FireworkExplosion.Shape.LARGE_BALL; + case STAR -> FireworkExplosion.Shape.STAR; + case CREEPER -> FireworkExplosion.Shape.CREEPER; + case BURST -> FireworkExplosion.Shape.BURST; + }; } - static Type getEffectType(FireworkExplosion.Shape nbt) { - switch (nbt) { - case SMALL_BALL: - return Type.BALL; - case LARGE_BALL: - return Type.BALL_LARGE; - case STAR: - return Type.STAR; - case CREEPER: - return Type.CREEPER; - case BURST: - return Type.BURST; - default: - throw new IllegalArgumentException("Unknown effect type " + nbt); - } + static Type getEffectType(FireworkExplosion.Shape shape) { + return switch (shape) { + case SMALL_BALL -> Type.BALL; + case LARGE_BALL -> Type.BALL_LARGE; + case STAR -> Type.STAR; + case CREEPER -> Type.CREEPER; + case BURST -> Type.BURST; + }; } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java index 3825cd713a42..888a2d51df23 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java @@ -5,14 +5,19 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; import com.mojang.logging.LogUtils; import com.mojang.serialization.DynamicOps; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.data.util.Conversions; +import io.papermc.paper.registry.set.PaperRegistrySets; +import io.papermc.paper.registry.set.RegistryKeySet; +import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -35,18 +40,18 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; -import java.util.SequencedSet; import java.util.Set; import java.util.StringJoiner; -import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; import javax.annotation.Nonnull; import javax.annotation.Nullable; import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; @@ -56,12 +61,10 @@ import net.minecraft.nbt.SnbtPrinterTagVisitor; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; -import net.minecraft.tags.TagKey; import net.minecraft.util.Unit; import net.minecraft.world.entity.EquipmentSlotGroup; import net.minecraft.world.food.FoodProperties; import net.minecraft.world.item.AdventureModePredicate; -import net.minecraft.world.item.EitherHolder; import net.minecraft.world.item.JukeboxPlayable; import net.minecraft.world.item.JukeboxSongs; import net.minecraft.world.item.Rarity; @@ -79,7 +82,6 @@ import net.minecraft.world.item.enchantment.ItemEnchantments; import net.minecraft.world.item.equipment.Equippable; import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.Tag; @@ -95,6 +97,7 @@ import org.bukkit.craftbukkit.attribute.CraftAttributeMap; import org.bukkit.craftbukkit.block.CraftBlockType; import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.configuration.ConfigSerializationUtil; import org.bukkit.craftbukkit.enchantments.CraftEnchantment; import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific; import org.bukkit.craftbukkit.inventory.components.CraftCustomModelDataComponent; @@ -129,14 +132,15 @@ import org.bukkit.inventory.meta.components.UseCooldownComponent; import org.bukkit.inventory.meta.tags.CustomItemTagContainer; import org.bukkit.persistence.PersistentDataContainer; -import org.bukkit.tag.DamageTypeTags; import org.slf4j.Logger; +import static java.util.Objects.requireNonNull; + /** * Children must include the following: * *
  • Constructor(CraftMetaItem meta) - *
  • Constructor(DataComponentPatch tag, Set<DataComponentType<?>> extraHandledDcts) + *
  • Constructor(DataComponentPatch patch, Set<DataComponentType<?>> extraHandledComponents) *
  • Constructor(Map<String, Object> map) *

    *
  • void applyToItem(CraftMetaItem.Applicator tag) @@ -219,7 +223,7 @@ DataComponentPatch build() { } } - static final ItemMetaKeyType NAME = new ItemMetaKeyType(DataComponents.CUSTOM_NAME, "display-name"); + static final ItemMetaKeyType CUSTOM_NAME = new ItemMetaKeyType(DataComponents.CUSTOM_NAME, "display-name"); static final ItemMetaKeyType ITEM_NAME = new ItemMetaKeyType(DataComponents.ITEM_NAME, "item-name"); static final ItemMetaKeyType LORE = new ItemMetaKeyType<>(DataComponents.LORE, "lore"); static final ItemMetaKeyType CUSTOM_MODEL_DATA = new ItemMetaKeyType<>(DataComponents.CUSTOM_MODEL_DATA, "custom-model-data"); @@ -291,7 +295,7 @@ DataComponentPatch build() { private boolean unbreakable; private Boolean enchantmentGlintOverride; private boolean glider; - private TagKey damageResistant; + private HolderSet damageResistantTypes; private Integer maxStackSize; private ItemRarity rarity; private ItemStack useRemainder; @@ -383,7 +387,7 @@ DataComponentPatch build() { this.unbreakable = meta.unbreakable; this.enchantmentGlintOverride = meta.enchantmentGlintOverride; this.glider = meta.glider; - this.damageResistant = meta.damageResistant; + this.damageResistantTypes = meta.damageResistantTypes; this.maxStackSize = meta.maxStackSize; this.rarity = meta.rarity; if (meta.hasUseRemainder()) { @@ -417,97 +421,97 @@ DataComponentPatch build() { this.version = meta.version; } - CraftMetaItem(DataComponentPatch tag, Set> extraHandledTags) { // Paper - improve handled tags on type changes + CraftMetaItem(DataComponentPatch patch, Set> extraHandledComponents) { // Paper - improve handled tags on type changes // Paper start - properly support data components in BlockEntity - this.updateFromPatch(tag, extraHandledTags); + this.updateFromPatch(patch, extraHandledComponents); } - protected final void updateFromPatch(DataComponentPatch tag, Set> extraHandledTags) { + protected final void updateFromPatch(DataComponentPatch patch, Set> extraHandledComponents) { // Paper end - properly support data components in BlockEntity - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.NAME).ifPresent((component) -> { - this.displayName = component; + getOrEmpty(patch, CraftMetaItem.CUSTOM_NAME).ifPresent((customName) -> { + this.displayName = customName; }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.ITEM_NAME).ifPresent((component) -> { - this.itemName = component; + getOrEmpty(patch, CraftMetaItem.ITEM_NAME).ifPresent((itemName) -> { + this.itemName = itemName; }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.LORE).ifPresent((l) -> { - this.lore = new ArrayList<>(l.lines()); + getOrEmpty(patch, CraftMetaItem.LORE).ifPresent((lore) -> { + this.lore = new ArrayList<>(lore.lines()); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.CUSTOM_MODEL_DATA).ifPresent((i) -> { - this.customModelData = new CraftCustomModelDataComponent(i); + getOrEmpty(patch, CraftMetaItem.CUSTOM_MODEL_DATA).ifPresent((customModelData) -> { + this.customModelData = new CraftCustomModelDataComponent(customModelData); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.ENCHANTABLE).ifPresent((i) -> { - this.enchantableValue = i.value(); + getOrEmpty(patch, CraftMetaItem.ENCHANTABLE).ifPresent((enchantable) -> { + this.enchantableValue = enchantable.value(); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.BLOCK_DATA).ifPresent((t) -> { - this.blockData = t.properties(); + getOrEmpty(patch, CraftMetaItem.BLOCK_DATA).ifPresent((blockStateProperties) -> { + this.blockData = blockStateProperties.properties(); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.ENCHANTMENTS).ifPresent((en) -> { - this.enchantments = CraftMetaItem.buildEnchantments(en); + getOrEmpty(patch, CraftMetaItem.ENCHANTMENTS).ifPresent((enchantments) -> { + this.enchantments = CraftMetaItem.buildEnchantments(enchantments); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.ATTRIBUTES).ifPresent((en) -> { - this.attributeModifiers = CraftMetaItem.buildModifiers(en); + getOrEmpty(patch, CraftMetaItem.ATTRIBUTES).ifPresent((attributeModifiers) -> { + this.attributeModifiers = CraftMetaItem.buildModifiers(attributeModifiers); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.REPAIR).ifPresent((i) -> { - this.repairCost = i; + getOrEmpty(patch, CraftMetaItem.REPAIR).ifPresent((repairCost) -> { + this.repairCost = repairCost; }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.TOOLTIP_DISPLAY).ifPresent((en) -> { - this.tooltipDisplay = new TooltipDisplay(en.hideTooltip(), new ReferenceLinkedOpenHashSet<>(en.hiddenComponents())); + getOrEmpty(patch, CraftMetaItem.TOOLTIP_DISPLAY).ifPresent((tooltipDisplay) -> { + this.tooltipDisplay = new TooltipDisplay(tooltipDisplay.hideTooltip(), new ReferenceLinkedOpenHashSet<>(tooltipDisplay.hiddenComponents())); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.TOOLTIP_STYLE).ifPresent((key) -> { - this.tooltipStyle = CraftNamespacedKey.fromMinecraft(key); + getOrEmpty(patch, CraftMetaItem.TOOLTIP_STYLE).ifPresent((tooltipStyle) -> { + this.tooltipStyle = CraftNamespacedKey.fromMinecraft(tooltipStyle); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.ITEM_MODEL).ifPresent((key) -> { - this.itemModel = CraftNamespacedKey.fromMinecraft(key); + getOrEmpty(patch, CraftMetaItem.ITEM_MODEL).ifPresent((itemModel) -> { + this.itemModel = CraftNamespacedKey.fromMinecraft(itemModel); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.UNBREAKABLE).ifPresent((u) -> { + getOrEmpty(patch, CraftMetaItem.UNBREAKABLE).ifPresent(_ -> { this.unbreakable = true; }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.ENCHANTMENT_GLINT_OVERRIDE).ifPresent((override) -> { - this.enchantmentGlintOverride = override; + getOrEmpty(patch, CraftMetaItem.ENCHANTMENT_GLINT_OVERRIDE).ifPresent((enchantmentGlintOverride) -> { + this.enchantmentGlintOverride = enchantmentGlintOverride; }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.GLIDER).ifPresent((u) -> { + getOrEmpty(patch, CraftMetaItem.GLIDER).ifPresent(_ -> { this.glider = true; }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.DAMAGE_RESISTANT).ifPresent((tags) -> { - this.damageResistant = tags.types(); + getOrEmpty(patch, CraftMetaItem.DAMAGE_RESISTANT).ifPresent((damageResistant) -> { + this.damageResistantTypes = damageResistant.types(); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.MAX_STACK_SIZE).ifPresent((i) -> { - this.maxStackSize = i; + getOrEmpty(patch, CraftMetaItem.MAX_STACK_SIZE).ifPresent((maxStackSize) -> { + this.maxStackSize = maxStackSize; }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.RARITY).ifPresent((enumItemRarity) -> { - this.rarity = ItemRarity.valueOf(enumItemRarity.name()); + getOrEmpty(patch, CraftMetaItem.RARITY).ifPresent((rarity) -> { + this.rarity = ItemRarity.valueOf(rarity.name()); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.USE_REMAINDER).ifPresent((remainder) -> { - this.useRemainder = CraftItemStack.asCraftMirror(remainder.convertInto()); + getOrEmpty(patch, CraftMetaItem.USE_REMAINDER).ifPresent((useRemainder) -> { + this.useRemainder = CraftItemStack.asCraftMirror(useRemainder.convertInto().create()); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.USE_COOLDOWN).ifPresent((cooldown) -> { - this.useCooldown = new CraftUseCooldownComponent(cooldown); + getOrEmpty(patch, CraftMetaItem.USE_COOLDOWN).ifPresent((useCooldown) -> { + this.useCooldown = new CraftUseCooldownComponent(useCooldown); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.FOOD).ifPresent((foodInfo) -> { - this.food = new CraftFoodComponent(foodInfo); + getOrEmpty(patch, CraftMetaItem.FOOD).ifPresent((foodProperties) -> { + this.food = new CraftFoodComponent(foodProperties); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.TOOL).ifPresent((toolInfo) -> { - this.tool = new CraftToolComponent(toolInfo); + getOrEmpty(patch, CraftMetaItem.TOOL).ifPresent((tool) -> { + this.tool = new CraftToolComponent(tool); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.EQUIPPABLE).ifPresent((equippableInfo) -> { - this.equippable = new CraftEquippableComponent(equippableInfo); + getOrEmpty(patch, CraftMetaItem.EQUIPPABLE).ifPresent((equippable) -> { + this.equippable = new CraftEquippableComponent(equippable); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.JUKEBOX_PLAYABLE).ifPresent((jukeboxPlayable) -> { + getOrEmpty(patch, CraftMetaItem.JUKEBOX_PLAYABLE).ifPresent((jukeboxPlayable) -> { this.jukebox = new CraftJukeboxComponent(jukeboxPlayable); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.DAMAGE).ifPresent((i) -> { - this.damage = i; + getOrEmpty(patch, CraftMetaItem.DAMAGE).ifPresent((damage) -> { + this.damage = damage; }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.MAX_DAMAGE).ifPresent((i) -> { - this.maxDamage = i; + getOrEmpty(patch, CraftMetaItem.MAX_DAMAGE).ifPresent((maxDamage) -> { + this.maxDamage = maxDamage; }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.CUSTOM_DATA).ifPresent((customData) -> { + getOrEmpty(patch, CraftMetaItem.CUSTOM_DATA).ifPresent((customData) -> { this.customTag = customData.copyTag(); CompoundTag customDataTag = this.customTag.getCompoundOrEmpty(CraftMetaItem.BUKKIT_CUSTOM_TAG.NBT); if (!customDataTag.isEmpty()) { @@ -523,23 +527,21 @@ protected final void updateFromPatch(DataComponentPatch tag, Set { - this.canPlaceOnPredicates = List.copyOf(data.predicates); + getOrEmpty(patch, CraftMetaItem.CAN_PLACE_ON.TYPE).ifPresent(canPlaceOn -> { + this.canPlaceOnPredicates = List.copyOf(canPlaceOn.predicates); }); - CraftMetaItem.getOrEmpty(tag, CraftMetaItem.CAN_BREAK).ifPresent(data -> { - this.canBreakPredicates = List.copyOf(data.predicates); + getOrEmpty(patch, CraftMetaItem.CAN_BREAK).ifPresent(canBreak -> { + this.canBreakPredicates = List.copyOf(canBreak.predicates); }); - // Paper start - improve checking handled data component types - Set> handledTags = getTopLevelHandledDcts(this.getClass()); - if (extraHandledTags != null) { - extraHandledTags.addAll(handledTags); - handledTags = extraHandledTags; + Set> handledComponents = getTopLevelHandledComponents(this.getClass()); + if (extraHandledComponents != null) { + extraHandledComponents.addAll(handledComponents); + handledComponents = extraHandledComponents; } - // Paper end - improve checking handled data component types - Set, Optional>> keys = tag.entrySet(); + Set, Optional>> keys = patch.entrySet(); for (Map.Entry, Optional> key : keys) { - if (!handledTags.contains(key.getKey())) { // Paper - improve checking handled data component types + if (!handledComponents.contains(key.getKey())) { key.getValue().ifPresent((value) -> { this.unhandledTags.set((DataComponentType) key.getKey(), value); }); @@ -551,32 +553,25 @@ protected final void updateFromPatch(DataComponentPatch tag, Set { - Holder id = entry.getKey(); - int level = entry.getIntValue(); + static EnchantmentMap buildEnchantments(ItemEnchantments enchantments) { + EnchantmentMap map = new EnchantmentMap(); - Enchantment enchant = CraftEnchantment.minecraftHolderToBukkit(id); - if (enchant != null) { - enchantments.put(enchant, level); - } + enchantments.entrySet().forEach((entry) -> { + Enchantment enchant = CraftEnchantment.minecraftHolderToBukkit(entry.getKey()); + map.put(enchant, entry.getIntValue()); }); - return enchantments; + return map; } - static Multimap buildModifiers(ItemAttributeModifiers tag) { + static Multimap buildModifiers(ItemAttributeModifiers attributeModifiers) { Multimap modifiers = LinkedHashMultimap.create(); - List mods = tag.modifiers(); - int size = mods.size(); + List entries = attributeModifiers.modifiers(); - for (int i = 0; i < size; i++) { - ItemAttributeModifiers.Entry entry = mods.get(i); + for (ItemAttributeModifiers.Entry entry : entries) { net.minecraft.world.entity.ai.attributes.AttributeModifier nmsModifier = entry.modifier(); - AttributeModifier attribMod = CraftAttributeInstance.convert(nmsModifier); + AttributeModifier modifier = CraftAttributeInstance.convert(nmsModifier); Attribute attribute = CraftAttribute.minecraftHolderToBukkit(entry.attribute()); if (attribute == null) { @@ -593,18 +588,18 @@ static Multimap buildModifiers(ItemAttributeModifi } if (slot == null) { - modifiers.put(attribute, attribMod); + modifiers.put(attribute, modifier); continue; } - attribMod = new AttributeModifier(attribMod.getKey(), attribMod.getAmount(), attribMod.getOperation(), slot); - modifiers.put(attribute, attribMod); + modifier = new AttributeModifier(modifier.getKey(), modifier.getAmount(), modifier.getOperation(), slot); + modifiers.put(attribute, modifier); } return modifiers; } CraftMetaItem(Map map) { - this.displayName = CraftChatMessage.fromJSONOrString(SerializableMeta.getString(map, CraftMetaItem.NAME.BUKKIT, true), true, false); + this.displayName = CraftChatMessage.fromJSONOrString(SerializableMeta.getString(map, CraftMetaItem.CUSTOM_NAME.BUKKIT, true), true, false); this.itemName = CraftChatMessage.fromJSONOrNull(SerializableMeta.getString(map, CraftMetaItem.ITEM_NAME.BUKKIT, true)); Iterable lore = SerializableMeta.getObject(Iterable.class, map, CraftMetaItem.LORE.BUKKIT, true); @@ -688,11 +683,11 @@ static Multimap buildModifiers(ItemAttributeModifi this.setGlider(glider); } - String damageResistant = SerializableMeta.getString(map, CraftMetaItem.DAMAGE_RESISTANT.BUKKIT, true); + Object damageResistant = SerializableMeta.getObject(Object.class, map, CraftMetaItem.DAMAGE_RESISTANT.BUKKIT, true); if (damageResistant != null) { - Tag tag = Bukkit.getTag(DamageTypeTags.REGISTRY_DAMAGE_TYPES, NamespacedKey.fromString(damageResistant), DamageType.class); - if (tag != null) { - this.setDamageResistant(tag); + HolderSet damageResistantTypes = ConfigSerializationUtil.getHolderSet(damageResistant, Registries.DAMAGE_TYPE); + if (damageResistantTypes != null) { + this.damageResistantTypes = damageResistantTypes; } } @@ -764,10 +759,10 @@ static Multimap buildModifiers(ItemAttributeModifi CompoundTag unhandledTag = NbtIo.readCompressed(buf, NbtAccounter.unlimitedHeap()); DataComponentPatch unhandledPatch = DataComponentPatch.CODEC.parse(CraftRegistry.getMinecraftRegistry().createSerializationContext(NbtOps.INSTANCE), unhandledTag).result().get(); - CraftMetaItem.getOrEmpty(unhandledPatch, CraftMetaItem.CAN_PLACE_ON).ifPresent(data -> { + getOrEmpty(unhandledPatch, CraftMetaItem.CAN_PLACE_ON).ifPresent(data -> { this.canPlaceOnPredicates = List.copyOf(data.predicates); }); - CraftMetaItem.getOrEmpty(unhandledPatch, CraftMetaItem.CAN_BREAK).ifPresent(data -> { + getOrEmpty(unhandledPatch, CraftMetaItem.CAN_BREAK).ifPresent(data -> { this.canBreakPredicates = List.copyOf(data.predicates); }); this.unhandledTags.copy(unhandledPatch.forget(type -> type == CraftMetaItem.CAN_PLACE_ON.TYPE || type == CraftMetaItem.CAN_BREAK.TYPE)); @@ -931,7 +926,7 @@ static Multimap buildModifiers(Map @Overridden void applyToItem(CraftMetaItem.Applicator tag) { if (this.hasDisplayName()) { - tag.put(CraftMetaItem.NAME, this.displayName); + tag.put(CraftMetaItem.CUSTOM_NAME, this.displayName); } if (this.hasItemName()) { @@ -986,7 +981,7 @@ void applyToItem(CraftMetaItem.Applicator tag) { } if (this.hasDamageResistant()) { - tag.put(CraftMetaItem.DAMAGE_RESISTANT, new DamageResistant(this.damageResistant)); + tag.put(CraftMetaItem.DAMAGE_RESISTANT, new DamageResistant(this.damageResistantTypes)); } if (this.hasMaxStackSize()) { @@ -998,7 +993,7 @@ void applyToItem(CraftMetaItem.Applicator tag) { } if (this.hasUseRemainder()) { - tag.put(CraftMetaItem.USE_REMAINDER, new UseRemainder(CraftItemStack.asNMSCopy(this.useRemainder))); + tag.put(CraftMetaItem.USE_REMAINDER, new UseRemainder(CraftItemStack.asTemplate(this.useRemainder))); } if (this.hasUseCooldown()) { @@ -1443,7 +1438,10 @@ public boolean hasBlockData() { @Override public BlockData getBlockData(Material material) { BlockState defaultData = CraftBlockType.bukkitToMinecraft(material).defaultBlockState(); - return CraftBlockData.fromData((this.hasBlockData()) ? new BlockItemStateProperties(this.blockData).apply(defaultData) : defaultData); + if (this.blockData == null) { + return defaultData.asBlockData(); + } + return new BlockItemStateProperties(this.blockData).apply(defaultData).asBlockData(); } @Override @@ -1543,27 +1541,50 @@ public void setGlider(boolean glider) { @Override public boolean isFireResistant() { - return this.hasDamageResistant() && this.damageResistant == net.minecraft.tags.DamageTypeTags.IS_FIRE; + return this.hasDamageResistant() && this.damageResistantTypes.unwrapKey().map(key -> key == net.minecraft.tags.DamageTypeTags.IS_FIRE).orElse(false); } @Override public void setFireResistant(boolean fireResistant) { - this.damageResistant = net.minecraft.tags.DamageTypeTags.IS_FIRE; + if (fireResistant) { + this.damageResistantTypes = CraftRegistry.getMinecraftRegistry(Registries.DAMAGE_TYPE).getOrThrow(net.minecraft.tags.DamageTypeTags.IS_FIRE); + } else if (this.isFireResistant()) { + this.damageResistantTypes = null; + } } @Override public boolean hasDamageResistant() { - return this.damageResistant != null; + return this.damageResistantTypes != null; } @Override public Tag getDamageResistant() { - return (this.hasDamageResistant()) ? Bukkit.getTag(DamageTypeTags.REGISTRY_DAMAGE_TYPES, CraftNamespacedKey.fromMinecraft(this.damageResistant.location()), DamageType.class) : null; + if (!this.hasDamageResistant()) { + return null; + } + return this.damageResistantTypes.unwrapKey() + .map(tagKey -> new CraftDamageTag(CraftRegistry.getMinecraftRegistry(Registries.DAMAGE_TYPE), tagKey)) + .orElse(null); } @Override public void setDamageResistant(Tag tag) { - this.damageResistant = (tag != null) ? ((CraftDamageTag) tag).getHandle().key() : null; + this.damageResistantTypes = (tag != null) ? ((CraftDamageTag) tag).getHandle() : null; + } + + @Override + public RegistryKeySet getDamageResistantTypes() { + return this.damageResistantTypes == null ? null : PaperRegistrySets.convertToApi(RegistryKey.DAMAGE_TYPE, this.damageResistantTypes); + } + + @Override + public void setDamageResistantTypes(RegistryKeySet types) { + if (types == null) { + this.damageResistantTypes = null; + } else { + this.damageResistantTypes = PaperRegistrySets.convertToNms(Registries.DAMAGE_TYPE, Conversions.global().lookup(), types); + } } @Override @@ -1683,7 +1704,7 @@ public boolean hasJukeboxPlayable() { @Override public JukeboxPlayableComponent getJukeboxPlayable() { - return (this.hasJukeboxPlayable()) ? new CraftJukeboxComponent(this.jukebox) : new CraftJukeboxComponent(new JukeboxPlayable(new EitherHolder<>(JukeboxSongs.THIRTEEN))); + return (this.hasJukeboxPlayable()) ? new CraftJukeboxComponent(this.jukebox) : new CraftJukeboxComponent(new JukeboxPlayable(CraftRegistry.getMinecraftRegistry(Registries.JUKEBOX_SONG).get(JukeboxSongs.THIRTEEN).orElseThrow())); } @Override @@ -1833,18 +1854,20 @@ public String getAsComponentString() { this.applyToItem(tag); DataComponentPatch patch = tag.build(); - RegistryAccess registryAccess = CraftRegistry.getMinecraftRegistry(); - DynamicOps ops = registryAccess.createSerializationContext(NbtOps.INSTANCE); - Registry> componentTypeRegistry = registryAccess.lookupOrThrow(Registries.DATA_COMPONENT_TYPE); - + DynamicOps ops = CraftRegistry.getMinecraftRegistry().createSerializationContext(NbtOps.INSTANCE); StringJoiner componentString = new StringJoiner(",", "[", "]"); + for (Entry, Optional> entry : patch.entrySet()) { - DataComponentType componentType = entry.getKey(); + DataComponentType type = entry.getKey(); + if (type.isTransient()) { + continue; + } + Optional componentValue = entry.getValue(); - String componentKey = componentTypeRegistry.getResourceKey(componentType).orElseThrow().identifier().toString(); + String componentKey = requireNonNull(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(type)).toString(); if (componentValue.isPresent()) { - net.minecraft.nbt.Tag componentValueAsNBT = (net.minecraft.nbt.Tag) ((DataComponentType) componentType).codecOrThrow().encodeStart(ops, componentValue.get()).getOrThrow(); + net.minecraft.nbt.Tag componentValueAsNBT = (net.minecraft.nbt.Tag) ((DataComponentType) type).codecOrThrow().encodeStart(ops, componentValue.get()).getOrThrow(); String componentValueAsNBTString = new SnbtPrinterTagVisitor("", 0, new ArrayList<>()).visit(componentValueAsNBT); componentString.add(componentKey + "=" + componentValueAsNBTString); } else { @@ -1970,7 +1993,7 @@ boolean equalsCommon(CraftMetaItem meta) { && (this.isUnbreakable() == meta.isUnbreakable()) && (this.hasEnchantmentGlintOverride() ? meta.hasEnchantmentGlintOverride() && this.enchantmentGlintOverride.equals(meta.enchantmentGlintOverride) : !meta.hasEnchantmentGlintOverride()) && (this.glider == meta.glider) - && (this.hasDamageResistant() ? meta.hasDamageResistant() && this.damageResistant.equals(meta.damageResistant) : !meta.hasDamageResistant()) + && (this.hasDamageResistant() ? meta.hasDamageResistant() && this.damageResistantTypes.equals(meta.damageResistantTypes) : !meta.hasDamageResistant()) && (this.hasMaxStackSize() ? meta.hasMaxStackSize() && this.maxStackSize.equals(meta.maxStackSize) : !meta.hasMaxStackSize()) && (this.rarity == meta.rarity) && (this.hasUseRemainder() ? meta.hasUseRemainder() && this.useRemainder.equals(meta.useRemainder) : !meta.hasUseRemainder()) @@ -2023,7 +2046,7 @@ int applyHash() { hash = 61 * hash + (this.isUnbreakable() ? 1231 : 1237); hash = 61 * hash + (this.hasEnchantmentGlintOverride() ? this.enchantmentGlintOverride.hashCode() : 0); hash = 61 * hash + (this.isGlider() ? 1231 : 1237); - hash = 61 * hash + (this.hasDamageResistant() ? this.damageResistant.hashCode() : 0); + hash = 61 * hash + (this.hasDamageResistant() ? this.damageResistantTypes.hashCode() : 0); hash = 61 * hash + (this.hasMaxStackSize() ? this.maxStackSize.hashCode() : 0); hash = 61 * hash + (this.hasRarity() ? this.rarity.hashCode() : 0); hash = 61 * hash + (this.hasUseRemainder() ? this.useRemainder.hashCode() : 0); @@ -2073,7 +2096,7 @@ public CraftMetaItem clone() { clone.unbreakable = this.unbreakable; clone.enchantmentGlintOverride = this.enchantmentGlintOverride; clone.glider = this.glider; - clone.damageResistant = this.damageResistant; + clone.damageResistantTypes = this.damageResistantTypes; clone.maxStackSize = this.maxStackSize; clone.rarity = this.rarity; if (this.hasUseRemainder()) { @@ -2122,7 +2145,7 @@ public final Map serialize() { @Overridden ImmutableMap.Builder serialize(ImmutableMap.Builder builder) { if (this.hasDisplayName()) { - builder.put(CraftMetaItem.NAME.BUKKIT, CraftChatMessage.toJSON(this.displayName)); + builder.put(CraftMetaItem.CUSTOM_NAME.BUKKIT, CraftChatMessage.toJSON(this.displayName)); } if (this.hasItemName()) { @@ -2186,7 +2209,7 @@ ImmutableMap.Builder serialize(ImmutableMap.Builder, Set>> HANDLED_DCTS_PER_TYPE = new HashMap<>(); protected static final Set> DEFAULT_HANDLED_DCTS = Set.of( - CraftMetaItem.NAME.TYPE, + CraftMetaItem.CUSTOM_NAME.TYPE, CraftMetaItem.ITEM_NAME.TYPE, CraftMetaItem.LORE.TYPE, CraftMetaItem.CUSTOM_MODEL_DATA.TYPE, @@ -2430,7 +2452,7 @@ public void setVersion(int version) { CraftMetaItem.CAN_PLACE_ON.TYPE, CraftMetaItem.CAN_BREAK.TYPE ); - public static Set> getTopLevelHandledDcts(final Class clazz) { + public static Set> getTopLevelHandledComponents(final Class clazz) { synchronized (HANDLED_DCTS_PER_TYPE) { if (HANDLED_DCTS_PER_TYPE.isEmpty()) { final Map, Set>> map = new HashMap<>(); @@ -2471,15 +2493,13 @@ public static Set> getTopLevelHandledDcts(final Class Optional getOrEmpty(DataComponentPatch tag, ItemMetaKeyType type) { - return getOrEmpty(tag, type.TYPE); + protected static Optional getOrEmpty(DataComponentPatch patch, ItemMetaKeyType type) { + return getOrEmpty(patch, type.TYPE); } - protected static Optional getOrEmpty(final DataComponentPatch tag, final DataComponentType type) { - Optional result = tag.get(type); - return (result != null) ? result : Optional.empty(); + protected static Optional getOrEmpty(final DataComponentPatch patch, final DataComponentType type) { + return Optional.ofNullable(patch.get(net.minecraft.core.component.DataComponentMap.EMPTY, type)); } private static class EnchantmentMap extends java.util.TreeMap { @@ -2562,7 +2582,7 @@ private static List convert final net.minecraft.core.Registry blockRegistry = CraftRegistry.getMinecraftRegistry().lookupOrThrow(net.minecraft.core.registries.Registries.BLOCK); for (final com.destroystokyo.paper.Namespaced namespaced : namespaceds) { if (namespaced instanceof final org.bukkit.NamespacedKey key) { - predicates.add(net.minecraft.advancements.criterion.BlockPredicate.Builder.block().of(blockRegistry, CraftBlockType.bukkitToMinecraft(Objects.requireNonNull(org.bukkit.Registry.MATERIAL.get(key)))).build()); + predicates.add(net.minecraft.advancements.criterion.BlockPredicate.Builder.block().of(blockRegistry, CraftBlockType.bukkitToMinecraft(requireNonNull(org.bukkit.Registry.MATERIAL.get(key)))).build()); } else if (namespaced instanceof final com.destroystokyo.paper.NamespacedTag tag) { predicates.add(net.minecraft.advancements.criterion.BlockPredicate.Builder.block().of(blockRegistry, net.minecraft.tags.TagKey.create(Registries.BLOCK, Identifier.fromNamespaceAndPath(tag.getNamespace(), tag.getKey()))).build()); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java index e6ef13de6a15..43ac638f449f 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java @@ -7,6 +7,7 @@ import java.util.Map; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.crafting.Recipe; import org.bukkit.NamespacedKey; @@ -30,10 +31,10 @@ public class CraftMetaKnowledgeBook extends CraftMetaItem implements KnowledgeBo } } - CraftMetaKnowledgeBook(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaKnowledgeBook(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaKnowledgeBook.BOOK_RECIPES).ifPresent((recipes) -> { + getOrEmpty(patch, CraftMetaKnowledgeBook.BOOK_RECIPES).ifPresent((recipes) -> { for (ResourceKey recipe : recipes) { this.addRecipe(CraftNamespacedKey.fromMinecraft(recipe.identifier())); } @@ -60,7 +61,7 @@ void applyToItem(CraftMetaItem.Applicator tag) { if (this.hasRecipes()) { List>> list = new ArrayList<>(); for (NamespacedKey recipe : this.recipes) { - list.add(CraftRecipe.toMinecraft(recipe)); + list.add(CraftNamespacedKey.toResourceKey(Registries.RECIPE, recipe)); } tag.put(CraftMetaKnowledgeBook.BOOK_RECIPES, list); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java index 98cf8679455e..0a975ed03263 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java @@ -1,6 +1,5 @@ package org.bukkit.craftbukkit.inventory; -import static org.bukkit.craftbukkit.inventory.CraftItemFactory.*; import com.google.common.collect.ImmutableMap.Builder; import java.util.Map; import java.util.Objects; @@ -13,6 +12,8 @@ import org.bukkit.inventory.meta.ColorableArmorMeta; import org.bukkit.inventory.meta.LeatherArmorMeta; +import static org.bukkit.craftbukkit.inventory.CraftItemFactory.DEFAULT_LEATHER_COLOR; + @DelegateDeserialization(SerializableMeta.class) class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta { @@ -29,10 +30,10 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta { this.color = leatherMeta.color; } - CraftMetaLeatherArmor(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); - getOrEmpty(tag, CraftMetaLeatherArmor.COLOR).ifPresent((dyedItemColor) -> { - this.color = dyedItemColor.rgb(); + CraftMetaLeatherArmor(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); + getOrEmpty(patch, CraftMetaLeatherArmor.COLOR).ifPresent((itemColor) -> { + this.color = itemColor.rgb(); }); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java index ff8b61762916..634d01855c5d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java @@ -44,20 +44,20 @@ class CraftMetaMap extends CraftMetaItem implements MapMeta { this.color = mapMeta.color; } - CraftMetaMap(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaMap(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaMap.MAP_ID).ifPresent((mapId) -> { - this.mapId = mapId.id(); + getOrEmpty(patch, CraftMetaMap.MAP_ID).ifPresent((map) -> { + this.mapId = map.id(); }); - getOrEmpty(tag, CraftMetaMap.MAP_POST_PROCESSING).ifPresent((mapPostProcessing) -> { + getOrEmpty(patch, CraftMetaMap.MAP_POST_PROCESSING).ifPresent((mapPostProcessing) -> { this.scaling = (mapPostProcessing == MapPostProcessing.SCALE) ? CraftMetaMap.SCALING_TRUE : CraftMetaMap.SCALING_FALSE; }); - getOrEmpty(tag, CraftMetaMap.MAP_COLOR).ifPresent((mapColor) -> { + getOrEmpty(patch, CraftMetaMap.MAP_COLOR).ifPresent((color) -> { try { - this.color = mapColor.rgb(); + this.color = color.rgb(); } catch (IllegalArgumentException ex) { // Invalid colour } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java index 26f9278ef747..71aa199b2e02 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java @@ -8,7 +8,6 @@ import org.bukkit.MusicInstrument; import org.bukkit.configuration.serialization.DelegateDeserialization; import org.bukkit.craftbukkit.CraftMusicInstrument; -import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.inventory.meta.MusicInstrumentMeta; @DelegateDeserialization(SerializableMeta.class) @@ -25,12 +24,11 @@ public class CraftMetaMusicInstrument extends CraftMetaItem implements MusicInst } } - CraftMetaMusicInstrument(DataComponentPatch tag, java.util.Set> extraHandledDcts) { // Paper - super(tag, extraHandledDcts); // Paper + CraftMetaMusicInstrument(DataComponentPatch patch, java.util.Set> extraHandledComponents) { // Paper + super(patch, extraHandledComponents); // Paper - getOrEmpty(tag, CraftMetaMusicInstrument.GOAT_HORN_INSTRUMENT).ifPresent((en) -> { - en.instrument().unwrap(CraftRegistry.getMinecraftRegistry()) - .ifPresent(instrument -> this.instrument = CraftMusicInstrument.minecraftHolderToBukkit(instrument)); + getOrEmpty(patch, CraftMetaMusicInstrument.GOAT_HORN_INSTRUMENT).ifPresent((instrumentComponent) -> { + this.instrument = CraftMusicInstrument.minecraftHolderToBukkit(instrumentComponent.instrument()); }); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java index 7bc9ab874f51..6422a399d556 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java @@ -24,9 +24,9 @@ public class CraftMetaOminousBottle extends CraftMetaItem implements OminousBott this.ominousBottleAmplifier = ominousBottleMeta.ominousBottleAmplifier; } - CraftMetaOminousBottle(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); - getOrEmpty(tag, CraftMetaOminousBottle.OMINOUS_BOTTLE_AMPLIFIER).ifPresent((amplifier) -> { + CraftMetaOminousBottle(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); + getOrEmpty(patch, CraftMetaOminousBottle.OMINOUS_BOTTLE_AMPLIFIER).ifPresent((amplifier) -> { this.ominousBottleAmplifier = amplifier.value(); }); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java index 49e3a8d3910e..75b0fec7929b 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java @@ -35,7 +35,7 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { static final ItemMetaKeyType POTION_CONTENTS = new ItemMetaKeyType<>(DataComponents.POTION_CONTENTS); static final ItemMetaKey POTION_EFFECTS = new ItemMetaKey("custom-effects"); static final ItemMetaKey POTION_COLOR = new ItemMetaKey("custom-color"); - static final ItemMetaKey CUSTOM_NAME = new ItemMetaKey("custom-name"); + static final ItemMetaKey POTION_CUSTOM_NAME = new ItemMetaKey("custom-name"); static final ItemMetaKey DEFAULT_POTION = new ItemMetaKey("potion-type"); private PotionType type; @@ -56,9 +56,9 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { } } - CraftMetaPotion(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); - getOrEmpty(tag, CraftMetaPotion.POTION_CONTENTS).ifPresent((potionContents) -> { + CraftMetaPotion(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); + getOrEmpty(patch, CraftMetaPotion.POTION_CONTENTS).ifPresent((potionContents) -> { potionContents.potion().ifPresent((potion) -> { this.type = CraftPotionType.minecraftHolderToBukkit(potion); }); @@ -109,7 +109,7 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta { this.setColor(color); } - String name = SerializableMeta.getString(map, CraftMetaPotion.CUSTOM_NAME.BUKKIT, true); + String name = SerializableMeta.getString(map, CraftMetaPotion.POTION_CUSTOM_NAME.BUKKIT, true); if (name != null) { this.setCustomPotionName(name); } @@ -397,7 +397,7 @@ Builder serialize(Builder builder) { } if (this.hasCustomPotionName()) { - builder.put(CraftMetaPotion.CUSTOM_NAME.BUKKIT, this.getCustomPotionName()); + builder.put(CraftMetaPotion.POTION_CUSTOM_NAME.BUKKIT, this.getCustomPotionName()); } if (this.hasCustomEffects()) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaShield.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaShield.java index 4a67cdd33777..35b6081ed571 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaShield.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaShield.java @@ -48,16 +48,16 @@ private boolean hasPatterns() { } } - CraftMetaShield(DataComponentPatch tag, java.util.Set> extraHandledDcts) { // Paper - improve checking handled tags in item meta - super(tag, extraHandledDcts); // Paper - improve checking handled tags in item meta + CraftMetaShield(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaShield.BASE_COLOR).ifPresent((color) -> { + getOrEmpty(patch, CraftMetaShield.BASE_COLOR).ifPresent((color) -> { this.baseColor = DyeColor.getByWoolData((byte) color.getId()); }); - getOrEmpty(tag, CraftMetaBanner.PATTERNS).ifPresent((entityTag) -> { - List patterns = entityTag.layers(); - for (int i = 0; i < Math.min(patterns.size(), 20); i++) { + getOrEmpty(patch, CraftMetaBanner.PATTERNS).ifPresent((bannerPattern) -> { + List patterns = bannerPattern.layers(); + for (int i = 0; i < Math.min(patterns.size(), CraftMetaBanner.ARBITRARY_LIMIT); i++) { BannerPatternLayers.Layer p = patterns.get(i); DyeColor color = DyeColor.getByWoolData((byte) p.color().getId()); PatternType pattern = CraftRegistry.unwrapAndConvertHolder(io.papermc.paper.registry.RegistryKey.BANNER_PATTERN, p.pattern()).orElse(null); // Paper - fix upstream not being correct @@ -283,7 +283,6 @@ static Material shieldToBannerHack(DyeColor color) { case GREEN -> Material.GREEN_BANNER; case RED -> Material.RED_BANNER; case BLACK -> Material.BLACK_BANNER; - default -> throw new IllegalArgumentException("Unknown banner colour"); }; } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java index d821df4787ac..f8b8e89a2cb9 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java @@ -2,16 +2,17 @@ import com.google.common.collect.ImmutableMap.Builder; import com.mojang.authlib.GameProfile; +import com.mojang.datafixers.util.Either; import java.util.Map; import java.util.Objects; -import com.mojang.datafixers.util.Either; -import net.minecraft.util.Util; import net.minecraft.core.UUIDUtil; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponents; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.resources.Identifier; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Util; import net.minecraft.world.entity.player.PlayerSkin; import net.minecraft.world.item.component.ResolvableProfile; import org.bukkit.Bukkit; @@ -50,12 +51,12 @@ class CraftMetaSkull extends CraftMetaItem implements SkullMeta { this.noteBlockSound = skullMeta.noteBlockSound; } - CraftMetaSkull(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaSkull(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaSkull.SKULL_PROFILE).ifPresent(this::setProfile); + getOrEmpty(patch, CraftMetaSkull.SKULL_PROFILE).ifPresent(this::setProfile); - getOrEmpty(tag, CraftMetaSkull.NOTE_BLOCK_SOUND).ifPresent((noteBlockSound) -> this.noteBlockSound = noteBlockSound); + getOrEmpty(patch, CraftMetaSkull.NOTE_BLOCK_SOUND).ifPresent((noteBlockSound) -> this.noteBlockSound = noteBlockSound); } CraftMetaSkull(Map map) { @@ -114,7 +115,7 @@ void applyToItem(CraftMetaItem.Applicator tag) { ownerProfile.update().thenAcceptAsync((filledProfile) -> { // Paper - run on main thread this.setOwnerProfile(filledProfile); tag.skullCallback(this.profile); // Paper - actually set profile on itemstack - }, ((org.bukkit.craftbukkit.CraftServer) org.bukkit.Bukkit.getServer()).getServer()); // Paper - run on main thread + }, MinecraftServer.getServer()); // Paper - run on main thread } } @@ -149,13 +150,13 @@ public String getOwner() { @Override public void setPlayerProfile(com.destroystokyo.paper.profile.@Nullable PlayerProfile profile) { - setProfile((profile == null) ? null : com.destroystokyo.paper.profile.CraftPlayerProfile.asResolvableProfileCopy(profile)); + this.setProfile((profile == null) ? null : com.destroystokyo.paper.profile.CraftPlayerProfile.asResolvableProfileCopy(profile)); } @Nullable @Override public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() { - return profile != null ? new com.destroystokyo.paper.profile.CraftPlayerProfile(profile) : null; + return this.profile != null ? new com.destroystokyo.paper.profile.CraftPlayerProfile(this.profile) : null; } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java index cdbdf46d38f5..0db2f981c78a 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java @@ -37,11 +37,11 @@ public class CraftMetaSpawnEgg extends CraftMetaItem implements SpawnEggMeta { this.entityTag = spawnEggMeta.entityTag; } - CraftMetaSpawnEgg(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaSpawnEgg(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaSpawnEgg.ENTITY_TAG).ifPresent((nbt) -> { - this.entityTag = nbt.copyTagWithEntityId(); + getOrEmpty(patch, CraftMetaSpawnEgg.ENTITY_TAG).ifPresent((entityData) -> { + this.entityTag = entityData.copyTagWithEntityId(); }); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java index 3197a31e125d..2c1dad444a0f 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java @@ -34,10 +34,10 @@ public class CraftMetaSuspiciousStew extends CraftMetaItem implements Suspicious } } - CraftMetaSuspiciousStew(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); - getOrEmpty(tag, CraftMetaSuspiciousStew.EFFECTS).ifPresent((suspiciousStewEffects) -> { - List list = suspiciousStewEffects.effects(); + CraftMetaSuspiciousStew(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); + getOrEmpty(patch, CraftMetaSuspiciousStew.EFFECTS).ifPresent((suspiciousStew) -> { + List list = suspiciousStew.effects(); int length = list.size(); this.customEffects = new ArrayList<>(length); @@ -56,14 +56,14 @@ public class CraftMetaSuspiciousStew extends CraftMetaItem implements Suspicious CraftMetaSuspiciousStew(Map map) { super(map); - Iterable rawEffectList = SerializableMeta.getObject(Iterable.class, map, CraftMetaSuspiciousStew.EFFECTS.BUKKIT, true); - if (rawEffectList == null) { + Iterable effects = SerializableMeta.getObject(Iterable.class, map, CraftMetaSuspiciousStew.EFFECTS.BUKKIT, true); + if (effects == null) { return; } - for (Object obj : rawEffectList) { - Preconditions.checkArgument(obj instanceof PotionEffect, "Object (%s) in effect list is not valid", obj.getClass()); - this.addCustomEffect((PotionEffect) obj, true); + for (Object effect : effects) { + Preconditions.checkArgument(effect instanceof PotionEffect, "Object (%s) in effect list is not valid", effect.getClass()); + this.addCustomEffect((PotionEffect) effect, true); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java index 4fdd196c0bb6..ee8839e6f05f 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java @@ -39,15 +39,15 @@ class CraftMetaTropicalFishBucket extends CraftMetaItem implements TropicalFishB this.bucketEntityTag = tropicalFishBucketMeta.bucketEntityTag; } - CraftMetaTropicalFishBucket(DataComponentPatch tag, java.util.Set> extraHandledDcts) { - super(tag, extraHandledDcts); + CraftMetaTropicalFishBucket(DataComponentPatch patch, java.util.Set> extraHandledComponents) { + super(patch, extraHandledComponents); - getOrEmpty(tag, CraftMetaTropicalFishBucket.ENTITY_TAG).ifPresent((nbt) -> { - this.entityTag = nbt.copyTagWithEntityId(); + getOrEmpty(patch, CraftMetaTropicalFishBucket.ENTITY_TAG).ifPresent((entityData) -> { + this.entityTag = entityData.copyTagWithEntityId(); this.entityTag.getInt(CraftMetaTropicalFishBucket.VARIANT.NBT).ifPresent(variant -> this.variant = variant); }); - getOrEmpty(tag, CraftMetaTropicalFishBucket.BUCKET_ENTITY_TAG).ifPresent((nbt) -> { - this.bucketEntityTag = nbt.copyTag(); + getOrEmpty(patch, CraftMetaTropicalFishBucket.BUCKET_ENTITY_TAG).ifPresent((customData) -> { + this.bucketEntityTag = customData.copyTag(); this.bucketEntityTag.getInt(CraftMetaTropicalFishBucket.VARIANT.NBT).ifPresent(variant -> this.variant = variant); }); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java index 833182cb6ee2..5e2439283dd1 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java @@ -9,30 +9,23 @@ import java.util.List; import java.util.Optional; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.crafting.Ingredient; -import org.bukkit.NamespacedKey; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.ItemType; import org.bukkit.inventory.Recipe; import org.bukkit.inventory.RecipeChoice; import org.bukkit.inventory.recipe.CookingBookCategory; import org.bukkit.inventory.recipe.CraftingBookCategory; +import org.jspecify.annotations.Nullable; public interface CraftRecipe extends Recipe { - void addToCraftingManager(); + void addToRecipeManager(); - default Optional toNMSOptional(RecipeChoice bukkit, boolean requireNotEmpty) { - return (bukkit == null || bukkit == RecipeChoice.empty()) ? Optional.empty() : Optional.of(this.toNMS(bukkit, requireNotEmpty)); // Paper - support "empty" choices + static Optional toPossibleIngredient(@Nullable RecipeChoice bukkit, boolean requireNotEmpty) { + return (bukkit == null || bukkit == RecipeChoice.empty()) ? Optional.empty() : Optional.of(toIngredient(bukkit, requireNotEmpty)); // Paper - support "empty" choices } - default Ingredient toNMS(RecipeChoice bukkit, boolean requireNotEmpty) { - // Paper start - return toIngredient(bukkit, requireNotEmpty); - } static Ingredient toIngredient(RecipeChoice bukkit, boolean requireNotEmpty) { - // Paper end Ingredient stack; if (bukkit == null) { @@ -40,9 +33,9 @@ static Ingredient toIngredient(RecipeChoice bukkit, boolean requireNotEmpty) { } else if (bukkit instanceof final RecipeChoice.ItemTypeChoice itemTypeChoice) { stack = Ingredient.of(PaperRegistrySets.convertToNms(Registries.ITEM, Conversions.global().lookup(), itemTypeChoice.itemTypes())); } else if (bukkit instanceof RecipeChoice.MaterialChoice) { - stack = Ingredient.of(((RecipeChoice.MaterialChoice) bukkit).getChoices().stream().map((mat) -> CraftItemType.bukkitToMinecraft(mat))); + stack = Ingredient.of(((RecipeChoice.MaterialChoice) bukkit).getChoices().stream().map(CraftItemType::bukkitToMinecraft)); } else if (bukkit instanceof RecipeChoice.ExactChoice) { - stack = Ingredient.ofStacks(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> CraftItemStack.asNMSCopy(mat)).toList()); + stack = Ingredient.ofStacks(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map(CraftItemStack::asNMSCopy).toList()); // Paper start - support "empty" choices - legacy method that spigot might incorrectly call // Their impl of Ingredient.of() will error, ingredients need at least one entry. // Callers running into this exception may have passed an incorrect empty() recipe choice to a non-empty slot or @@ -61,45 +54,41 @@ static Ingredient toIngredient(RecipeChoice bukkit, boolean requireNotEmpty) { return stack; } - public static RecipeChoice toBukkit(Optional list) { - return list.map(CraftRecipe::toBukkit).orElse(RecipeChoice.empty()); // Paper - fix issue with recipe API + static RecipeChoice toChoice(Optional ingredient) { + return ingredient.map(CraftRecipe::toChoice).orElse(RecipeChoice.empty()); // Paper - fix issue with recipe API } - public static RecipeChoice toBukkit(Ingredient list) { - if (list.isEmpty()) { + static RecipeChoice toChoice(Ingredient ingredient) { + if (ingredient.isEmpty()) { return RecipeChoice.empty(); // Paper - null breaks API contracts } - if (list.isExact()) { - List choices = new ArrayList<>(list.itemStacks().size()); - for (net.minecraft.world.item.ItemStack i : list.itemStacks()) { + if (ingredient.isExact()) { + List choices = new ArrayList<>(ingredient.itemStacks().size()); + for (net.minecraft.world.item.ItemStack i : ingredient.itemStacks()) { choices.add(CraftItemStack.asBukkitCopy(i)); } return new RecipeChoice.ExactChoice(choices); } else { - final RegistryKeySet itemTypes = PaperRegistrySets.convertToApi(RegistryKey.ITEM, list.values); + final RegistryKeySet itemTypes = PaperRegistrySets.convertToApi(RegistryKey.ITEM, ingredient.values); return RecipeChoice.itemType(itemTypes); } } - public static net.minecraft.world.item.crafting.CraftingBookCategory getCategory(CraftingBookCategory bukkit) { + static net.minecraft.world.item.crafting.CraftingBookCategory getCategory(CraftingBookCategory bukkit) { return net.minecraft.world.item.crafting.CraftingBookCategory.valueOf(bukkit.name()); } - public static CraftingBookCategory getCategory(net.minecraft.world.item.crafting.CraftingBookCategory nms) { - return CraftingBookCategory.valueOf(nms.name()); + static CraftingBookCategory getCategory(net.minecraft.world.item.crafting.CraftingBookCategory internal) { + return CraftingBookCategory.valueOf(internal.name()); } - public static net.minecraft.world.item.crafting.CookingBookCategory getCategory(CookingBookCategory bukkit) { + static net.minecraft.world.item.crafting.CookingBookCategory getCategory(CookingBookCategory bukkit) { return net.minecraft.world.item.crafting.CookingBookCategory.valueOf(bukkit.name()); } - public static CookingBookCategory getCategory(net.minecraft.world.item.crafting.CookingBookCategory nms) { - return CookingBookCategory.valueOf(nms.name()); - } - - public static ResourceKey> toMinecraft(NamespacedKey key) { - return ResourceKey.create(Registries.RECIPE, CraftNamespacedKey.toMinecraft(key)); + static CookingBookCategory getCategory(net.minecraft.world.item.crafting.CookingBookCategory internal) { + return CookingBookCategory.valueOf(internal.name()); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapedRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapedRecipe.java index 31da9c526135..bc509ebe9fdc 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapedRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapedRecipe.java @@ -3,11 +3,13 @@ import com.google.common.collect.Maps; import java.util.Map; import java.util.Objects; +import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.item.crafting.ShapedRecipePattern; import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.RecipeChoice; import org.bukkit.inventory.ShapedRecipe; @@ -36,23 +38,29 @@ public static CraftShapedRecipe fromBukkitRecipe(ShapedRecipe recipe) { ret.shape(shape); Map ingredientMap = recipe.getChoiceMap(); for (char c : ingredientMap.keySet()) { - RecipeChoice stack = ingredientMap.get(c); - if (stack != null) { - ret.setIngredient(c, stack); + RecipeChoice ingredient = ingredientMap.get(c); + if (ingredient != null) { + ret.setIngredient(c, ingredient); } } return ret; } @Override - public void addToCraftingManager() { - Map ingred = this.getChoiceMap(); - String[] shape = CraftShapedRecipe.replaceUndefinedIngredientsWithEmpty(this.getShape(), ingred); - ingred.values().removeIf(Objects::isNull); - Map data = Maps.transformValues(ingred, (bukkit) -> this.toNMS(bukkit, false)); + public void addToRecipeManager() { + Map choices = this.getChoiceMap(); + String[] shape = CraftShapedRecipe.replaceUndefinedIngredientsWithEmpty(this.getShape(), choices); + choices.values().removeIf(Objects::isNull); + Map ingredients = Maps.transformValues(choices, (bukkit) -> CraftRecipe.toIngredient(bukkit, false)); + ShapedRecipePattern pattern = ShapedRecipePattern.of(ingredients, shape); - ShapedRecipePattern pattern = ShapedRecipePattern.of(data, shape); - MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.ShapedRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), pattern, CraftItemStack.asNMSCopy(this.getResult())))); + net.minecraft.world.item.crafting.ShapedRecipe recipe = new net.minecraft.world.item.crafting.ShapedRecipe( + new net.minecraft.world.item.crafting.Recipe.CommonInfo(true), + new net.minecraft.world.item.crafting.CraftingRecipe.CraftingBookInfo(CraftRecipe.getCategory(this.getCategory()), this.getGroup()), + pattern, + CraftItemStack.asTemplate(this.getResult()) + ); + MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toResourceKey(Registries.RECIPE, this.getKey()), recipe)); } private static String[] replaceUndefinedIngredientsWithEmpty(String[] shape, Map ingredients) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java index 7c989318dc7a..6a7bb225f044 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java @@ -2,10 +2,12 @@ import java.util.ArrayList; import java.util.List; +import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.RecipeHolder; import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.RecipeChoice; import org.bukkit.inventory.ShapelessRecipe; @@ -30,20 +32,26 @@ public static CraftShapelessRecipe fromBukkitRecipe(ShapelessRecipe recipe) { CraftShapelessRecipe ret = new CraftShapelessRecipe(recipe.getKey(), recipe.getResult()); ret.setGroup(recipe.getGroup()); ret.setCategory(recipe.getCategory()); - for (RecipeChoice ingred : recipe.getChoiceList()) { - ret.addIngredient(ingred); + for (RecipeChoice ingredient : recipe.getChoiceList()) { + ret.addIngredient(ingredient); } return ret; } @Override - public void addToCraftingManager() { - List ingred = this.getChoiceList(); - List data = new ArrayList<>(ingred.size()); - for (org.bukkit.inventory.RecipeChoice i : ingred) { - data.add(this.toNMS(i, true)); + public void addToRecipeManager() { + List choices = this.getChoiceList(); + List ingredients = new ArrayList<>(choices.size()); + for (org.bukkit.inventory.RecipeChoice choice : choices) { + ingredients.add(CraftRecipe.toIngredient(choice, true)); } - MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.ShapelessRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), CraftItemStack.asNMSCopy(this.getResult()), data))); + net.minecraft.world.item.crafting.ShapelessRecipe recipe = new net.minecraft.world.item.crafting.ShapelessRecipe( + new net.minecraft.world.item.crafting.Recipe.CommonInfo(true), + new net.minecraft.world.item.crafting.CraftingRecipe.CraftingBookInfo(CraftRecipe.getCategory(this.getCategory()), this.getGroup()), + CraftItemStack.asTemplate(this.getResult()), + ingredients + ); + MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toResourceKey(Registries.RECIPE, this.getKey()), recipe)); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java index fd4297be2565..c85e357efed6 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java @@ -1,9 +1,10 @@ package org.bukkit.craftbukkit.inventory; +import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; import net.minecraft.world.item.crafting.RecipeHolder; -import net.minecraft.world.item.crafting.TransmuteResult; import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.RecipeChoice; import org.bukkit.inventory.SmithingTransformRecipe; @@ -22,21 +23,19 @@ public static CraftSmithingTransformRecipe fromBukkitRecipe(SmithingTransformRec if (recipe instanceof CraftSmithingTransformRecipe) { return (CraftSmithingTransformRecipe) recipe; } - CraftSmithingTransformRecipe ret = new CraftSmithingTransformRecipe(recipe.getKey(), recipe.getResult(), recipe.getTemplate(), recipe.getBase(), recipe.getAddition(), recipe.willCopyDataComponents()); // Paper - Option to prevent data components copy - return ret; + return new CraftSmithingTransformRecipe(recipe.getKey(), recipe.getResult(), recipe.getTemplate(), recipe.getBase(), recipe.getAddition(), recipe.willCopyDataComponents()); } @Override - public void addToCraftingManager() { - ItemStack result = this.getResult(); - final net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(result); + public void addToRecipeManager() { final net.minecraft.world.item.crafting.SmithingTransformRecipe recipe = new net.minecraft.world.item.crafting.SmithingTransformRecipe( - this.toNMSOptional(this.getTemplate(), false), - this.toNMS(this.getBase(), false), - this.toNMSOptional(this.getAddition(), false), - new TransmuteResult(nmsStack.getItemHolder(), nmsStack.getCount(), nmsStack.getComponentsPatch()) - , this.willCopyDataComponents() + new net.minecraft.world.item.crafting.Recipe.CommonInfo(true), + CraftRecipe.toPossibleIngredient(this.getTemplate(), false), + CraftRecipe.toIngredient(this.getBase(), false), + CraftRecipe.toPossibleIngredient(this.getAddition(), false), + CraftItemStack.asTemplate(this.getResult()), + this.willCopyDataComponents() ); - MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), recipe)); // Paper - Option to prevent data components copy + MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toResourceKey(Registries.RECIPE, this.getKey()), recipe)); // Paper - Option to prevent data components copy } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java index 868d6dce5afe..53f74584425e 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java @@ -1,9 +1,11 @@ package org.bukkit.craftbukkit.inventory; +import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; import net.minecraft.world.item.crafting.RecipeHolder; import org.bukkit.NamespacedKey; import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.RecipeChoice; import org.bukkit.inventory.SmithingTrimRecipe; import org.bukkit.inventory.meta.trim.TrimPattern; @@ -20,22 +22,22 @@ public CraftSmithingTrimRecipe(NamespacedKey key, RecipeChoice template, RecipeC // Paper end - Option to prevent data components copy public static CraftSmithingTrimRecipe fromBukkitRecipe(SmithingTrimRecipe recipe) { - if (recipe instanceof CraftSmithingTrimRecipe) { - return (CraftSmithingTrimRecipe) recipe; + if (recipe instanceof CraftSmithingTrimRecipe smithingTrimRecipe) { + return smithingTrimRecipe; } - CraftSmithingTrimRecipe ret = new CraftSmithingTrimRecipe(recipe.getKey(), recipe.getTemplate(), recipe.getBase(), recipe.getAddition(), recipe.getTrimPattern(), recipe.willCopyDataComponents()); // Paper - Option to prevent data components copy - return ret; + return new CraftSmithingTrimRecipe(recipe.getKey(), recipe.getTemplate(), recipe.getBase(), recipe.getAddition(), recipe.getTrimPattern(), recipe.willCopyDataComponents()); } @Override - public void addToCraftingManager() { + public void addToRecipeManager() { final net.minecraft.world.item.crafting.SmithingTrimRecipe recipe = new net.minecraft.world.item.crafting.SmithingTrimRecipe( - this.toNMS(this.getTemplate(), false), - this.toNMS(this.getBase(), false), - this.toNMS(this.getAddition(), false), + new net.minecraft.world.item.crafting.Recipe.CommonInfo(true), + CraftRecipe.toIngredient(this.getTemplate(), false), + CraftRecipe.toIngredient(this.getBase(), false), + CraftRecipe.toIngredient(this.getAddition(), false), CraftTrimPattern.bukkitToMinecraftHolder(this.getTrimPattern()), this.willCopyDataComponents() ); - MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), recipe)); + MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toResourceKey(Registries.RECIPE, this.getKey()), recipe)); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmokingRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmokingRecipe.java index 2b5738399741..25b5c019e63d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmokingRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmokingRecipe.java @@ -1,8 +1,10 @@ package org.bukkit.craftbukkit.inventory; +import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; import net.minecraft.world.item.crafting.RecipeHolder; import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.RecipeChoice; import org.bukkit.inventory.SmokingRecipe; @@ -23,9 +25,15 @@ public static CraftSmokingRecipe fromBukkitRecipe(SmokingRecipe recipe) { } @Override - public void addToCraftingManager() { - ItemStack result = this.getResult(); - - MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.SmokingRecipe(this.getGroup(), CraftRecipe.getCategory(this.getCategory()), this.toNMS(this.getInputChoice(), true), CraftItemStack.asNMSCopy(result), this.getExperience(), this.getCookingTime()))); + public void addToRecipeManager() { + net.minecraft.world.item.crafting.SmokingRecipe recipe = new net.minecraft.world.item.crafting.SmokingRecipe( + new net.minecraft.world.item.crafting.Recipe.CommonInfo(true), + new net.minecraft.world.item.crafting.AbstractCookingRecipe.CookingBookInfo(CraftRecipe.getCategory(this.getCategory()), this.getGroup()), + CraftRecipe.toIngredient(this.getInputChoice(), true), + CraftItemStack.asTemplate(this.getResult()), + this.getExperience(), + this.getCookingTime() + ); + MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toResourceKey(Registries.RECIPE, this.getKey()), recipe)); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftStonecuttingRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftStonecuttingRecipe.java index 723701283fb9..4edce3a4dc6f 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftStonecuttingRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftStonecuttingRecipe.java @@ -1,8 +1,11 @@ package org.bukkit.craftbukkit.inventory; +import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.StonecutterRecipe; import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.RecipeChoice; import org.bukkit.inventory.StonecuttingRecipe; @@ -13,18 +16,19 @@ public CraftStonecuttingRecipe(NamespacedKey key, ItemStack result, RecipeChoice } public static CraftStonecuttingRecipe fromBukkitRecipe(StonecuttingRecipe recipe) { - if (recipe instanceof CraftStonecuttingRecipe) { - return (CraftStonecuttingRecipe) recipe; + if (recipe instanceof CraftStonecuttingRecipe stonecuttingRecipe) { + return stonecuttingRecipe; } - CraftStonecuttingRecipe ret = new CraftStonecuttingRecipe(recipe.getKey(), recipe.getResult(), recipe.getInputChoice()); - ret.setGroup(recipe.getGroup()); - return ret; + return new CraftStonecuttingRecipe(recipe.getKey(), recipe.getResult(), recipe.getInputChoice()); } @Override - public void addToCraftingManager() { - ItemStack result = this.getResult(); - - MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.StonecutterRecipe(this.getGroup(), this.toNMS(this.getInputChoice(), true), CraftItemStack.asNMSCopy(result)))); + public void addToRecipeManager() { + StonecutterRecipe recipe = new net.minecraft.world.item.crafting.StonecutterRecipe( + new net.minecraft.world.item.crafting.Recipe.CommonInfo(true), + CraftRecipe.toIngredient(this.getInputChoice(), true), + CraftItemStack.asTemplate(this.getResult()) + ); + MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toResourceKey(Registries.RECIPE, this.getKey()), recipe)); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftTransmuteRecipe.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftTransmuteRecipe.java index 76716d066550..38c2061ab4d1 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftTransmuteRecipe.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftTransmuteRecipe.java @@ -1,12 +1,11 @@ package org.bukkit.craftbukkit.inventory; -import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; import net.minecraft.server.MinecraftServer; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeHolder; -import net.minecraft.world.item.crafting.TransmuteResult; import org.bukkit.Material; import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.RecipeChoice; import org.bukkit.inventory.TransmuteRecipe; @@ -27,17 +26,16 @@ public static CraftTransmuteRecipe fromBukkitRecipe(TransmuteRecipe recipe) { } @Override - public void addToCraftingManager() { - final ItemStack unwrappedInternalStack = CraftItemStack.unwrap(this.getResult()); - MinecraftServer.getServer().getRecipeManager().addRecipe( - new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), - new net.minecraft.world.item.crafting.TransmuteRecipe(this.getGroup(), - CraftRecipe.getCategory(this.getCategory()), - this.toNMS(this.getInput(), true), - this.toNMS(this.getMaterial(), true), - new TransmuteResult(unwrappedInternalStack.getItemHolder(), unwrappedInternalStack.getCount(), unwrappedInternalStack.getComponentsPatch()) - ) - ) + public void addToRecipeManager() { + net.minecraft.world.item.crafting.TransmuteRecipe recipe = new net.minecraft.world.item.crafting.TransmuteRecipe( + new net.minecraft.world.item.crafting.Recipe.CommonInfo(true), + new net.minecraft.world.item.crafting.CraftingRecipe.CraftingBookInfo(CraftRecipe.getCategory(this.getCategory()), this.getGroup()), + CraftRecipe.toIngredient(this.getInput(), true), + CraftRecipe.toIngredient(this.getMaterial(), true), + net.minecraft.world.item.crafting.TransmuteRecipe.DEFAULT_MATERIAL_COUNT, + CraftItemStack.asTemplate(this.getResult()), + false ); + MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toResourceKey(Registries.RECIPE, this.getKey()), recipe)); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftEquippableComponent.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftEquippableComponent.java index 9db372844211..093e1dbb3d92 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftEquippableComponent.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftEquippableComponent.java @@ -10,8 +10,8 @@ import net.minecraft.core.HolderSet; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.item.equipment.EquipmentAssets; import net.minecraft.world.item.equipment.Equippable; @@ -50,7 +50,8 @@ public CraftEquippableComponent(Map map) { Sound equipSound = null; String equipSoundKey = SerializableMeta.getString(map, "equip-sound", true); if (equipSoundKey != null) { - equipSound = Registry.SOUNDS.get(NamespacedKey.fromString(equipSoundKey)); + NamespacedKey key = NamespacedKey.fromString(equipSoundKey); + equipSound = key == null ? null : Registry.SOUNDS.get(key); } String model = SerializableMeta.getString(map, "model", true); @@ -67,16 +68,25 @@ public CraftEquippableComponent(Map map) { Boolean damageOnHurt = SerializableMeta.getObject(Boolean.class, map, "damage-on-hurt", true); Boolean equipOnInteract = SerializableMeta.getObject(Boolean.class, map, "equip-on-interact", true); + Boolean canBeSheared = SerializableMeta.getObject(Boolean.class, map, "can-be-sheared", true); + Sound shearingSound = null; + String shearingSoundKey = SerializableMeta.getString(map, "shearing-sound", true); + if (shearingSoundKey != null) { + NamespacedKey key = NamespacedKey.fromString(shearingSoundKey); + shearingSound = key == null ? null : Registry.SOUNDS.get(key); + } + this.handle = new Equippable(slot, - (equipSound != null) ? CraftSound.bukkitToMinecraftHolder(equipSound) : SoundEvents.ARMOR_EQUIP_GENERIC, - Optional.ofNullable(model).map(Identifier::parse).map((k) -> ResourceKey.create(EquipmentAssets.ROOT_ID, k)), - Optional.ofNullable(cameraOverlay).map(Identifier::parse), - Optional.ofNullable(allowedEntities), - (dispensable != null) ? dispensable : true, - (swappable != null) ? swappable : true, - (damageOnHurt != null) ? damageOnHurt : true, - (equipOnInteract != null) ? equipOnInteract : false, - false, BuiltInRegistries.SOUND_EVENT.wrapAsHolder(SoundEvents.SHEARS_SNIP) // TODO - 1.21.6 + equipSound != null ? CraftSound.bukkitToMinecraftHolder(equipSound) : SoundEvents.ARMOR_EQUIP_GENERIC, + Optional.ofNullable(model).map(Identifier::parse).map((k) -> ResourceKey.create(EquipmentAssets.ROOT_ID, k)), + Optional.ofNullable(cameraOverlay).map(Identifier::parse), + Optional.ofNullable(allowedEntities), + (dispensable != null) ? dispensable : true, + (swappable != null) ? swappable : true, + (damageOnHurt != null) ? damageOnHurt : true, + (equipOnInteract != null) ? equipOnInteract : false, + (canBeSheared != null) ? canBeSheared : false, + shearingSound != null ? CraftSound.bukkitToMinecraftHolder(shearingSound) : BuiltInRegistries.SOUND_EVENT.wrapAsHolder(SoundEvents.SHEARS_SNIP) ); } @@ -102,6 +112,11 @@ public Map serialize() { result.put("swappable", this.isSwappable()); result.put("damage-on-hurt", this.isDamageOnHurt()); result.put("equip-on-interact", this.isEquipOnInteract()); + result.put("can-be-sheared", this.canBeSheared()); + Sound shearingSound = this.getShearingSound(); + if (shearingSound != null) { + result.put("shearing-sound", Registry.SOUND_EVENT.getKeyOrThrow(shearingSound).toString()); + } return result; } @@ -122,7 +137,7 @@ public void setSlot(EquipmentSlot slot) { @Override public Sound getEquipSound() { - return CraftSound.minecraftToBukkit(this.handle.equipSound().value()); + return CraftSound.minecraftHolderToBukkit(this.handle.equipSound()); } @Override @@ -158,16 +173,16 @@ public Collection getAllowedEntities() { @Override public void setAllowedEntities(EntityType entities) { this.handle = new Equippable(this.handle.slot(), this.handle.equipSound(), this.handle.assetId(), this.handle.cameraOverlay(), - (entities != null) ? Optional.of(HolderSet.direct(CraftEntityType.bukkitToMinecraftHolder(entities))) : Optional.empty(), - this.handle.dispensable(), this.handle.swappable(), this.handle.damageOnHurt(), this.handle.equipOnInteract(), this.handle.canBeSheared(), this.handle.shearingSound() + (entities != null) ? Optional.of(HolderSet.direct(CraftEntityType.bukkitToMinecraftHolder(entities))) : Optional.empty(), + this.handle.dispensable(), this.handle.swappable(), this.handle.damageOnHurt(), this.handle.equipOnInteract(), this.handle.canBeSheared(), this.handle.shearingSound() ); } @Override public void setAllowedEntities(Collection entities) { this.handle = new Equippable(this.handle.slot(), this.handle.equipSound(), this.handle.assetId(), this.handle.cameraOverlay(), - (entities != null) ? Optional.of(HolderSet.direct(entities.stream().map(CraftEntityType::bukkitToMinecraftHolder).collect(Collectors.toList()))) : Optional.empty(), - this.handle.dispensable(), this.handle.swappable(), this.handle.damageOnHurt(), this.handle.equipOnInteract(), this.handle.canBeSheared(), this.handle.shearingSound() + (entities != null) ? Optional.of(HolderSet.direct(entities.stream().map(CraftEntityType::bukkitToMinecraftHolder).collect(Collectors.toList()))) : Optional.empty(), + this.handle.dispensable(), this.handle.swappable(), this.handle.damageOnHurt(), this.handle.equipOnInteract(), this.handle.canBeSheared(), this.handle.shearingSound() ); } @@ -176,8 +191,8 @@ public void setAllowedEntities(Tag tag) { Preconditions.checkArgument(tag == null || tag instanceof CraftEntityTag, "tag must be an entity tag"); // Paper this.handle = new Equippable(this.handle.slot(), this.handle.equipSound(), this.handle.assetId(), this.handle.cameraOverlay(), - (tag != null) ? Optional.of(((CraftEntityTag) tag).getHandle()) : Optional.empty(), - this.handle.dispensable(), this.handle.swappable(), this.handle.damageOnHurt(), this.handle.equipOnInteract(), this.handle.canBeSheared(), this.handle.shearingSound() + (tag != null) ? Optional.of(((CraftEntityTag) tag).getHandle()) : Optional.empty(), + this.handle.dispensable(), this.handle.swappable(), this.handle.damageOnHurt(), this.handle.equipOnInteract(), this.handle.canBeSheared(), this.handle.shearingSound() ); } @@ -221,6 +236,26 @@ public void setEquipOnInteract(final boolean equip) { this.handle = new Equippable(this.handle.slot(), this.handle.equipSound(), this.handle.assetId(), this.handle.cameraOverlay(), this.handle.allowedEntities(), this.handle.dispensable(), this.handle.swappable(), this.handle.damageOnHurt(), equip, this.handle.canBeSheared(), this.handle.shearingSound()); } + @Override + public boolean canBeSheared() { + return this.handle.canBeSheared(); + } + + @Override + public void setCanBeSheared(boolean sheared) { + this.handle = new Equippable(this.handle.slot(), this.handle.equipSound(), this.handle.assetId(), this.handle.cameraOverlay(), this.handle.allowedEntities(), this.handle.dispensable(), this.handle.swappable(), this.handle.damageOnHurt(), this.handle.equipOnInteract(), sheared, this.handle.shearingSound()); + } + + @Override + public Sound getShearingSound() { + return CraftSound.minecraftHolderToBukkit(this.handle.shearingSound()); + } + + @Override + public void setShearingSound(Sound sound) { + this.handle = new Equippable(this.handle.slot(), this.handle.equipSound(), this.handle.assetId(), this.handle.cameraOverlay(), this.handle.allowedEntities(), this.handle.dispensable(), this.handle.swappable(), this.handle.damageOnHurt(), this.handle.equipOnInteract(), this.handle.canBeSheared(), (sound != null) ? CraftSound.bukkitToMinecraftHolder(sound) : BuiltInRegistries.SOUND_EVENT.wrapAsHolder(SoundEvents.SHEARS_SNIP)); + } + @Override public int hashCode() { int hash = 7; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftJukeboxComponent.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftJukeboxComponent.java index 616d409a8753..adf8660bf457 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftJukeboxComponent.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftJukeboxComponent.java @@ -3,12 +3,9 @@ import com.google.common.base.Preconditions; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Optional; import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; import net.minecraft.resources.Identifier; -import net.minecraft.world.item.EitherHolder; import net.minecraft.world.item.JukeboxPlayable; import org.bukkit.JukeboxSong; import org.bukkit.NamespacedKey; @@ -35,7 +32,9 @@ public CraftJukeboxComponent(CraftJukeboxComponent jukebox) { public CraftJukeboxComponent(Map map) { String song = SerializableMeta.getObject(String.class, map, "song", false); - this.handle = new JukeboxPlayable(new EitherHolder<>(ResourceKey.create(Registries.JUKEBOX_SONG, Identifier.parse(song)))); + final net.minecraft.core.Registry registry = CraftRegistry.getMinecraftRegistry(Registries.JUKEBOX_SONG); + final Holder.Reference holder = registry.get(Identifier.parse(song)).orElseThrow(); + this.handle = new JukeboxPlayable(holder); } @Override @@ -51,27 +50,28 @@ public JukeboxPlayable getHandle() { @Override public JukeboxSong getSong() { - Optional> song = this.handle.song().unwrap(CraftRegistry.getMinecraftRegistry()); - return song.map(CraftJukeboxSong::minecraftHolderToBukkit).orElse(null); + return CraftJukeboxSong.minecraftHolderToBukkit(this.handle.song()); } @Override public NamespacedKey getSongKey() { - return CraftNamespacedKey.fromMinecraft(this.handle.song().key().orElseThrow().identifier()); + return CraftNamespacedKey.fromMinecraft(this.handle.song().unwrapKey().orElseThrow().identifier()); } @Override public void setSong(JukeboxSong song) { Preconditions.checkArgument(song != null, "song cannot be null"); - this.handle = new JukeboxPlayable(new EitherHolder<>(CraftJukeboxSong.bukkitToMinecraftHolder(song))); + this.handle = new JukeboxPlayable(CraftJukeboxSong.bukkitToMinecraftHolder(song)); } @Override public void setSongKey(NamespacedKey song) { Preconditions.checkArgument(song != null, "song cannot be null"); - this.handle = new JukeboxPlayable(new EitherHolder<>(ResourceKey.create(Registries.JUKEBOX_SONG, CraftNamespacedKey.toMinecraft(song)))); + final net.minecraft.core.Registry registry = CraftRegistry.getMinecraftRegistry(Registries.JUKEBOX_SONG); + final Holder.Reference holder = registry.get(CraftNamespacedKey.toMinecraft(song)).orElseThrow(); + this.handle = new JukeboxPlayable(holder); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftMenus.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftMenus.java index 7a33cabd19c7..707148e0bcb3 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftMenus.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftMenus.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.inventory.util; +import java.util.function.Supplier; import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.npc.villager.Villager; @@ -41,8 +42,6 @@ import org.bukkit.inventory.view.builder.InventoryViewBuilder; import org.jspecify.annotations.NullMarked; -import java.util.function.Supplier; - @NullMarked public final class CraftMenus { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/builder/CraftAbstractLocationInventoryViewBuilder.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/builder/CraftAbstractLocationInventoryViewBuilder.java index 15f1b3016003..38fa12815ed8 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/builder/CraftAbstractLocationInventoryViewBuilder.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/builder/CraftAbstractLocationInventoryViewBuilder.java @@ -42,7 +42,7 @@ public LocationInventoryViewBuilder location(final Location location) { Preconditions.checkArgument(location != null, "The provided location must not be null"); Preconditions.checkArgument(location.getWorld() != null, "The provided location must be associated with a world"); this.world = ((CraftWorld) location.getWorld()).getHandle(); - this.position = CraftLocation.toBlockPosition(location); + this.position = CraftLocation.toBlockPos(location); return this; } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java index fc24964d8921..883a92b134da 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java @@ -47,8 +47,8 @@ public final class CraftLegacy { private static final Set whitelistedStates = new HashSet<>(Arrays.asList("explode", "check_decay", "decayable", "facing")); private static final Map materialToItem = new HashMap<>(16384); private static final Map itemToMaterial = new HashMap<>(1024); - private static final Map materialToData = new HashMap<>(4096); - private static final Map dataToMaterial = new HashMap<>(4096); + private static final Map materialToState = new HashMap<>(4096); + private static final Map stateToMaterial = new HashMap<>(4096); private static final Map materialToBlock = new HashMap<>(4096); private static final Map blockToMaterial = new HashMap<>(1024); @@ -81,7 +81,7 @@ public static MaterialData toLegacyData(Material material, boolean itemPriority) BlockState state = block.defaultBlockState(); // Try exact match first - mappedData = CraftLegacy.dataToMaterial.get(state); + mappedData = CraftLegacy.stateToMaterial.get(state); // Fallback to any block if (mappedData == null) { mappedData = CraftLegacy.blockToMaterial.get(block); @@ -104,7 +104,7 @@ public static BlockState fromLegacyData(Material material, byte data) { MaterialData materialData = new MaterialData(material, data); // Try exact match first - BlockState converted = CraftLegacy.materialToData.get(materialData); + BlockState converted = CraftLegacy.materialToState.get(materialData); if (converted != null) { return converted; } @@ -133,7 +133,7 @@ public static Item fromLegacyData(Material material, short data) { // Fallback to matching block if (material.isBlock()) { // Try exact match first - BlockState converted = CraftLegacy.materialToData.get(materialData); + BlockState converted = CraftLegacy.materialToState.get(materialData); if (converted != null) { return converted.getBlock().asItem(); } @@ -161,7 +161,7 @@ public static MaterialData toLegacy(BlockState state) { MaterialData mappedData; // Try exact match first - mappedData = CraftLegacy.dataToMaterial.get(state); + mappedData = CraftLegacy.stateToMaterial.get(state); // Fallback to any block if (mappedData == null) { mappedData = CraftLegacy.blockToMaterial.get(state.getBlock()); @@ -200,7 +200,7 @@ public static Material fromLegacy(MaterialData materialData, boolean itemPriorit if (mappedData == null) { // Try exact match first - BlockState iblock = CraftLegacy.materialToData.get(materialData); + BlockState iblock = CraftLegacy.materialToState.get(materialData); if (iblock != null) { mappedData = CraftMagicNumbers.getMaterial(iblock.getBlock()); } @@ -342,25 +342,25 @@ public static void init() { if (block == null) { continue; } - BlockState blockData = block.defaultBlockState(); - StateDefinition states = block.getStateDefinition(); + BlockState state = block.defaultBlockState(); + StateDefinition def = block.getStateDefinition(); - Optional propMap = blockTag.getElement("Properties").result(); - if (propMap.isPresent()) { - CompoundTag properties = propMap.get(); - for (String dataKey : properties.keySet()) { - Property state = states.getProperty(dataKey); + Optional propertiesTag = blockTag.getElement("Properties").result(); + if (propertiesTag.isPresent()) { + CompoundTag properties = propertiesTag.get(); + for (String propertyName : properties.keySet()) { + Property property = def.getProperty(propertyName); - if (state == null) { - Preconditions.checkArgument(whitelistedStates.contains(dataKey), "No state for %s", dataKey); + if (property == null) { + Preconditions.checkArgument(whitelistedStates.contains(propertyName), "No property for %s", propertyName); continue; } - Preconditions.checkState(properties.getString(dataKey).isPresent(), "Empty data string"); - Optional opt = state.getValue(properties.getStringOr(dataKey, "")); - Preconditions.checkArgument(opt.isPresent(), "No state value %s for %s", properties.getString(dataKey), dataKey); + Preconditions.checkState(properties.getString(propertyName).isPresent(), "Empty data string"); + Optional opt = property.getValue(properties.getStringOr(propertyName, "")); + Preconditions.checkArgument(opt.isPresent(), "No state value %s for %s", properties.getString(propertyName), propertyName); - blockData = blockData.setValue(state, (Comparable) opt.get()); + state = state.setValue(property, (Comparable) opt.get()); } } @@ -368,9 +368,9 @@ public static void init() { continue; } - materialToData.put(matData, blockData); - if (!dataToMaterial.containsKey(blockData)) { - dataToMaterial.put(blockData, matData); + materialToState.put(matData, state); + if (!stateToMaterial.containsKey(state)) { + stateToMaterial.put(state, matData); } materialToBlock.put(matData, block); @@ -413,13 +413,13 @@ public static void init() { LEGACY_ELYTRA -> 1; default -> 16; }; - // Manually do oldold spawn eggs + // Manually do old spawn eggs if (material == Material.LEGACY_MONSTER_EGG) { - maxData = 121; // Vilager + 1 + maxData = 121; // Villager + 1 } for (byte data = 0; data < maxData; data++) { - // Manually skip invalid oldold spawn + // Manually skip invalid old spawn if (material == Material.LEGACY_MONSTER_EGG /*&& data != 0 && EntityType.fromId(data) == null*/) { // Mojang broke 18w19b continue; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java index 9bc8055c3632..a76e89c12afd 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java @@ -24,7 +24,6 @@ import org.bukkit.block.DecoratedPot; import org.bukkit.block.Jukebox; import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.inventory.CraftItemType; import org.bukkit.craftbukkit.legacy.reroute.InjectPluginVersion; import org.bukkit.craftbukkit.legacy.reroute.RerouteStatic; @@ -252,7 +251,7 @@ public static Set getBarterList(Piglin piglin, @InjectPluginVersion Ap @Deprecated public static void sendBlockChange(Player player, Location location, Material material, byte data) { - player.sendBlockChange(location, CraftBlockData.fromData(CraftMagicNumbers.getBlock(material, data))); + player.sendBlockChange(location, CraftMagicNumbers.getBlock(material, data).asBlockData()); } public static Material getSteerMaterial(Steerable steerable, @InjectPluginVersion ApiVersion version) { @@ -573,7 +572,7 @@ public static Set getValues(Tag tag, @InjectPluginVersio @Deprecated public static FallingBlock spawnFallingBlock(World world, Location location, Material material, byte data) { - return world.spawnFallingBlock(location, CraftBlockData.fromData(CraftMagicNumbers.getBlock(material, data))); + return world.spawnFallingBlock(location, CraftMagicNumbers.getBlock(material, data).asBlockData()); } public static ToolComponent.ToolRule addRule(ToolComponent toolComponent, Material block, Float speed, Boolean correctForDrops) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java b/paper-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java index 4881e366c8d4..6748e1b45b5e 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java @@ -61,7 +61,7 @@ public NamespacedKey getKey() { @Override public double getDurationModifier() { - return 1.0D; + return 1.0; } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionType.java b/paper-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionType.java index a304fafd78f6..c42f79fec497 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionType.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionType.java @@ -94,7 +94,7 @@ public CraftPotionType(NamespacedKey key, Potion potion) { @Override public PotionEffectType getEffectType() { - return this.getPotionEffects().isEmpty() ? null : this.getPotionEffects().get(0).getType(); + return this.getPotionEffects().isEmpty() ? null : this.getPotionEffects().getFirst().getType(); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java b/paper-server/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java index 4d4c7861323a..30a11ae7e872 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java @@ -6,6 +6,8 @@ import com.mojang.authlib.properties.Property; import com.mojang.authlib.properties.PropertyMap; import com.mojang.authlib.yggdrasil.ProfileResult; +import com.mojang.datafixers.util.Either; +import io.papermc.paper.profile.MutablePropertyMap; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; @@ -15,12 +17,9 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; -import com.mojang.datafixers.util.Either; -import io.papermc.paper.profile.MutablePropertyMap; -import net.minecraft.util.Util; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.players.NameAndId; +import net.minecraft.util.Util; import net.minecraft.world.entity.player.PlayerSkin; import net.minecraft.world.item.component.ResolvableProfile; import org.apache.commons.lang3.StringUtils; @@ -38,24 +37,6 @@ @SerializableAs("PlayerProfile") public final class CraftPlayerProfile implements PlayerProfile, com.destroystokyo.paper.profile.SharedPlayerProfile, com.destroystokyo.paper.profile.PlayerProfile { // Paper - public static GameProfile validateSkullProfile(GameProfile gameProfile) { - // The GameProfile needs to contain either both a uuid and textures, or a name. - // The GameProfile always has a name or a uuid, so checking if it has a name is sufficient. - boolean isValidSkullProfile = (gameProfile.name() != null) - || gameProfile.properties().containsKey(CraftPlayerTextures.PROPERTY_NAME); - Preconditions.checkArgument(isValidSkullProfile, "The skull profile is missing a name or textures!"); - Preconditions.checkArgument(gameProfile.name().length() <= 16, "The name of the profile is longer than 16 characters"); - Preconditions.checkArgument(net.minecraft.util.StringUtil.isValidPlayerName(gameProfile.name()), "The name of the profile contains invalid characters: %s", gameProfile.name()); - final PropertyMap properties = gameProfile.properties(); - Preconditions.checkArgument(properties.size() <= 16, "The profile contains more than 16 properties"); - for (final Property property : properties.values()) { - Preconditions.checkArgument(property.name().length() <= 64, "The name of a property is longer than 64 characters"); - Preconditions.checkArgument(property.value().length() <= Short.MAX_VALUE, "The value of a property is longer than 32767 characters"); - Preconditions.checkArgument(property.signature() == null || property.signature().length() <= 1024, "The signature of a property is longer than 1024 characters"); - } - return gameProfile; - } - public static @Nullable Property getProperty(GameProfile profile, String propertyName) { return Iterables.getFirst(profile.properties().get(propertyName), null); } @@ -213,23 +194,11 @@ public String toString() { builder.append(", name="); builder.append(this.getName()); builder.append(", properties="); - builder.append(CraftPlayerProfile.toString(this.properties)); + builder.append(this.properties); builder.append("]"); return builder.toString(); } - public static String toString(PropertyMap propertyMap) { - StringBuilder builder = new StringBuilder(); - builder.append("{"); - propertyMap.asMap().forEach((propertyName, properties) -> { - builder.append(propertyName); - builder.append("="); - builder.append(properties.stream().map(CraftProfileProperty::toString).collect(Collectors.joining(",", "[", "]"))); - }); - builder.append("}"); - return builder.toString(); - } - @Override public boolean equals(Object obj) { if (this == obj) return true; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/profile/CraftProfileProperty.java b/paper-server/src/main/java/org/bukkit/craftbukkit/profile/CraftProfileProperty.java index 41dd3a79ba6e..c1ae3b8d9ffb 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/profile/CraftProfileProperty.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/profile/CraftProfileProperty.java @@ -83,19 +83,6 @@ public static String encodePropertyValue(JsonObject propertyValue, JsonFormatter return Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8)); } - public static String toString(Property property) { - StringBuilder builder = new StringBuilder(); - builder.append("{"); - builder.append("name="); - builder.append(property.name()); - builder.append(", value="); - builder.append(property.value()); - builder.append(", signature="); - builder.append(property.signature()); - builder.append("}"); - return builder.toString(); - } - public static int hashCode(Property property) { int result = 1; result = 31 * result + Objects.hashCode(property.name()); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/structure/CraftStructure.java b/paper-server/src/main/java/org/bukkit/craftbukkit/structure/CraftStructure.java index 8065aedbee7f..c54c45c3323b 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/structure/CraftStructure.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/structure/CraftStructure.java @@ -35,7 +35,7 @@ import org.bukkit.craftbukkit.util.CraftLocation; import org.bukkit.craftbukkit.util.CraftStructureTransformer; import org.bukkit.craftbukkit.util.RandomSourceWrapper; -import org.bukkit.craftbukkit.util.TransformerGeneratorAccess; +import org.bukkit.craftbukkit.util.TransformerLevelAccessor; import org.bukkit.entity.Entity; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.structure.Palette; @@ -100,12 +100,12 @@ public void place(RegionAccessor regionAccessor, BlockVector location, boolean i BlockPos pos = CraftBlockVector.toBlockPosition(location); WorldGenLevel handle = ((CraftRegionAccessor) regionAccessor).getHandle(); - TransformerGeneratorAccess access = new TransformerGeneratorAccess(); - access.setDelegate(handle); - access.setStructureTransformer(new CraftStructureTransformer(handle, new ChunkPos(pos), blockTransformers, entityTransformers)); + TransformerLevelAccessor accessor = new TransformerLevelAccessor(); + accessor.setDelegate(handle); + accessor.setStructureTransformer(new CraftStructureTransformer(handle, ChunkPos.containing(pos), blockTransformers, entityTransformers)); - this.structure.placeInWorld(access, pos, pos, definedstructureinfo, randomSource, Block.UPDATE_CLIENTS); - access.getStructureTransformer().discard(); + this.structure.placeInWorld(accessor, pos, pos, definedstructureinfo, randomSource, Block.UPDATE_CLIENTS); + accessor.getStructureTransformer().discard(); } @Override @@ -128,7 +128,7 @@ public void fill(Location origin, BlockVector size, boolean includeEntities) { Preconditions.checkArgument(size != null, "BlockVector size cannot be null"); Preconditions.checkArgument(size.getBlockX() >= 1 && size.getBlockY() >= 1 && size.getBlockZ() >= 1, "Size must be at least 1x1x1 but was %sx%sx%s", size.getBlockX(), size.getBlockY(), size.getBlockZ()); - this.structure.fillFromWorld(((CraftWorld) world).getHandle(), CraftLocation.toBlockPosition(origin), CraftBlockVector.toBlockPosition(size), includeEntities, List.of(Blocks.STRUCTURE_VOID)); + this.structure.fillFromWorld(((CraftWorld) world).getHandle(), CraftLocation.toBlockPos(origin), CraftBlockVector.toBlockPosition(size), includeEntities, List.of(Blocks.STRUCTURE_VOID)); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/structure/CraftStructureManager.java b/paper-server/src/main/java/org/bukkit/craftbukkit/structure/CraftStructureManager.java index 5cdc356f7288..a24343bdfa30 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/structure/CraftStructureManager.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/structure/CraftStructureManager.java @@ -19,6 +19,7 @@ import net.minecraft.resources.Identifier; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import net.minecraft.world.level.levelgen.structure.templatesystem.loader.TemplateSource; import org.bukkit.NamespacedKey; import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.structure.Structure; @@ -56,18 +57,16 @@ public Structure getStructure(NamespacedKey structureKey) { @Override public Structure loadStructure(NamespacedKey structureKey, boolean register) { - Identifier minecraftKey = this.createAndValidateMinecraftStructureKey(structureKey); + Identifier id = this.createAndValidateStructureId(structureKey); - Optional structure = this.structureManager.structureRepository.get(minecraftKey); - structure = structure == null ? Optional.empty() : structure; - structure = structure.isPresent() ? structure : this.structureManager.loadFromGenerated(minecraftKey); - structure = structure.isPresent() ? structure : this.structureManager.loadFromResource(minecraftKey); + Optional structure = this.structureManager.structureRepository.getOrDefault(id, Optional.empty()) + .or(() -> this.structureManager.tryLoad(id)); if (register) { - this.structureManager.structureRepository.put(minecraftKey, structure); + this.structureManager.structureRepository.put(id, structure); } - return structure.map((s) -> new CraftStructure(s, this.registry)).orElse(null); + return structure.map((template) -> new CraftStructure(template, this.registry)).orElse(null); } @Override @@ -77,9 +76,7 @@ public Structure loadStructure(NamespacedKey structureKey) { @Override public void saveStructure(NamespacedKey structureKey) { - Identifier minecraftKey = this.createAndValidateMinecraftStructureKey(structureKey); - - this.structureManager.save(minecraftKey); + this.structureManager.save(this.createAndValidateStructureId(structureKey)); } @Override @@ -96,19 +93,19 @@ public void saveStructure(NamespacedKey structureKey, Structure structure) throw public Structure registerStructure(NamespacedKey structureKey, Structure structure) { Preconditions.checkArgument(structureKey != null, "NamespacedKey structureKey cannot be null"); Preconditions.checkArgument(structure != null, "Structure cannot be null"); - Identifier minecraftKey = this.createAndValidateMinecraftStructureKey(structureKey); + Identifier id = this.createAndValidateStructureId(structureKey); final Optional optionalDefinedStructure = Optional.of(((CraftStructure) structure).getHandle()); - final Optional previousStructure = this.structureManager.structureRepository.put(minecraftKey, optionalDefinedStructure); + final Optional previousStructure = this.structureManager.structureRepository.put(id, optionalDefinedStructure); return previousStructure == null ? null : previousStructure.map((s) -> new CraftStructure(s, this.registry)).orElse(null); } @Override public Structure unregisterStructure(NamespacedKey structureKey) { Preconditions.checkArgument(structureKey != null, "NamespacedKey structureKey cannot be null"); - Identifier minecraftKey = this.createAndValidateMinecraftStructureKey(structureKey); + Identifier id = this.createAndValidateStructureId(structureKey); - final Optional previousStructure = this.structureManager.structureRepository.remove(minecraftKey); + final Optional previousStructure = this.structureManager.structureRepository.remove(id); return previousStructure == null ? null : previousStructure.map((s) -> new CraftStructure(s, this.registry)).orElse(null); } @@ -119,19 +116,19 @@ public void deleteStructure(NamespacedKey structureKey) throws IOException { @Override public void deleteStructure(NamespacedKey structureKey, boolean unregister) throws IOException { - Identifier key = CraftNamespacedKey.toMinecraft(structureKey); + Identifier id = CraftNamespacedKey.toMinecraft(structureKey); if (unregister) { - this.structureManager.structureRepository.remove(key); + this.structureManager.structureRepository.remove(id); } - Path path = this.structureManager.createAndValidatePathToGeneratedStructure(key, ".nbt"); + Path path = this.structureManager.worldTemplates().createAndValidatePathToStructure(id, StructureTemplateManager.WORLD_STRUCTURE_LISTER); Files.deleteIfExists(path); } @Override public File getStructureFile(NamespacedKey structureKey) { - Identifier minecraftKey = this.createAndValidateMinecraftStructureKey(structureKey); - return this.structureManager.createAndValidatePathToGeneratedStructure(minecraftKey, ".nbt").toFile(); + Identifier id = this.createAndValidateStructureId(structureKey); + return this.structureManager.worldTemplates().createAndValidatePathToStructure(id, StructureTemplateManager.WORLD_STRUCTURE_LISTER).toFile(); } @Override @@ -146,7 +143,7 @@ public Structure loadStructure(File file) throws IOException { public Structure loadStructure(InputStream inputStream) throws IOException { Preconditions.checkArgument(inputStream != null, "inputStream cannot be null"); - return new CraftStructure(this.structureManager.readStructure(inputStream), this.registry); + return new CraftStructure(this.structureManager.resourceManagerSource.readStructure(TemplateSource.readStructure(inputStream)), this.registry); } @Override @@ -172,17 +169,17 @@ public Structure createStructure() { return new CraftStructure(new StructureTemplate(), this.registry); } - private Identifier createAndValidateMinecraftStructureKey(NamespacedKey structureKey) { + private Identifier createAndValidateStructureId(NamespacedKey structureKey) { Preconditions.checkArgument(structureKey != null, "NamespacedKey structureKey cannot be null"); - Identifier key = CraftNamespacedKey.toMinecraft(structureKey); - Preconditions.checkArgument(!key.getPath().contains("//"), "Resource key for Structures can not contain \"//\""); - return key; + Identifier id = CraftNamespacedKey.toMinecraft(structureKey); + Preconditions.checkArgument(!id.getPath().contains("//"), "Resource id for Structures cannot contain \"//\""); + return id; } @Override public Structure copy(Structure structure) { Preconditions.checkArgument(structure != null, "Structure cannot be null"); - return new CraftStructure(this.structureManager.readStructure(((CraftStructure) structure).getHandle().save(new CompoundTag())), this.registry); + return new CraftStructure(this.structureManager.resourceManagerSource.readStructure(((CraftStructure) structure).getHandle().save(new CompoundTag())), this.registry); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/tag/CraftEntityTag.java b/paper-server/src/main/java/org/bukkit/craftbukkit/tag/CraftEntityTag.java index 4270b771ff15..6dec42b91702 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/tag/CraftEntityTag.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/tag/CraftEntityTag.java @@ -16,7 +16,7 @@ public CraftEntityTag(Registry> registr @Override public boolean isTagged(EntityType entity) { - return CraftEntityType.bukkitToMinecraft(entity).is(this.tag); + return CraftEntityType.bukkitToMinecraft(entity).builtInRegistryHolder().is(this.tag); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java index 989b2f751a59..dcd668fedc37 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java @@ -4,6 +4,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.Predicate; import net.minecraft.core.BlockPos; @@ -12,17 +13,20 @@ import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.storage.LevelData; import org.bukkit.block.BlockState; import org.bukkit.craftbukkit.block.CraftBlockState; import org.bukkit.craftbukkit.block.CraftBlockStates; -public class BlockStateListPopulator extends DummyGeneratorAccess { +public class BlockStateListPopulator extends DummyLevelAccessor { private final LevelAccessor level; private final Map blocks = new LinkedHashMap<>(); @@ -52,37 +56,37 @@ public BlockEntity getBlockEntity(BlockPos pos) { } @Override - public boolean setBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @Block.UpdateFlags int flags, int recursionLeft) { + public boolean setBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState blockState, @Block.UpdateFlags int updateFlags, int updateLimit) { pos = pos.immutable(); // remove first to keep last updated order this.blocks.remove(pos); final BlockEntity newBlockEntity; - if (state.getBlock() instanceof EntityBlock entityBlock) { + if (blockState.getBlock() instanceof EntityBlock entityBlock) { // based on LevelChunk#setBlockState BlockEntity currentBlockEntity = this.getBlockEntity(pos); - if (currentBlockEntity != null && currentBlockEntity.isValidBlockState(state)) { + if (currentBlockEntity != null && currentBlockEntity.isValidBlockState(blockState)) { newBlockEntity = currentBlockEntity; // previous block entity is still valid for this block state - currentBlockEntity.setBlockState(state); + currentBlockEntity.setBlockState(blockState); } else { - newBlockEntity = entityBlock.newBlockEntity(pos, state); // create a new one when the block change + newBlockEntity = entityBlock.newBlockEntity(pos, blockState); // create a new one when the block change } } else { newBlockEntity = null; } - this.blocks.put(pos, new CapturedBlock(state, flags, newBlockEntity)); + this.blocks.put(pos, new CapturedBlock(blockState, updateFlags, newBlockEntity)); return true; } @Override - public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity, int recursionLeft) { + public boolean destroyBlock(BlockPos pos, boolean dropResources, Entity breaker, int updateLimit) { net.minecraft.world.level.block.state.BlockState blockState = this.getBlockState(pos); if (blockState.isAir()) { return false; } - this.setBlock(pos, blockState.getFluidState().createLegacyBlock(), Block.UPDATE_ALL, recursionLeft); // capture block without the event + this.setBlock(pos, blockState.getFluidState().createLegacyBlock(), Block.UPDATE_ALL, updateLimit); // capture block without the event return true; } @@ -104,15 +108,15 @@ private void iterateSnapshots(Consumer callback) { } public void placeBlocks() { - this.placeSomeBlocks($ -> true); + this.placeSomeBlocks(_ -> true); } public void placeSomeBlocks(Predicate filter) { - this.placeSomeBlocks($ -> {}, filter); + this.placeSomeBlocks(_ -> {}, filter); } public void placeBlocks(Consumer beforeRun) { - this.placeSomeBlocks(beforeRun, $ -> true); + this.placeSomeBlocks(beforeRun, _ -> true); } public void placeSomeBlocks(Consumer beforeRun, Predicate filter) { @@ -151,13 +155,13 @@ public int getHeight() { } @Override - public boolean isStateAtPosition(BlockPos pos, Predicate state) { - return state.test(this.getBlockState(pos)); + public boolean isStateAtPosition(BlockPos pos, Predicate predicate) { + return predicate.test(this.getBlockState(pos)); } @Override - public boolean isFluidAtPosition(BlockPos pos, Predicate state) { - return state.test(this.getFluidState(pos)); + public boolean isFluidAtPosition(BlockPos pos, Predicate predicate) { + return predicate.test(this.getFluidState(pos)); } @Override @@ -188,28 +192,28 @@ public RandomSource getRandom() { } @Override - public java.util.Optional getBlockEntity(BlockPos pos, net.minecraft.world.level.block.entity.BlockEntityType type) { + public Optional getBlockEntity(BlockPos pos, BlockEntityType type) { BlockEntity blockEntity = this.getBlockEntity(pos); - return blockEntity != null && blockEntity.getType() == type ? (java.util.Optional) java.util.Optional.of(blockEntity) : java.util.Optional.empty(); + return blockEntity != null && blockEntity.getType() == type ? Optional.of((T) blockEntity) : Optional.empty(); } @Override - public BlockPos getHeightmapPos(net.minecraft.world.level.levelgen.Heightmap.Types heightmapType, BlockPos pos) { - return this.level.getHeightmapPos(heightmapType, pos); + public BlockPos getHeightmapPos(Heightmap.Types type, BlockPos pos) { + return this.level.getHeightmapPos(type, pos); } @Override - public int getHeight(net.minecraft.world.level.levelgen.Heightmap.Types heightmapType, int x, int z) { - return this.level.getHeight(heightmapType, x, z); + public int getHeight(Heightmap.Types type, int x, int z) { + return this.level.getHeight(type, x, z); } @Override - public int getRawBrightness(BlockPos pos, int amount) { - return this.level.getRawBrightness(pos, amount); + public int getRawBrightness(BlockPos pos, int darkening) { + return this.level.getRawBrightness(pos, darkening); } @Override - public int getBrightness(net.minecraft.world.level.LightLayer lightType, BlockPos pos) { - return this.level.getBrightness(lightType, pos); + public int getBrightness(LightLayer layer, BlockPos pos) { + return this.level.getBrightness(layer, pos); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java index cfba954cc65c..c103a31c14b8 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/Commodore.java @@ -135,24 +135,13 @@ public List getReroutes() { // Paper start - Plugin rewrites private static final String CB_PACKAGE_PREFIX = "org/bukkit/".concat("craftbukkit/"); - private static final String LEGACY_CB_PACKAGE_PREFIX = CB_PACKAGE_PREFIX + io.papermc.paper.util.MappingEnvironment.LEGACY_CB_VERSION + "/"; private static String runtimeCbPkgPrefix() { - if (io.papermc.paper.util.MappingEnvironment.reobf()) { - return LEGACY_CB_PACKAGE_PREFIX; - } return CB_PACKAGE_PREFIX; } @Nonnull private static String getOriginalOrRewrite(@Nonnull String original) { - // Relocation is applied in reobf, and when mappings are present they handle the relocation - if (!io.papermc.paper.util.MappingEnvironment.reobf() && !io.papermc.paper.util.MappingEnvironment.hasMappings()) { - if (original.contains(LEGACY_CB_PACKAGE_PREFIX)) { - original = original.replace(LEGACY_CB_PACKAGE_PREFIX, CB_PACKAGE_PREFIX); - } - } - return original; } // Paper end - Plugin rewrites @@ -226,16 +215,12 @@ public byte[] convert(byte[] b, final String pluginName, final ApiVersion plugin ClassReader cr = new ClassReader(b); ClassWriter cw = new ClassWriter(cr, 0); - ClassVisitor visitor = cw; - - visitor = io.papermc.paper.pluginremap.reflect.ReflectionRemapper.visitor(visitor); // Paper - Map renames = new HashMap<>(RENAMES); if (pluginVersion.isOlderThan(ApiVersion.ABSTRACT_COW)) { renames.put("org/bukkit/entity/Cow", "org/bukkit/entity/AbstractCow"); } - cr.accept(new ClassRemapper(new ClassVisitor(Opcodes.ASM9, visitor) { + cr.accept(new ClassRemapper(new ClassVisitor(Opcodes.ASM9, cw) { final Set rerouteMethodData = new HashSet<>(); String className; boolean isInterface; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftDifficulty.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftDifficulty.java index f8f4d7955ce2..02d4bfcda8ef 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftDifficulty.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftDifficulty.java @@ -4,6 +4,10 @@ @NullMarked public final class CraftDifficulty { + + private CraftDifficulty() { + } + public static org.bukkit.Difficulty toBukkit(net.minecraft.world.Difficulty difficulty) { return switch (difficulty) { case EASY -> org.bukkit.Difficulty.EASY; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java index 644e7019c4c4..26a986a1b12f 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java @@ -57,12 +57,12 @@ public static Location toBukkit(Node point, Level level) { return new Location(level.getWorld(), point.x, point.y, point.z); } - public static BlockPos toBlockPosition(Location loc) { + public static BlockPos toBlockPos(Location loc) { return new BlockPos(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); } public static net.minecraft.core.GlobalPos toGlobalPos(Location loc) { - return net.minecraft.core.GlobalPos.of(((org.bukkit.craftbukkit.CraftWorld) loc.getWorld()).getHandle().dimension(), toBlockPosition(loc)); + return net.minecraft.core.GlobalPos.of(((org.bukkit.craftbukkit.CraftWorld) loc.getWorld()).getHandle().dimension(), toBlockPos(loc)); } public static Location fromGlobalPos(net.minecraft.core.GlobalPos globalPos) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java index cfa4c67e80a5..986078b33130 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -14,7 +14,7 @@ import com.mojang.serialization.JavaOps; import com.mojang.serialization.JsonOps; import io.papermc.paper.adventure.AdventureCodecs; -import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.entity.EntitySerializationFlag; import io.papermc.paper.registry.RegistryKey; import java.io.File; import java.io.IOException; @@ -25,15 +25,15 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.stream.Stream; -import io.papermc.paper.entity.EntitySerializationFlag; import net.kyori.adventure.text.event.HoverEvent; import net.minecraft.SharedConstants; import net.minecraft.advancements.AdvancementHolder; -import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.item.ItemParser; +import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; @@ -61,7 +61,6 @@ import org.bukkit.Keyed; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.Registry; import org.bukkit.UnsafeValues; import org.bukkit.World; import org.bukkit.advancement.Advancement; @@ -72,7 +71,6 @@ import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.damage.CraftDamageSourceBuilder; import org.bukkit.craftbukkit.entity.CraftEntity; import org.bukkit.craftbukkit.inventory.CraftItemStack; @@ -248,7 +246,7 @@ public Material fromLegacy(MaterialData material, boolean itemPriority) { @Override public BlockData fromLegacy(Material material, byte data) { - return CraftBlockData.fromData(CraftMagicNumbers.getBlock(material, data)); + return CraftMagicNumbers.getBlock(material, data).asBlockData(); } @Override @@ -273,7 +271,8 @@ public Material getMaterial(String material, int version) { /** * @deprecated in favor of {@link io.papermc.paper.ServerBuildInfo#minecraftVersionId()} - * Paper has used Mojang mappings since 1.20.5, and this method no longer returns a useful value. + * Paper has used Mojang mappings since 1.20.5 and now in 26.1 the server is not obfuscated anymore, + * so this method no longer returns a useful value. */ @Deprecated(forRemoval = true, since = "1.21.6") public String getMappingsVersion() { @@ -286,18 +285,22 @@ public int getDataVersion() { } @Override - public ItemStack modifyItemStack(ItemStack stack, String arguments) { - net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + public ItemStack modifyItemStack(ItemStack item, String components) { + if (item.isEmpty()) { + return item; + } + net.minecraft.world.item.ItemStack stack = CraftItemStack.unwrap(item); // mutation is expected as old behavior try { - nmsStack.applyComponents(new ItemParser(Commands.createValidationContext(CraftRegistry.getMinecraftRegistry())).parse(new StringReader(arguments)).components()); + StringReader reader = new StringReader(item.getType().key().asString() + components); + stack.applyComponents(new ItemParser(CraftRegistry.getMinecraftRegistry()).parse(reader).components()); + if (reader.canRead()) { + throw new IllegalArgumentException("Trailing input found when parsing components: " + reader.getRemaining()); + } } catch (CommandSyntaxException ex) { - com.mojang.logging.LogUtils.getClassLogger().error("Exception modifying ItemStack", new Throwable(ex)); // Paper - show stack trace + throw new IllegalArgumentException("Could not parse components: " + components, ex); } - - stack.setItemMeta(CraftItemStack.getItemMeta(nmsStack)); - - return stack; + return item; } private static File getBukkitDataPackFolder() { @@ -394,8 +397,7 @@ public static boolean isLegacy(PluginDescriptionFile pdf) { public byte[] processClass(PluginDescriptionFile pdf, String path, byte[] clazz) { // Paper start if (DISABLE_OLD_API_SUPPORT) { - // Make sure we still go through our reflection rewriting if needed - return io.papermc.paper.pluginremap.reflect.ReflectionRemapper.processClass(clazz); + return clazz; } // Paper end try { @@ -849,9 +851,9 @@ public List computeTooltipLines(final ItemSt @Override public org.bukkit.Color getSpawnEggLayerColor(final EntityType entityType, final int layer) { - final net.minecraft.world.entity.EntityType nmsType = org.bukkit.craftbukkit.entity.CraftEntityType.bukkitToMinecraft(entityType); - final net.minecraft.world.item.SpawnEggItem eggItem = net.minecraft.world.item.SpawnEggItem.byId(nmsType); - if (eggItem != null) { + final net.minecraft.world.entity.EntityType type = org.bukkit.craftbukkit.entity.CraftEntityType.bukkitToMinecraft(entityType); + final Optional> eggItem = net.minecraft.world.item.SpawnEggItem.byId(type); + if (eggItem.isPresent()) { throw new UnsupportedOperationException(); } return null; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftStructureTransformer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftStructureTransformer.java index 64d2e0212c8e..996dc34576a2 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftStructureTransformer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftStructureTransformer.java @@ -63,18 +63,18 @@ private void destroyCopies() { private BlockTransformer[] blockTransformers; private EntityTransformer[] entityTransformers; - public CraftStructureTransformer(Cause cause, WorldGenLevel generatoraccessseed, StructureManager structuremanager, Structure structure, BoundingBox structureboundingbox, ChunkPos chunkcoordintpair) { - AsyncStructureGenerateEvent event = new AsyncStructureGenerateEvent(structuremanager.level.getMinecraftWorld().getWorld(), !Bukkit.isPrimaryThread(), cause, CraftStructure.minecraftToBukkit(structure), new org.bukkit.util.BoundingBox(structureboundingbox.minX(), structureboundingbox.minY(), structureboundingbox.minZ(), structureboundingbox.maxX(), structureboundingbox.maxY(), structureboundingbox.maxZ()), chunkcoordintpair.x, chunkcoordintpair.z); + public CraftStructureTransformer(Cause cause, WorldGenLevel level, StructureManager structuremanager, Structure structure, BoundingBox box, ChunkPos center) { + AsyncStructureGenerateEvent event = new AsyncStructureGenerateEvent(structuremanager.level.getMinecraftWorld().getWorld(), !Bukkit.isPrimaryThread(), cause, CraftStructure.minecraftToBukkit(structure), new org.bukkit.util.BoundingBox(box.minX(), box.minY(), box.minZ(), box.maxX(), box.maxY(), box.maxZ()), center.x(), center.z()); Bukkit.getPluginManager().callEvent(event); this.blockTransformers = event.getBlockTransformers().values().toArray(BlockTransformer[]::new); this.entityTransformers = event.getEntityTransformers().values().toArray(EntityTransformer[]::new); - this.limitedRegion = new CraftLimitedRegion(generatoraccessseed, chunkcoordintpair); + this.limitedRegion = new CraftLimitedRegion(level, center); } - public CraftStructureTransformer(WorldGenLevel generatoraccessseed, ChunkPos chunkcoordintpair, Collection blockTransformers, Collection entityTransformers) { + public CraftStructureTransformer(WorldGenLevel level, ChunkPos center, Collection blockTransformers, Collection entityTransformers) { this.blockTransformers = blockTransformers.toArray(BlockTransformer[]::new); this.entityTransformers = entityTransformers.toArray(EntityTransformer[]::new); - this.limitedRegion = new CraftLimitedRegion(generatoraccessseed, chunkcoordintpair); + this.limitedRegion = new CraftLimitedRegion(level, center); } public boolean transformEntity(Entity entity) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/DelegatedLevelAccessor.java similarity index 58% rename from paper-server/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java rename to paper-server/src/main/java/org/bukkit/craftbukkit/util/DelegatedLevelAccessor.java index bb117482fe9d..280b95047b64 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/DelegatedLevelAccessor.java @@ -28,7 +28,6 @@ import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.ClipBlockStateContext; import net.minecraft.world.level.ClipContext; -import net.minecraft.world.level.ColorResolver; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.biome.Biome; @@ -59,7 +58,7 @@ import org.bukkit.event.entity.CreatureSpawnEvent; import org.jetbrains.annotations.Nullable; -public abstract class DelegatedGeneratorAccess implements WorldGenLevel { +public abstract class DelegatedLevelAccessor implements WorldGenLevel { private WorldGenLevel delegate; @@ -82,8 +81,8 @@ public boolean ensureCanWrite(BlockPos pos) { } @Override - public void setCurrentlyGenerating(Supplier structureName) { - this.delegate.setCurrentlyGenerating(structureName); + public void setCurrentlyGenerating(Supplier currentlyGenerating) { + this.delegate.setCurrentlyGenerating(currentlyGenerating); } @Override @@ -107,13 +106,13 @@ public LevelTickAccess getBlockTicks() { } @Override - public void scheduleTick(BlockPos pos, Block block, int delay, TickPriority priority) { - this.delegate.scheduleTick(pos, block, delay, priority); + public void scheduleTick(BlockPos pos, Block type, int tickDelay, TickPriority priority) { + this.delegate.scheduleTick(pos, type, tickDelay, priority); } @Override - public void scheduleTick(BlockPos pos, Block block, int delay) { - this.delegate.scheduleTick(pos, block, delay); + public void scheduleTick(BlockPos pos, Block type, int tickDelay) { + this.delegate.scheduleTick(pos, type, tickDelay); } @Override @@ -122,13 +121,13 @@ public LevelTickAccess getFluidTicks() { } @Override - public void scheduleTick(BlockPos pos, Fluid fluid, int delay, TickPriority priority) { - this.delegate.scheduleTick(pos, fluid, delay, priority); + public void scheduleTick(BlockPos pos, Fluid type, int tickDelay, TickPriority priority) { + this.delegate.scheduleTick(pos, type, tickDelay, priority); } @Override - public void scheduleTick(BlockPos pos, Fluid fluid, int delay) { - this.delegate.scheduleTick(pos, fluid, delay); + public void scheduleTick(BlockPos pos, Fluid type, int tickDelay) { + this.delegate.scheduleTick(pos, type, tickDelay); } @Override @@ -167,58 +166,58 @@ public RandomSource getRandom() { } @Override - public void updateNeighborsAt(BlockPos pos, Block block) { - this.delegate.updateNeighborsAt(pos, block); + public void updateNeighborsAt(BlockPos pos, Block sourceBlock) { + this.delegate.updateNeighborsAt(pos, sourceBlock); } @Override - public void neighborShapeChanged(Direction direction, BlockPos pos, BlockPos neighborPos, BlockState neighborState, @Block.UpdateFlags int flags, int recursionLeft) { - this.delegate.neighborShapeChanged(direction, pos, neighborPos, neighborState, flags, recursionLeft); + public void neighborShapeChanged(Direction direction, BlockPos pos, BlockPos neighborPos, BlockState neighborState, @Block.UpdateFlags int updateFlags, int updateLimit) { + this.delegate.neighborShapeChanged(direction, pos, neighborPos, neighborState, updateFlags, updateLimit); } @Override - public void playSound(@Nullable final Entity entity, final BlockPos pos, final SoundEvent sound, final SoundSource source, final float volume, final float pitch) { - this.delegate.playSound(entity, pos, sound, source, volume, pitch); + public void playSound(@Nullable final Entity except, final BlockPos pos, final SoundEvent sound, final SoundSource source, final float volume, final float pitch) { + this.delegate.playSound(except, pos, sound, source, volume, pitch); } @Override - public void addParticle(ParticleOptions parameters, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { - this.delegate.addParticle(parameters, x, y, z, velocityX, velocityY, velocityZ); + public void addParticle(ParticleOptions particle, double x, double y, double z, double xd, double yd, double zd) { + this.delegate.addParticle(particle, x, y, z, xd, yd, zd); } @Override - public void levelEvent(Entity entity, int eventId, BlockPos pos, int data) { - this.delegate.levelEvent(entity, eventId, pos, data); + public void levelEvent(Entity source, int type, BlockPos pos, int data) { + this.delegate.levelEvent(source, type, pos, data); } @Override - public void levelEvent(int eventId, BlockPos pos, int data) { - this.delegate.levelEvent(eventId, pos, data); + public void levelEvent(int type, BlockPos pos, int data) { + this.delegate.levelEvent(type, pos, data); } @Override - public void gameEvent(Holder gameEvent, Vec3 pos, GameEvent.Context context) { - this.delegate.gameEvent(gameEvent, pos, context); + public void gameEvent(Holder gameEvent, Vec3 position, GameEvent.Context context) { + this.delegate.gameEvent(gameEvent, position, context); } @Override - public void gameEvent(Entity entity, Holder gameEvent, Vec3 pos) { - this.delegate.gameEvent(entity, gameEvent, pos); + public void gameEvent(Entity sourceEntity, Holder gameEvent, Vec3 pos) { + this.delegate.gameEvent(sourceEntity, gameEvent, pos); } @Override - public void gameEvent(Entity entity, Holder gameEvent, BlockPos pos) { - this.delegate.gameEvent(entity, gameEvent, pos); + public void gameEvent(Entity sourceEntity, Holder gameEvent, BlockPos pos) { + this.delegate.gameEvent(sourceEntity, gameEvent, pos); } @Override - public void gameEvent(Holder gameEvent, BlockPos pos, GameEvent.Context emitter) { - this.delegate.gameEvent(gameEvent, pos, emitter); + public void gameEvent(Holder gameEvent, BlockPos pos, GameEvent.Context context) { + this.delegate.gameEvent(gameEvent, pos, context); } @Override - public void gameEvent(ResourceKey gameEvent, BlockPos pos, GameEvent.Context emitter) { - this.delegate.gameEvent(gameEvent, pos, emitter); + public void gameEvent(ResourceKey gameEvent, BlockPos pos, GameEvent.Context context) { + this.delegate.gameEvent(gameEvent, pos, context); } @Override @@ -227,28 +226,28 @@ public Optional getBlockEntity(BlockPos pos, BlockEnt } @Override - public List getEntityCollisions(Entity entity, AABB box) { - return this.delegate.getEntityCollisions(entity, box); + public List getEntityCollisions(Entity source, AABB testArea) { + return this.delegate.getEntityCollisions(source, testArea); } @Override - public boolean isUnobstructed(Entity except, VoxelShape shape) { - return this.delegate.isUnobstructed(except, shape); + public boolean isUnobstructed(Entity source, VoxelShape shape) { + return this.delegate.isUnobstructed(source, shape); } @Override - public BlockPos getHeightmapPos(Heightmap.Types heightmap, BlockPos pos) { - return this.delegate.getHeightmapPos(heightmap, pos); + public BlockPos getHeightmapPos(Heightmap.Types type, BlockPos pos) { + return this.delegate.getHeightmapPos(type, pos); } @Override - public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) { - return this.delegate.getChunk(chunkX, chunkZ, leastStatus, create); + public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus targetStatus, boolean loadOrGenerate) { + return this.delegate.getChunk(chunkX, chunkZ, targetStatus, loadOrGenerate); } @Override - public int getHeight(Heightmap.Types heightmap, int x, int z) { - return this.delegate.getHeight(heightmap, x, z); + public int getHeight(Heightmap.Types type, int x, int z) { + return this.delegate.getHeight(type, x, z); } @Override @@ -271,19 +270,15 @@ public Stream getBlockStatesIfLoaded(AABB box) { return this.delegate.getBlockStatesIfLoaded(box); } - @Override - public int getBlockTint(BlockPos pos, ColorResolver colorResolver) { - return this.delegate.getBlockTint(pos, colorResolver); - } @Override - public Holder getNoiseBiome(int biomeX, int biomeY, int biomeZ) { - return this.delegate.getNoiseBiome(biomeX, biomeY, biomeZ); + public Holder getNoiseBiome(int quartX, int quartY, int quartZ) { + return this.delegate.getNoiseBiome(quartX, quartY, quartZ); } @Override - public Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { - return this.delegate.getUncachedNoiseBiome(biomeX, biomeY, biomeZ); + public Holder getUncachedNoiseBiome(int quartX, int quartY, int quartZ) { + return this.delegate.getUncachedNoiseBiome(quartX, quartY, quartZ); } @Override @@ -342,8 +337,8 @@ public ChunkAccess getChunk(int chunkX, int chunkZ) { } @Override - public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus chunkStatus) { - return this.delegate.getChunk(chunkX, chunkZ, chunkStatus); + public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus status) { + return this.delegate.getChunk(chunkX, chunkZ, status); } @Override @@ -367,13 +362,13 @@ public int getMaxLocalRawBrightness(BlockPos pos) { } @Override - public int getMaxLocalRawBrightness(BlockPos pos, int ambientDarkness) { - return this.delegate.getMaxLocalRawBrightness(pos, ambientDarkness); + public int getMaxLocalRawBrightness(BlockPos pos, int skyDarkening) { + return this.delegate.getMaxLocalRawBrightness(pos, skyDarkening); } @Override - public boolean hasChunkAt(int x, int z) { - return this.delegate.hasChunkAt(x, z); + public boolean hasChunkAt(int blockX, int blockZ) { + return this.delegate.hasChunkAt(blockX, blockZ); } @Override @@ -382,18 +377,18 @@ public boolean hasChunkAt(BlockPos pos) { } @Override - public boolean hasChunksAt(BlockPos from, BlockPos to) { - return this.delegate.hasChunksAt(from, to); + public boolean hasChunksAt(BlockPos pos0, BlockPos pos1) { + return this.delegate.hasChunksAt(pos0, pos1); } @Override - public boolean hasChunksAt(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { - return this.delegate.hasChunksAt(minX, minY, minZ, maxX, maxY, maxZ); + public boolean hasChunksAt(int x0, int y0, int z0, int x1, int y1, int z1) { + return this.delegate.hasChunksAt(x0, y0, z0, x1, y1, z1); } @Override - public boolean hasChunksAt(int minX, int minZ, int maxX, int maxZ) { - return this.delegate.hasChunksAt(minX, minZ, maxX, maxZ); + public boolean hasChunksAt(int x0, int z0, int x1, int z1) { + return this.delegate.hasChunksAt(x0, z0, x1, z1); } @Override @@ -412,14 +407,10 @@ public EnvironmentAttributeReader environmentAttributes() { } @Override - public HolderLookup holderLookup(ResourceKey> registryKey) { - return this.delegate.holderLookup(registryKey); + public HolderLookup holderLookup(ResourceKey> key) { + return this.delegate.holderLookup(key); } - @Override - public float getShade(Direction direction, boolean shade) { - return this.delegate.getShade(direction, shade); - } @Override public LevelLightEngine getLightEngine() { @@ -427,13 +418,13 @@ public LevelLightEngine getLightEngine() { } @Override - public int getBrightness(LightLayer type, BlockPos pos) { - return this.delegate.getBrightness(type, pos); + public int getBrightness(LightLayer layer, BlockPos pos) { + return this.delegate.getBrightness(layer, pos); } @Override - public int getRawBrightness(BlockPos pos, int ambientDarkness) { - return this.delegate.getRawBrightness(pos, ambientDarkness); + public int getRawBrightness(BlockPos pos, int darkening) { + return this.delegate.getRawBrightness(pos, darkening); } @Override @@ -452,68 +443,68 @@ public boolean isUnobstructed(BlockState state, BlockPos pos, CollisionContext c } @Override - public boolean isUnobstructed(Entity entity) { - return this.delegate.isUnobstructed(entity); + public boolean isUnobstructed(Entity ignore) { + return this.delegate.isUnobstructed(ignore); } @Override - public boolean noCollision(AABB collisionBox) { - return this.delegate.noCollision(collisionBox); + public boolean noCollision(AABB aabb) { + return this.delegate.noCollision(aabb); } @Override - public boolean noCollision(Entity entity) { - return this.delegate.noCollision(entity); + public boolean noCollision(Entity source) { + return this.delegate.noCollision(source); } @Override - public boolean noCollision(Entity entity, AABB collisionBox) { - return this.delegate.noCollision(entity, collisionBox); + public boolean noCollision(Entity entity, AABB aabb) { + return this.delegate.noCollision(entity, aabb); } @Override - public boolean noCollision(Entity entity, AABB collisionBox, boolean checkFluid) { - return this.delegate.noCollision(entity, collisionBox, checkFluid); + public boolean noCollision(Entity entity, AABB aabb, boolean alwaysCollideWithFluids) { + return this.delegate.noCollision(entity, aabb, alwaysCollideWithFluids); } @Override - public boolean noBlockCollision(Entity entity, AABB collisionBox) { - return this.delegate.noBlockCollision(entity, collisionBox); + public boolean noBlockCollision(Entity entity, AABB aabb) { + return this.delegate.noBlockCollision(entity, aabb); } @Override - public Iterable getCollisions(Entity entity, AABB collisionBox) { - return this.delegate.getCollisions(entity, collisionBox); + public Iterable getCollisions(Entity source, AABB box) { + return this.delegate.getCollisions(source, box); } @Override - public Iterable getBlockCollisions(Entity entity, AABB collisionBox) { - return this.delegate.getBlockCollisions(entity, collisionBox); + public Iterable getBlockCollisions(Entity source, AABB box) { + return this.delegate.getBlockCollisions(source, box); } @Override - public Iterable getBlockAndLiquidCollisions(Entity entity, AABB collisionBox) { - return this.delegate.getBlockAndLiquidCollisions(entity, collisionBox); + public Iterable getBlockAndLiquidCollisions(Entity source, AABB box) { + return this.delegate.getBlockAndLiquidCollisions(source, box); } @Override - public BlockHitResult clipIncludingBorder(ClipContext clipContext) { - return this.delegate.clipIncludingBorder(clipContext); + public BlockHitResult clipIncludingBorder(ClipContext c) { + return this.delegate.clipIncludingBorder(c); } @Override - public boolean collidesWithSuffocatingBlock(Entity entity, AABB box) { - return this.delegate.collidesWithSuffocatingBlock(entity, box); + public boolean collidesWithSuffocatingBlock(Entity source, AABB box) { + return this.delegate.collidesWithSuffocatingBlock(source, box); } @Override - public Optional findSupportingBlock(Entity entity, AABB box) { - return this.delegate.findSupportingBlock(entity, box); + public Optional findSupportingBlock(Entity source, AABB box) { + return this.delegate.findSupportingBlock(source, box); } @Override - public Optional findFreePosition(Entity entity, VoxelShape shape, Vec3 pos, double x, double y, double z) { - return this.delegate.findFreePosition(entity, shape, pos, x, y, z); + public Optional findFreePosition(Entity source, VoxelShape allowedCenters, Vec3 preferredCenter, double sizeX, double sizeY, double sizeZ) { + return this.delegate.findFreePosition(source, allowedCenters, preferredCenter, sizeX, sizeY, sizeZ); } @Override @@ -527,8 +518,8 @@ public int getDirectSignalTo(BlockPos pos) { } @Override - public int getControlInputSignal(BlockPos pos, Direction direction, boolean diodesOnly) { - return this.delegate.getControlInputSignal(pos, direction, diodesOnly); + public int getControlInputSignal(BlockPos pos, Direction direction, boolean onlyDiodes) { + return this.delegate.getControlInputSignal(pos, direction, onlyDiodes); } @Override @@ -542,8 +533,8 @@ public int getSignal(BlockPos pos, Direction direction) { } @Override - public boolean hasNeighborSignal(BlockPos pos) { - return this.delegate.hasNeighborSignal(pos); + public boolean hasNeighborSignal(BlockPos blockPos) { + return this.delegate.hasNeighborSignal(blockPos); } @Override @@ -572,33 +563,33 @@ public int getLightEmission(BlockPos pos) { } @Override - public Stream getBlockStates(AABB area) { - return this.delegate.getBlockStates(area); + public Stream getBlockStates(AABB box) { + return this.delegate.getBlockStates(box); } @Override - public BlockHitResult isBlockInLine(ClipBlockStateContext context) { - return this.delegate.isBlockInLine(context); + public BlockHitResult isBlockInLine(ClipBlockStateContext c) { + return this.delegate.isBlockInLine(c); } @Override - public BlockHitResult clip(ClipContext traverseContext, BlockPos traversePos) { - return this.delegate.clip(traverseContext, traversePos); + public BlockHitResult clip(ClipContext c, BlockPos pos) { + return this.delegate.clip(c, pos); } @Override - public BlockHitResult clip(ClipContext context) { - return this.delegate.clip(context); + public BlockHitResult clip(ClipContext c) { + return this.delegate.clip(c); } @Override - public BlockHitResult clipWithInteractionOverride(Vec3 startVec, Vec3 endVec, BlockPos pos, VoxelShape shape, BlockState state) { - return this.delegate.clipWithInteractionOverride(startVec, endVec, pos, shape, state); + public BlockHitResult clipWithInteractionOverride(Vec3 from, Vec3 to, BlockPos pos, VoxelShape blockShape, BlockState blockState) { + return this.delegate.clipWithInteractionOverride(from, to, pos, blockShape, blockState); } @Override - public double getBlockFloorHeight(VoxelShape shape, Supplier belowShapeSupplier) { - return this.delegate.getBlockFloorHeight(shape, belowShapeSupplier); + public double getBlockFloorHeight(VoxelShape blockShape, Supplier belowBlockShape) { + return this.delegate.getBlockFloorHeight(blockShape, belowBlockShape); } @Override @@ -607,18 +598,18 @@ public double getBlockFloorHeight(BlockPos pos) { } @Override - public List getEntities(Entity except, AABB area, Predicate predicate) { - return this.delegate.getEntities(except, area, predicate); + public List getEntities(Entity except, AABB bb, Predicate selector) { + return this.delegate.getEntities(except, bb, selector); } @Override - public List getEntities(EntityTypeTest entityTypeTest, AABB bounds, Predicate predicate) { - return this.delegate.getEntities(entityTypeTest, bounds, predicate); + public List getEntities(EntityTypeTest type, AABB bb, Predicate selector) { + return this.delegate.getEntities(type, bb, selector); } @Override - public List getEntitiesOfClass(Class entityClass, AABB area, Predicate filter) { - return this.delegate.getEntitiesOfClass(entityClass, area, filter); + public List getEntitiesOfClass(Class baseClass, AABB bb, Predicate selector) { + return this.delegate.getEntitiesOfClass(baseClass, bb, selector); } @Override @@ -627,33 +618,33 @@ public List players() { } @Override - public List getEntities(Entity except, AABB area) { - return this.delegate.getEntities(except, area); + public List getEntities(Entity except, AABB bb) { + return this.delegate.getEntities(except, bb); } @Override - public List getEntitiesOfClass(Class entityClass, AABB area) { - return this.delegate.getEntitiesOfClass(entityClass, area); + public List getEntitiesOfClass(Class baseClass, AABB bb) { + return this.delegate.getEntitiesOfClass(baseClass, bb); } @Override - public Player getNearestPlayer(double x, double y, double z, double maxDistance, Predicate targetPredicate) { - return this.delegate.getNearestPlayer(x, y, z, maxDistance, targetPredicate); + public Player getNearestPlayer(double x, double y, double z, double range, Predicate predicate) { + return this.delegate.getNearestPlayer(x, y, z, range, predicate); } @Override - public Player getNearestPlayer(Entity entity, double maxDistance) { - return this.delegate.getNearestPlayer(entity, maxDistance); + public Player getNearestPlayer(Entity source, double maxDist) { + return this.delegate.getNearestPlayer(source, maxDist); } @Override - public Player getNearestPlayer(double x, double y, double z, double maxDistance, boolean ignoreCreative) { - return this.delegate.getNearestPlayer(x, y, z, maxDistance, ignoreCreative); + public Player getNearestPlayer(double x, double y, double z, double maxDist, boolean filterOutCreative) { + return this.delegate.getNearestPlayer(x, y, z, maxDist, filterOutCreative); } @Override - public boolean hasNearbyAlivePlayer(double x, double y, double z, double distance) { - return this.delegate.hasNearbyAlivePlayer(x, y, z, distance); + public boolean hasNearbyAlivePlayer(double x, double y, double z, double range) { + return this.delegate.hasNearbyAlivePlayer(x, y, z, range); } @Override @@ -662,33 +653,33 @@ public Player getPlayerByUUID(UUID uuid) { } @Override - public boolean setBlock(BlockPos pos, BlockState state, @Block.UpdateFlags int flags, int recursionLeft) { - return this.delegate.setBlock(pos, state, flags, recursionLeft); + public boolean setBlock(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags, int updateLimit) { + return this.delegate.setBlock(pos, blockState, updateFlags, updateLimit); } @Override - public boolean setBlock(BlockPos pos, BlockState state, @Block.UpdateFlags int flags) { - return this.delegate.setBlock(pos, state, flags); + public boolean setBlock(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags) { + return this.delegate.setBlock(pos, blockState, updateFlags); } @Override - public boolean removeBlock(BlockPos pos, boolean isMoving) { - return this.delegate.removeBlock(pos, isMoving); + public boolean removeBlock(BlockPos pos, boolean movedByPiston) { + return this.delegate.removeBlock(pos, movedByPiston); } @Override - public boolean destroyBlock(BlockPos pos, boolean dropBlock) { - return this.delegate.destroyBlock(pos, dropBlock); + public boolean destroyBlock(BlockPos pos, boolean dropResources) { + return this.delegate.destroyBlock(pos, dropResources); } @Override - public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity) { - return this.delegate.destroyBlock(pos, dropBlock, entity); + public boolean destroyBlock(BlockPos pos, boolean dropResources, Entity breaker) { + return this.delegate.destroyBlock(pos, dropResources, breaker); } @Override - public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity, int recursionLeft) { - return this.delegate.destroyBlock(pos, dropBlock, entity, recursionLeft); + public boolean destroyBlock(BlockPos pos, boolean dropResources, Entity breaker, int updateLimit) { + return this.delegate.destroyBlock(pos, dropResources, breaker, updateLimit); } @Override @@ -722,8 +713,8 @@ public int getMaxSectionY() { } @Override - public boolean isInsideBuildHeight(int y) { - return this.delegate.isInsideBuildHeight(y); + public boolean isInsideBuildHeight(int blockY) { + return this.delegate.isInsideBuildHeight(blockY); } @Override @@ -732,18 +723,18 @@ public boolean isOutsideBuildHeight(BlockPos pos) { } @Override - public boolean isOutsideBuildHeight(int y) { - return this.delegate.isOutsideBuildHeight(y); + public boolean isOutsideBuildHeight(int blockY) { + return this.delegate.isOutsideBuildHeight(blockY); } @Override - public int getSectionIndex(int y) { - return this.delegate.getSectionIndex(y); + public int getSectionIndex(int blockY) { + return this.delegate.getSectionIndex(blockY); } @Override - public int getSectionIndexFromSectionY(int sectionIndex) { - return this.delegate.getSectionIndexFromSectionY(sectionIndex); + public int getSectionIndexFromSectionY(int sectionY) { + return this.delegate.getSectionIndexFromSectionY(sectionY); } @Override @@ -752,8 +743,8 @@ public int getSectionYFromSectionIndex(int sectionIndex) { } @Override - public boolean isStateAtPosition(BlockPos pos, Predicate state) { - return this.delegate.isStateAtPosition(pos, state); + public boolean isStateAtPosition(BlockPos pos, Predicate predicate) { + return this.delegate.isStateAtPosition(pos, predicate); } @Override @@ -779,4 +770,3 @@ public ChunkAccess getChunkIfLoadedImmediately(final int x, final int z) { return this.delegate.getChunkIfLoadedImmediately(x, z); } } - diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/DummyLevelAccessor.java similarity index 79% rename from paper-server/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java rename to paper-server/src/main/java/org/bukkit/craftbukkit/util/DummyLevelAccessor.java index 3693c069f911..1fb4ae845640 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/DummyLevelAccessor.java @@ -3,7 +3,6 @@ import java.util.List; import java.util.function.Predicate; import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; import net.minecraft.core.particles.ParticleOptions; @@ -42,11 +41,11 @@ import net.minecraft.world.ticks.BlackholeTickAccess; import net.minecraft.world.ticks.LevelTickAccess; -public class DummyGeneratorAccess implements WorldGenLevel { +public class DummyLevelAccessor implements WorldGenLevel { - public static final WorldGenLevel INSTANCE = new DummyGeneratorAccess(); + public static final WorldGenLevel INSTANCE = new DummyLevelAccessor(); - protected DummyGeneratorAccess() { + protected DummyLevelAccessor() { } @Override @@ -70,7 +69,7 @@ public LevelTickAccess getBlockTicks() { } @Override - public void scheduleTick(BlockPos pos, Block block, int delay) { + public void scheduleTick(BlockPos pos, Block type, int tickDelay) { // Used by ComposterBlock } @@ -105,32 +104,32 @@ public RandomSource getRandom() { } @Override - public void playSound(Entity source, BlockPos pos, SoundEvent sound, SoundSource category, float volume, float pitch) { + public void playSound(Entity except, BlockPos pos, SoundEvent sound, SoundSource source, float volume, float pitch) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public void addParticle(ParticleOptions parameters, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { + public void addParticle(ParticleOptions particle, double x, double y, double z, double xd, double yd, double zd) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public void levelEvent(Entity entity, int eventId, BlockPos pos, int data) { + public void levelEvent(Entity source, int type, BlockPos pos, int data) { // Used by PowderSnowBlock.pickupBlock } @Override - public void gameEvent(Holder event, Vec3 emitterPos, GameEvent.Context emitter) { + public void gameEvent(Holder gameEvent, Vec3 position, GameEvent.Context context) { // Used by ComposterBlock } @Override - public List getEntities(Entity except, AABB box, Predicate predicate) { + public List getEntities(Entity except, AABB bb, Predicate selector) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public List getEntities(EntityTypeTest filter, AABB box, Predicate predicate) { + public List getEntities(EntityTypeTest type, AABB bb, Predicate selector) { throw new UnsupportedOperationException("Not supported yet."); } @@ -140,12 +139,12 @@ public List players() { } @Override - public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) { + public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus targetStatus, boolean loadOrGenerate) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public int getHeight(Heightmap.Types heightmap, int x, int z) { + public int getHeight(Heightmap.Types type, int x, int z) { throw new UnsupportedOperationException("Not supported yet."); } @@ -160,7 +159,7 @@ public BiomeManager getBiomeManager() { } @Override - public Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { + public Holder getUncachedNoiseBiome(int quartX, int quartY, int quartZ) { throw new UnsupportedOperationException("Not supported yet."); } @@ -194,11 +193,6 @@ public EnvironmentAttributeReader environmentAttributes() { return EnvironmentAttributeReader.EMPTY; } - @Override - public float getShade(Direction direction, boolean shaded) { - throw new UnsupportedOperationException("Not supported yet."); - } - @Override public LevelLightEngine getLightEngine() { throw new UnsupportedOperationException("Not supported yet."); @@ -240,36 +234,36 @@ public WorldBorder getWorldBorder() { } @Override - public boolean isStateAtPosition(BlockPos pos, Predicate state) { + public boolean isStateAtPosition(BlockPos pos, Predicate predicate) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public boolean isFluidAtPosition(BlockPos pos, Predicate state) { + public boolean isFluidAtPosition(BlockPos pos, Predicate predicate) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public boolean setBlock(BlockPos pos, BlockState state, @Block.UpdateFlags int flags, int recursionLeft) { + public boolean setBlock(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags, int updateLimit) { return false; } @Override - public boolean removeBlock(BlockPos pos, boolean move) { + public boolean removeBlock(BlockPos pos, boolean movedByPiston) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public boolean destroyBlock(BlockPos pos, boolean drop, Entity breakingEntity, int recursionLeft) { + public boolean destroyBlock(BlockPos pos, boolean dropResources, Entity breaker, int updateLimit) { return false; // SPIGOT-6515 } @Override - public void scheduleTick(BlockPos pos, Fluid fluid, int delay) {} + public void scheduleTick(BlockPos pos, Fluid type, int tickDelay) {} @Override - public void scheduleTick(BlockPos pos, Block block, int delay, net.minecraft.world.ticks.TickPriority priority) {} + public void scheduleTick(BlockPos pos, Block type, int tickDelay, net.minecraft.world.ticks.TickPriority priority) {} @Override - public void scheduleTick(BlockPos pos, Fluid fluid, int delay, net.minecraft.world.ticks.TickPriority priority) {} + public void scheduleTick(BlockPos pos, Fluid type, int tickDelay, net.minecraft.world.ticks.TickPriority priority) {} } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/TransformerGeneratorAccess.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/TransformerLevelAccessor.java similarity index 71% rename from paper-server/src/main/java/org/bukkit/craftbukkit/util/TransformerGeneratorAccess.java rename to paper-server/src/main/java/org/bukkit/craftbukkit/util/TransformerLevelAccessor.java index c698c8438052..23b5a09cf5f4 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/TransformerGeneratorAccess.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/TransformerLevelAccessor.java @@ -17,7 +17,7 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; -public class TransformerGeneratorAccess extends DelegatedGeneratorAccess { +public class TransformerLevelAccessor extends DelegatedLevelAccessor { private static final Logger LOGGER = LogUtils.getLogger(); @@ -51,23 +51,23 @@ public boolean addFreshEntity(Entity entity, @Nullable SpawnReason reason) { return super.addFreshEntity(entity, reason); } - public boolean setCraftBlock(BlockPos position, CraftBlockState craftBlockState, @Block.UpdateFlags int flags, int recursionLeft) { + public boolean setCraftBlock(BlockPos pos, CraftBlockState craftBlockState, @Block.UpdateFlags int updateFlags, int updateLimit) { craftBlockState = this.structureTransformer.transformCraftState(craftBlockState); // This code is based on the method 'net.minecraft.world.level.levelgen.structure.StructurePiece#placeBlock' // It ensures that any kind of block is updated correctly upon placing it BlockState snapshot = craftBlockState.getHandle(); - boolean result = super.setBlock(position, snapshot, flags, recursionLeft); - FluidState fluidState = this.getFluidState(position); + boolean result = super.setBlock(pos, snapshot, updateFlags, updateLimit); + FluidState fluidState = this.getFluidState(pos); if (!fluidState.isEmpty()) { - this.scheduleTick(position, fluidState.getType(), 0); + this.scheduleTick(pos, fluidState.getType(), 0); } if (StructurePiece.SHAPE_CHECK_BLOCKS.contains(snapshot.getBlock())) { - this.getChunk(position).markPosForPostprocessing(position); + this.getChunk(pos).markPosForPostprocessing(pos); } - BlockEntity blockEntity = this.getBlockEntity(position); + BlockEntity blockEntity = this.getBlockEntity(pos); if (blockEntity != null && craftBlockState instanceof CraftBlockEntityState craftEntityState) { try (final ProblemReporter.ScopedCollector problemReporter = new ProblemReporter.ScopedCollector( - () -> "TransformerGeneratorAccess@" + position.toShortString(), LOGGER + () -> "TransformerLevelAccessor@" + pos.toShortString(), LOGGER )) { blockEntity.loadWithComponents(TagValueInput.create( problemReporter, this.registryAccess(), craftEntityState.getSnapshotNBT() @@ -77,20 +77,20 @@ public boolean setCraftBlock(BlockPos position, CraftBlockState craftBlockState, return result; } - public boolean setCraftBlock(BlockPos pos, CraftBlockState craftBlockState, @Block.UpdateFlags int flags) { - return this.setCraftBlock(pos, craftBlockState, flags, Block.UPDATE_LIMIT); + public boolean setCraftBlock(BlockPos pos, CraftBlockState craftBlockState, @Block.UpdateFlags int updateFlags) { + return this.setCraftBlock(pos, craftBlockState, updateFlags, Block.UPDATE_LIMIT); } @Override - public boolean setBlock(BlockPos pos, BlockState state, @Block.UpdateFlags int flags, int recursionLeft) { + public boolean setBlock(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags, int updateLimit) { if (this.canTransformBlocks()) { - return this.setCraftBlock(pos, (CraftBlockState) CraftBlockStates.getBlockState(this, pos, state, null), flags, recursionLeft); + return this.setCraftBlock(pos, (CraftBlockState) CraftBlockStates.getBlockState(this, pos, blockState, null), updateFlags, updateLimit); } - return super.setBlock(pos, state, flags, recursionLeft); + return super.setBlock(pos, blockState, updateFlags, updateLimit); } @Override - public boolean setBlock(BlockPos pos, BlockState state, @Block.UpdateFlags int flags) { - return this.setBlock(pos, state, flags, Block.UPDATE_LIMIT); + public boolean setBlock(BlockPos pos, BlockState blockState, @Block.UpdateFlags int updateFlags) { + return this.setBlock(pos, blockState, updateFlags, Block.UPDATE_LIMIT); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/WorldUUID.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/WorldUUID.java deleted file mode 100644 index fd37759e6b1e..000000000000 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/WorldUUID.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.bukkit.craftbukkit.util; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.UUID; -import com.mojang.logging.LogUtils; -import org.slf4j.Logger; - -public final class WorldUUID { - - private static final Logger LOGGER = LogUtils.getLogger(); - - private WorldUUID() { - } - - public static UUID getOrCreate(File worldDir) { - File fileId = new File(worldDir, "uid.dat"); - if (fileId.exists()) { - try (DataInputStream inputStream = new DataInputStream(new FileInputStream(fileId))) { - return new UUID(inputStream.readLong(), inputStream.readLong()); - } catch (IOException ex) { - LOGGER.warn("Failed to read {}, generating new random UUID", fileId, ex); - } - } - - UUID uuid = UUID.randomUUID(); - try (DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(fileId))) { - outputStream.writeLong(uuid.getMostSignificantBits()); - outputStream.writeLong(uuid.getLeastSignificantBits()); - } catch (IOException ex) { - LOGGER.warn("Failed to write {}", fileId, ex); - } - return uuid; - } -} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java index a454350085f2..732a2a7d504b 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java @@ -84,6 +84,7 @@ public static Permission registerPermissions(Permission parent) { DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "spreadplayers", "Allows the user to teleport entities to random locations", PermissionDefault.OP, commands); DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "stopsound", "Allows the user to stop a sound", PermissionDefault.OP, commands); DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "summon", "Allows the user to summon an entity", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "swing", "Allows the user to swing arms of an entity", PermissionDefault.OP, commands); DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "tag", "Allows the user to control entity tags", PermissionDefault.OP, commands); DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "team", "Allows the user to control teams", PermissionDefault.OP, commands); DefaultPermissions.registerPermission(CommandPermissions.PREFIX + "teammsg", "Allows the user to specify the message to send to team", PermissionDefault.TRUE, commands); // defaults to all players diff --git a/paper-server/src/main/java/org/spigotmc/SpigotConfig.java b/paper-server/src/main/java/org/spigotmc/SpigotConfig.java index 97d81401484b..0e8ac5714bd8 100644 --- a/paper-server/src/main/java/org/spigotmc/SpigotConfig.java +++ b/paper-server/src/main/java/org/spigotmc/SpigotConfig.java @@ -289,12 +289,12 @@ private static void saveUserCacheOnStopOnly() { public static double movedWronglyThreshold; private static void movedWronglyThreshold() { - SpigotConfig.movedWronglyThreshold = SpigotConfig.getDouble("settings.moved-wrongly-threshold", 0.0625D); + SpigotConfig.movedWronglyThreshold = SpigotConfig.getDouble("settings.moved-wrongly-threshold", 0.0625); } public static double movedTooQuicklyMultiplier; private static void movedTooQuicklyMultiplier() { - SpigotConfig.movedTooQuicklyMultiplier = SpigotConfig.getDouble("settings.moved-too-quickly-multiplier", 10.0D); + SpigotConfig.movedTooQuicklyMultiplier = SpigotConfig.getDouble("settings.moved-too-quickly-multiplier", 10.0); } public static double maxAbsorption = 2048; diff --git a/paper-server/src/main/java/org/spigotmc/SpigotWorldConfig.java b/paper-server/src/main/java/org/spigotmc/SpigotWorldConfig.java index 43c6240ec285..9e63995af22b 100644 --- a/paper-server/src/main/java/org/spigotmc/SpigotWorldConfig.java +++ b/paper-server/src/main/java/org/spigotmc/SpigotWorldConfig.java @@ -280,7 +280,7 @@ private void hoppers() { private void arrowDespawnRate() { this.arrowDespawnRate = this.getInt("arrow-despawn-rate", 1200); this.tridentDespawnRate = this.getInt("trident-despawn-rate", this.arrowDespawnRate); - this.log("Arrow Despawn Rate: " + this.arrowDespawnRate + " Trident Respawn Rate:" + this.tridentDespawnRate); + this.log("Arrow Despawn Rate: " + this.arrowDespawnRate + " Trident Despawn Rate:" + this.tridentDespawnRate); } public boolean zombieAggressiveTowardsVillager; diff --git a/paper-server/src/main/java/org/spigotmc/TicksPerSecondCommand.java b/paper-server/src/main/java/org/spigotmc/TicksPerSecondCommand.java index 2756ca738b99..5eb0a3e02bcf 100644 --- a/paper-server/src/main/java/org/spigotmc/TicksPerSecondCommand.java +++ b/paper-server/src/main/java/org/spigotmc/TicksPerSecondCommand.java @@ -21,7 +21,7 @@ public class TicksPerSecondCommand extends Command { public TicksPerSecondCommand(String name) { super(name); this.description = "Gets the current ticks per second for the server"; - this.usageMessage = "/tps"; + this.usageMessage = "/tps [mem]"; this.setPermission("bukkit.command.tps"); } diff --git a/paper-server/src/main/resources/data/minecraft/datapacks/paper/pack.mcmeta b/paper-server/src/main/resources/data/minecraft/datapacks/paper/pack.mcmeta index 9d380a80550a..bcccdca454e0 100644 --- a/paper-server/src/main/resources/data/minecraft/datapacks/paper/pack.mcmeta +++ b/paper-server/src/main/resources/data/minecraft/datapacks/paper/pack.mcmeta @@ -1,7 +1,10 @@ { - "pack": { - "description": "Built-in Paper Datapack", - "min_format": 94, - "max_format": 94 - } + "pack": { + "description": "Built-in Paper Datapack", + "max_format": 101, + "min_format": [ + 101, + 1 + ] + } } diff --git a/paper-server/src/test/java/io/papermc/paper/adventure/AdventureCodecsTest.java b/paper-server/src/test/java/io/papermc/paper/adventure/AdventureCodecsTest.java index ea59a360732a..ec8053b6a7f7 100644 --- a/paper-server/src/test/java/io/papermc/paper/adventure/AdventureCodecsTest.java +++ b/paper-server/src/test/java/io/papermc/paper/adventure/AdventureCodecsTest.java @@ -39,6 +39,7 @@ import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.resources.Identifier; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.Items; import org.bukkit.support.RegistryHelper; import org.bukkit.support.environment.VanillaFeature; @@ -165,11 +166,11 @@ void testShowItemHoverEvent() { assertTrue(dataResult.result().isPresent(), () -> dataResult + " result is not present"); final net.minecraft.network.chat.HoverEvent.ShowItem nms = (net.minecraft.network.chat.HoverEvent.ShowItem) dataResult.result().orElseThrow().getFirst(); assertEquals(hoverEvent.action().toString(), nms.action().getSerializedName()); - final ItemStack item = nms.item(); - assertNotNull(item); - assertEquals(hoverEvent.value().count(), item.getCount()); - assertEquals(hoverEvent.value().item().asString(), item.getItem().toString()); - assertEquals(stack.getComponentsPatch(), item.getComponentsPatch()); + final ItemStackTemplate itemTemplate = nms.item(); + assertNotNull(itemTemplate); + assertEquals(hoverEvent.value().count(), itemTemplate.count()); + assertEquals(hoverEvent.value().item().asString(), itemTemplate.item().unwrapKey().orElseThrow().identifier().toString()); + assertEquals(stack.getComponentsPatch(), itemTemplate.components()); } @Test @@ -405,7 +406,7 @@ static List testSelectors() { static List testBlockNbts() { return List.of( - blockNBT().nbtPath("abc").localPos(1.23d, 2.0d, 3.89d).build(), + blockNBT().nbtPath("abc").localPos(1.23, 2.0, 3.89).build(), blockNBT().nbtPath("xyz").absoluteWorldPos(4, 5, 6).interpret(true).build(), blockNBT().nbtPath("eeee").relativeWorldPos(7, 83, 900) .separator(text(';')) diff --git a/paper-server/src/test/java/io/papermc/paper/block/CraftBlockDataDestroySpeedTest.java b/paper-server/src/test/java/io/papermc/paper/block/CraftBlockDataDestroySpeedTest.java index f70985a8e222..6e8fed185f49 100644 --- a/paper-server/src/test/java/io/papermc/paper/block/CraftBlockDataDestroySpeedTest.java +++ b/paper-server/src/test/java/io/papermc/paper/block/CraftBlockDataDestroySpeedTest.java @@ -56,7 +56,7 @@ public void testCorrectEnchantmentDestroySpeedComputation() { itemStack.set(DataComponents.ENCHANTMENTS, mutable.toImmutable()); // Compute expected value by running the entire attribute instance chain - final AttributeInstance dummyInstance = new AttributeInstance(Attributes.MINING_EFFICIENCY, $ -> { + final AttributeInstance dummyInstance = new AttributeInstance(Attributes.MINING_EFFICIENCY, _ -> { }); EnchantmentHelper.forEachModifier(itemStack, EquipmentSlot.MAINHAND, (attributeHolder, attributeModifier) -> { if (attributeHolder.is(Attributes.MINING_EFFICIENCY)) dummyInstance.addTransientModifier(attributeModifier); diff --git a/paper-server/src/test/java/io/papermc/paper/block/InstrumentSoundTest.java b/paper-server/src/test/java/io/papermc/paper/block/InstrumentSoundTest.java index bf4365c607b0..8d4862d3aa6f 100644 --- a/paper-server/src/test/java/io/papermc/paper/block/InstrumentSoundTest.java +++ b/paper-server/src/test/java/io/papermc/paper/block/InstrumentSoundTest.java @@ -22,7 +22,7 @@ static Stream bukkitInstruments() { @ParameterizedTest @MethodSource("bukkitInstruments") void checkInstrumentSound(final Instrument bukkit) { - final NoteBlockInstrument nms = CraftBlockData.toNMS(bukkit, NoteBlockInstrument.class); + final NoteBlockInstrument nms = CraftBlockData.toVanilla(bukkit, NoteBlockInstrument.class); assertEquals(nms.getSoundEvent(), CraftSound.bukkitToMinecraftHolder(bukkit.getSound())); } } diff --git a/paper-server/src/test/java/io/papermc/paper/item/ItemStackDataComponentTest.java b/paper-server/src/test/java/io/papermc/paper/item/ItemStackDataComponentTest.java index 8a83468ec2f4..a0c2fc1882bb 100644 --- a/paper-server/src/test/java/io/papermc/paper/item/ItemStackDataComponentTest.java +++ b/paper-server/src/test/java/io/papermc/paper/item/ItemStackDataComponentTest.java @@ -30,7 +30,6 @@ import net.kyori.adventure.util.TriState; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.Registries; -import net.minecraft.world.item.EitherHolder; import net.minecraft.world.item.Items; import net.minecraft.world.item.JukeboxSongs; import org.bukkit.Color; @@ -369,7 +368,7 @@ void testJukeboxWithEitherKey() { void testJukeboxWithEitherHolder() { final net.minecraft.world.item.ItemStack internalStack = new net.minecraft.world.item.ItemStack(Items.STONE); internalStack.set(DataComponents.JUKEBOX_PLAYABLE, new net.minecraft.world.item.JukeboxPlayable( - new EitherHolder<>(RegistryHelper.registryAccess().lookupOrThrow(Registries.JUKEBOX_SONG).getOrThrow(JukeboxSongs.FIVE)) + RegistryHelper.registryAccess().lookupOrThrow(Registries.JUKEBOX_SONG).getOrThrow(JukeboxSongs.FIVE) )); final ItemStack apiStack = CraftItemStack.asBukkitCopy(internalStack); diff --git a/paper-server/src/test/java/io/papermc/paper/world/TranslationKeyTest.java b/paper-server/src/test/java/io/papermc/paper/world/TranslationKeyTest.java index 282ead960273..b026158150a1 100644 --- a/paper-server/src/test/java/io/papermc/paper/world/TranslationKeyTest.java +++ b/paper-server/src/test/java/io/papermc/paper/world/TranslationKeyTest.java @@ -22,7 +22,7 @@ public void testDifficultyKeys() { @Test public void testFireworkEffectType() { for (final FireworkEffect.Type type : FireworkEffect.Type.values()) { - final net.minecraft.world.item.component.FireworkExplosion.Shape nmsType = org.bukkit.craftbukkit.inventory.CraftMetaFirework.getNBT(type); + final net.minecraft.world.item.component.FireworkExplosion.Shape nmsType = org.bukkit.craftbukkit.inventory.CraftMetaFirework.getShape(type); Assertions.assertTrue(nmsType.getName().getContents() instanceof TranslatableContents, "contents aren't translatable"); Assertions.assertEquals(((TranslatableContents) nmsType.getName().getContents()).getKey(), type.translationKey(), "translation key mismatch for " + type); } diff --git a/paper-server/src/test/java/org/bukkit/BlockDataConversionTest.java b/paper-server/src/test/java/org/bukkit/BlockDataConversionTest.java index d6a1e4c42254..541ee0d98210 100644 --- a/paper-server/src/test/java/org/bukkit/BlockDataConversionTest.java +++ b/paper-server/src/test/java/org/bukkit/BlockDataConversionTest.java @@ -1,16 +1,16 @@ package org.bukkit; -import static org.junit.jupiter.api.Assertions.*; import java.util.stream.Stream; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.support.environment.AllFeatures; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertNotNull; + /** * This test class ensures that all Blocks (as registered in BuiltInRegistries.BLOCK) * can be converted into their CraftBlockData equivalent. @@ -26,6 +26,6 @@ public static Stream data() { @MethodSource("data") public void testNotNull(BlockState state) { assertNotNull(state); - assertNotNull(CraftBlockData.fromData(state)); + assertNotNull(state.asBlockData()); } } diff --git a/paper-server/src/test/java/org/bukkit/BlockDataTest.java b/paper-server/src/test/java/org/bukkit/BlockDataTest.java index 9168a173577d..cd910ad0a4f0 100644 --- a/paper-server/src/test/java/org/bukkit/BlockDataTest.java +++ b/paper-server/src/test/java/org/bukkit/BlockDataTest.java @@ -1,9 +1,5 @@ package org.bukkit; -import static org.bukkit.support.MatcherAssert.*; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.*; - import net.minecraft.core.Direction; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.CakeBlock; @@ -17,56 +13,66 @@ import org.bukkit.support.environment.VanillaFeature; import org.junit.jupiter.api.Test; +import static org.bukkit.support.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + @VanillaFeature public class BlockDataTest { @Test public void testParsing() { - BlockData cakeTest = CraftBlockData.fromData(Blocks.CAKE.defaultBlockState().setValue(CakeBlock.BITES, 3)); + BlockData cakeTest = Blocks.CAKE.defaultBlockState().setValue(CakeBlock.BITES, 3).asBlockData(); - BlockData materialString = CraftBlockData.newData(BlockType.CAKE, "[bites=3]"); + BlockData materialString = CraftBlockData.fromString(BlockType.CAKE, "[bites=3]"); assertThat(materialString, is(cakeTest)); - BlockData combined = CraftBlockData.newData(null, "cake[bites=3]"); + BlockData combined = CraftBlockData.fromString(null, "cake[bites=3]"); assertThat(combined, is(cakeTest)); - BlockData combinedMinecraft = CraftBlockData.newData(null, "minecraft:cake[bites=3]"); + BlockData combinedMinecraft = CraftBlockData.fromString(null, "minecraft:cake[bites=3]"); assertThat(combinedMinecraft, is(cakeTest)); - BlockData inverted = CraftBlockData.newData(null, cakeTest.getAsString()); + BlockData inverted = CraftBlockData.fromString(null, cakeTest.getAsString()); assertThat(inverted, is(cakeTest)); } @Test public void testBadMaterial() { - assertThrows(IllegalArgumentException.class, () -> CraftBlockData.newData(null, "invalid")); + assertThrows(IllegalArgumentException.class, () -> CraftBlockData.fromString(null, "invalid")); } @Test public void testBadSyntax() { - assertThrows(IllegalArgumentException.class, () -> CraftBlockData.newData(null, "minecraft:cake[bites=3")); + assertThrows(IllegalArgumentException.class, () -> CraftBlockData.fromString(null, "minecraft:cake[bites=3")); } @Test public void testDoubleMaterial() { - assertThrows(IllegalArgumentException.class, () -> CraftBlockData.newData(BlockType.CAKE, "minecraft:cake[bites=3]")); + assertThrows(IllegalArgumentException.class, () -> CraftBlockData.fromString(BlockType.CAKE, "minecraft:cake[bites=3]")); } @Test public void testMistake() { - BlockData cakeTest = CraftBlockData.fromData(Blocks.CAKE.defaultBlockState().setValue(CakeBlock.BITES, 3)); + BlockData cakeTest = Blocks.CAKE.defaultBlockState().setValue(CakeBlock.BITES, 3).asBlockData(); - assertThrows(IllegalArgumentException.class, () -> CraftBlockData.newData(BlockType.CAKE, cakeTest.toString())); + assertThrows(IllegalArgumentException.class, () -> CraftBlockData.fromString(BlockType.CAKE, cakeTest.toString())); } @Test public void testItemParse() { - assertThrows(IllegalArgumentException.class, () -> CraftBlockData.newData(null, "minecraft:diamond_axe")); + assertThrows(IllegalArgumentException.class, () -> CraftBlockData.fromString(null, "minecraft:diamond_axe")); } @Test public void testClone() { - Cake cakeTest = (Cake) CraftBlockData.fromData(Blocks.CAKE.defaultBlockState().setValue(CakeBlock.BITES, 3)); + Cake cakeTest = (Cake) Blocks.CAKE.defaultBlockState().setValue(CakeBlock.BITES, 3).asBlockData(); Cake clone = (Cake) cakeTest.clone(); assertNotSame(cakeTest, clone, "Clone did not return new object"); @@ -78,9 +84,9 @@ public void testClone() { @Test public void testMerge() { - Chest trueTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]"); - Chest falseTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=false]"); - Chest waterlogged = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); + Chest trueTarget = (Chest) CraftBlockData.fromString(null, "minecraft:chest[facing=east,waterlogged=true]"); + Chest falseTarget = (Chest) CraftBlockData.fromString(null, "minecraft:chest[facing=east,waterlogged=false]"); + Chest waterlogged = (Chest) CraftBlockData.fromString(null, "minecraft:chest[waterlogged=true]"); BlockData candidate; @@ -95,9 +101,9 @@ public void testMerge() { @Test public void testMergeAny() { - Chest trueTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]"); - Chest falseTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=false]"); - Chest any = (Chest) CraftBlockData.newData(null, "minecraft:chest"); + Chest trueTarget = (Chest) CraftBlockData.fromString(null, "minecraft:chest[facing=east,waterlogged=true]"); + Chest falseTarget = (Chest) CraftBlockData.fromString(null, "minecraft:chest[facing=east,waterlogged=false]"); + Chest any = (Chest) CraftBlockData.fromString(null, "minecraft:chest"); BlockData candidate; @@ -112,16 +118,16 @@ public void testMergeAny() { @Test public void testCannotMerge1() { - Chest one = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]"); - Chest two = (Chest) CraftBlockData.fromData(Blocks.CHEST.defaultBlockState()); + Chest one = (Chest) CraftBlockData.fromString(null, "minecraft:chest[facing=east,waterlogged=true]"); + Chest two = (Chest) Blocks.CHEST.defaultBlockState().asBlockData(); assertThrows(IllegalArgumentException.class, () -> one.merge(two)); } @Test public void testCannotMerge2() { - Chest one = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); - Chest two = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); + Chest one = (Chest) CraftBlockData.fromString(null, "minecraft:chest[waterlogged=true]"); + Chest two = (Chest) CraftBlockData.fromString(null, "minecraft:chest[waterlogged=true]"); one.merge(two); @@ -131,22 +137,22 @@ public void testCannotMerge2() { @Test public void testCannotMerge3() { - Chest one = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); - Chest two = (Chest) CraftBlockData.newData(null, "minecraft:trapped_chest[waterlogged=true]"); + Chest one = (Chest) CraftBlockData.fromString(null, "minecraft:chest[waterlogged=true]"); + Chest two = (Chest) CraftBlockData.fromString(null, "minecraft:trapped_chest[waterlogged=true]"); assertThrows(IllegalArgumentException.class, () -> one.merge(two)); } @Test public void testMatch() { - assertTrue(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"))); - assertFalse(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=false]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"))); - assertTrue(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.newData(null, "minecraft:chest"))); - assertFalse(CraftBlockData.newData(null, "minecraft:trapped_chest[facing=east,waterlogged=false]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"))); - assertTrue(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true,facing=east]"))); + assertTrue(CraftBlockData.fromString(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.fromString(null, "minecraft:chest[waterlogged=true]"))); + assertFalse(CraftBlockData.fromString(null, "minecraft:chest[facing=east,waterlogged=false]").matches(CraftBlockData.fromString(null, "minecraft:chest[waterlogged=true]"))); + assertTrue(CraftBlockData.fromString(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.fromString(null, "minecraft:chest"))); + assertFalse(CraftBlockData.fromString(null, "minecraft:trapped_chest[facing=east,waterlogged=false]").matches(CraftBlockData.fromString(null, "minecraft:chest[waterlogged=true]"))); + assertTrue(CraftBlockData.fromString(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.fromString(null, "minecraft:chest[waterlogged=true,facing=east]"))); - Chest one = (Chest) CraftBlockData.fromData(Blocks.CHEST.defaultBlockState().setValue(ChestBlock.FACING, Direction.EAST)); - Chest two = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=false]"); + Chest one = (Chest) Blocks.CHEST.defaultBlockState().setValue(ChestBlock.FACING, Direction.EAST).asBlockData(); + Chest two = (Chest) CraftBlockData.fromString(null, "minecraft:chest[waterlogged=false]"); assertTrue(one.matches(two)); assertFalse(two.matches(one)); @@ -155,7 +161,7 @@ public void testMatch() { @Test public void testGetAsString() { String dataString = "minecraft:chest[facing=east,waterlogged=true]"; - BlockData data = CraftBlockData.newData(null, dataString); + BlockData data = CraftBlockData.fromString(null, dataString); assertThat(data.getAsString(true), is(dataString)); assertThat(data.getAsString(false), is("minecraft:chest[facing=east,type=single,waterlogged=true]")); @@ -163,7 +169,7 @@ public void testGetAsString() { @Test public void testGetAsString2() { - Chest data = (Chest) CraftBlockData.fromData(Blocks.CHEST.defaultBlockState().setValue(ChestBlock.FACING, Direction.EAST)); + Chest data = (Chest) Blocks.CHEST.defaultBlockState().setValue(ChestBlock.FACING, Direction.EAST).asBlockData(); assertThat(data.getAsString(true), is("minecraft:chest[facing=east,type=single,waterlogged=false]")); assertThat(data.getAsString(false), is("minecraft:chest[facing=east,type=single,waterlogged=false]")); diff --git a/paper-server/src/test/java/org/bukkit/InstrumentTest.java b/paper-server/src/test/java/org/bukkit/InstrumentTest.java index 0363b615f3a7..35ec6465708e 100644 --- a/paper-server/src/test/java/org/bukkit/InstrumentTest.java +++ b/paper-server/src/test/java/org/bukkit/InstrumentTest.java @@ -7,13 +7,12 @@ import static org.hamcrest.CoreMatchers.is; @Normal -public class InstrumentTest { // Paper - moved to internals as this test now access the sound registry. +@Deprecated +public class InstrumentTest { @Test public void getByType() { for (Instrument instrument : Instrument.values()) { - // Paper - byte magic values are still used - assertThat(Instrument.getByType(instrument.getType()), is(instrument)); } } diff --git a/paper-server/src/test/java/org/bukkit/ParticleTest.java b/paper-server/src/test/java/org/bukkit/ParticleTest.java index 414586df882d..b04339c4aa47 100644 --- a/paper-server/src/test/java/org/bukkit/ParticleTest.java +++ b/paper-server/src/test/java/org/bukkit/ParticleTest.java @@ -2,7 +2,7 @@ import com.mojang.serialization.DataResult; import java.util.Optional; -import java.util.stream.Stream; +import java.util.Set; import net.minecraft.core.particles.BlockParticleOption; import net.minecraft.core.particles.ColorParticleOption; import net.minecraft.core.particles.DustColorTransitionOptions; @@ -24,7 +24,6 @@ import org.bukkit.block.data.BlockData; import org.bukkit.craftbukkit.CraftParticle; import org.bukkit.craftbukkit.CraftRegistry; -import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.inventory.ItemStack; @@ -32,7 +31,6 @@ import org.joml.Vector3f; import org.joml.Vector4f; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; @@ -46,8 +44,8 @@ @VanillaFeature public class ParticleTest { - public static Stream data() { - return CraftRegistry.getMinecraftRegistry(Registries.PARTICLE_TYPE).keySet().stream().map(Arguments::of); + public static Set data() { + return CraftRegistry.getMinecraftRegistry(Registries.PARTICLE_TYPE).keySet(); } @ParameterizedTest @@ -160,10 +158,10 @@ private void testDustOption(Particle bukkit, net.min } private void testItemStack(Particle bukkit, net.minecraft.core.particles.ParticleType minecraft) { - ItemStack itemStack = new ItemStack(Material.STONE); - ItemParticleOption param = this.createAndTest(bukkit, minecraft, itemStack, ItemParticleOption.class); + ItemStack item = ItemStack.of(Material.STONE); + ItemParticleOption param = this.createAndTest(bukkit, minecraft, item, ItemParticleOption.class); - assertEquals(itemStack, CraftItemStack.asBukkitCopy(param.getItem()), String.format(""" + assertEquals(CraftItemStack.asTemplate(item), param.getItem(), String.format(""" ItemStack for particle %s do not match. Did something change in the implementation or minecraft? """, bukkit.getKey())); @@ -173,7 +171,7 @@ private void testBlockData(Particle bukkit, net.mine BlockData blockData = Bukkit.createBlockData(Material.STONE); BlockParticleOption param = this.createAndTest(bukkit, minecraft, blockData, BlockParticleOption.class); - assertEquals(blockData, CraftBlockData.fromData(param.getState()), String.format(""" + assertEquals(blockData, param.getState().asBlockData(), String.format(""" Block data for particle %s do not match. Did something change in the implementation or minecraft? """, bukkit.getKey())); diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java index d484d1de01bc..8f5eb2df8989 100644 --- a/paper-server/src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java +++ b/paper-server/src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java @@ -1,6 +1,8 @@ package org.bukkit.craftbukkit.generator; import static org.junit.jupiter.api.Assertions.*; + +import net.minecraft.world.level.chunk.PalettedContainerFactory; import org.bukkit.Material; import org.bukkit.block.data.BlockData; import org.bukkit.support.RegistryHelper; @@ -34,23 +36,30 @@ private void testSetRegion(OldCraftChunkData data, int minx, int miny, int minz, } } + private static PalettedContainerFactory palettedContainerFactory() { + final class Holder { + public static final PalettedContainerFactory FACTORY = PalettedContainerFactory.create(RegistryHelper.registryAccess()); + } + return Holder.FACTORY; + } + @Test public void testMinHeight() { - OldCraftChunkData data = new OldCraftChunkData(-128, 128, RegistryHelper.context().palettedContainerFactory().get()); + OldCraftChunkData data = new OldCraftChunkData(-128, 128, palettedContainerFactory()); assertTrue(this.testSetBlock(data, 0, -256, 0, ChunkDataTest.RED_WOOL, ChunkDataTest.AIR), "Could not set block below min height"); assertTrue(this.testSetBlock(data, 0, -64, 0, ChunkDataTest.RED_WOOL, ChunkDataTest.RED_WOOL), "Could set block above min height"); } @Test public void testMaxHeight() { - OldCraftChunkData data = new OldCraftChunkData(0, 128, RegistryHelper.context().palettedContainerFactory().get()); + OldCraftChunkData data = new OldCraftChunkData(0, 128, palettedContainerFactory()); assertTrue(this.testSetBlock(data, 0, 128, 0, ChunkDataTest.RED_WOOL, ChunkDataTest.AIR), "Could not set block above max height"); assertTrue(this.testSetBlock(data, 0, 127, 0, ChunkDataTest.RED_WOOL, ChunkDataTest.RED_WOOL), "Could set block below max height"); } @Test public void testBoundsCheckingSingle() { - OldCraftChunkData data = new OldCraftChunkData(0, 256, RegistryHelper.context().palettedContainerFactory().get()); + OldCraftChunkData data = new OldCraftChunkData(0, 256, palettedContainerFactory()); assertTrue(this.testSetBlock(data, 0, 0, 0, ChunkDataTest.RED_WOOL, ChunkDataTest.RED_WOOL), "Can set block inside chunk bounds"); assertTrue(this.testSetBlock(data, 15, 255, 15, ChunkDataTest.RED_WOOL, ChunkDataTest.RED_WOOL), "Can set block inside chunk bounds"); assertTrue(this.testSetBlock(data, -1, 0, 0, ChunkDataTest.RED_WOOL, ChunkDataTest.AIR), "Can no set block outside chunk bounds"); @@ -63,7 +72,7 @@ public void testBoundsCheckingSingle() { @Test public void testSetRegion() { - OldCraftChunkData data = new OldCraftChunkData(0, 256, RegistryHelper.context().palettedContainerFactory().get()); + OldCraftChunkData data = new OldCraftChunkData(0, 256, palettedContainerFactory()); this.testSetRegion(data, -100, 0, -100, 0, 256, 0, ChunkDataTest.RED_WOOL); // exclusively outside this.testSetRegion(data, 16, 256, 16, 0, 0, 0, ChunkDataTest.RED_WOOL); // minimum >= maximum this.testSetRegion(data, 0, 0, 0, 0, 0, 0, ChunkDataTest.RED_WOOL); // minimum == maximum diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java index 1c80fe7549d7..07ffea04850c 100644 --- a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java +++ b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java @@ -94,10 +94,10 @@ private NamespacedKey requestKey(String keyName) { public void testNBTTagStoring() { CraftMetaItem itemMeta = this.createComplexItemMeta(); - CraftMetaItem.Applicator compound = new CraftMetaItem.Applicator() {}; // Paper - itemMeta.applyToItem(compound); + CraftMetaItem.Applicator applicator = new CraftMetaItem.Applicator() {}; // Paper + itemMeta.applyToItem(applicator); - assertEquals(itemMeta, new CraftMetaItem(compound.build(), null)); // Paper + assertEquals(itemMeta, new CraftMetaItem(applicator.build(), null)); // Paper } @Test diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java index cd783d3de3e4..f375aea89cf9 100644 --- a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java +++ b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java @@ -46,7 +46,6 @@ import org.bukkit.inventory.meta.LeatherArmorMeta; import org.bukkit.inventory.meta.MapMeta; import org.bukkit.inventory.meta.PotionMeta; -import org.bukkit.inventory.meta.SpawnEggMeta; import org.bukkit.inventory.meta.TropicalFishBucketMeta; import org.bukkit.inventory.meta.trim.ArmorTrim; import org.bukkit.inventory.meta.trim.TrimMaterial; @@ -430,8 +429,8 @@ public void testAttributeModifiers() { @Test public void testBlockData() { BlockDataMeta itemMeta = (BlockDataMeta) Bukkit.getItemFactory().getItemMeta(Material.CHEST); - itemMeta.setBlockData(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]")); - assertThat(itemMeta.getBlockData(Material.CHEST), is(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"))); + itemMeta.setBlockData(CraftBlockData.fromString(null, "minecraft:chest[waterlogged=true]")); + assertThat(itemMeta.getBlockData(Material.CHEST), is(CraftBlockData.fromString(null, "minecraft:chest[waterlogged=true]"))); } @Test diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/MetaHandledTagsTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/MetaHandledTagsTest.java index df20446af3d4..dd282530b357 100644 --- a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/MetaHandledTagsTest.java +++ b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/MetaHandledTagsTest.java @@ -23,7 +23,7 @@ public void checkAllMetasHaveHandledTags() { assertFalse(subclasses.isEmpty(), "found 0 sub types"); for (final ClassInfo subclass : subclasses) { final Class clazz = subclass.loadClass(CraftMetaItem.class); - CraftMetaItem.getTopLevelHandledDcts(clazz); // load into map + CraftMetaItem.getTopLevelHandledComponents(clazz); // load into map assertTrue(CraftMetaItem.HANDLED_DCTS_PER_TYPE.containsKey(clazz), subclass.getName() + " not found in handled tags map"); } } catch (Exception e) { diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java index f33b49915d1f..f56d5d2c72ce 100644 --- a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java +++ b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java @@ -128,10 +128,10 @@ public void testCopyToReplace() { public void testNBTTagStoring() { CraftMetaItem itemMeta = this.createComplexItemMeta(); - CraftMetaItem.Applicator compound = new CraftMetaItem.Applicator() {}; // Paper - itemMeta.applyToItem(compound); + CraftMetaItem.Applicator applicator = new CraftMetaItem.Applicator() {}; // Paper + itemMeta.applyToItem(applicator); - assertEquals(itemMeta, new CraftMetaItem(compound.build(), null)); // Paper + assertEquals(itemMeta, new CraftMetaItem(applicator.build(), null)); // Paper } @Test @@ -463,7 +463,7 @@ private static Stream testListTypeArgumentSource() { } @Test - public void testEmptyListApplicationToAnyType() throws IOException { + public void testEmptyListApplicationToAnyType() { final CraftMetaItem craftItem = new CraftMetaItem(DataComponentPatch.EMPTY, null); // Paper final PersistentDataContainer container = craftItem.getPersistentDataContainer(); @@ -519,7 +519,7 @@ public void testListOfListViaContainerArray() { assertNotNull(containerListList); assertEquals(1, containerListList.size()); - final PersistentDataContainer[] arrayOfPDC = containerListList.get(0); + final PersistentDataContainer[] arrayOfPDC = containerListList.getFirst(); assertEquals(2, arrayOfPDC.length); assertEquals("hi", arrayOfPDC[0].get(PersistentDataContainerTest.requestKey("a"), PersistentDataType.STRING)); diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java index c6e5e2506b46..62f41aa7e711 100644 --- a/paper-server/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java +++ b/paper-server/src/test/java/org/bukkit/craftbukkit/legacy/LegacyTest.java @@ -156,6 +156,8 @@ public class LegacyTest { // 1.21.11 Material.COPPER_NAUTILUS_ARMOR, Material.COPPER_SPEAR, Material.DIAMOND_NAUTILUS_ARMOR, Material.DIAMOND_SPEAR, Material.GOLDEN_NAUTILUS_ARMOR, Material.GOLDEN_SPEAR, Material.IRON_NAUTILUS_ARMOR, Material.IRON_SPEAR, Material.NAUTILUS_SPAWN_EGG, Material.NETHERITE_NAUTILUS_ARMOR, Material.NETHERITE_SPEAR, Material.STONE_SPEAR, Material.WOODEN_SPEAR, Material.ZOMBIE_NAUTILUS_SPAWN_EGG, Material.CAMEL_HUSK_SPAWN_EGG, Material.PARCHED_SPAWN_EGG, Material.NETHERITE_HORSE_ARMOR, + // 26.1 + Material.GOLDEN_DANDELION, Material.POTTED_GOLDEN_DANDELION, // Material.LEGACY_AIR, Material.LEGACY_DEAD_BUSH, Material.LEGACY_BURNING_FURNACE, Material.LEGACY_WALL_SIGN, Material.LEGACY_REDSTONE_TORCH_OFF, Material.LEGACY_SKULL, Material.LEGACY_REDSTONE_COMPARATOR_ON, Material.LEGACY_WALL_BANNER, Material.LEGACY_MONSTER_EGG)); diff --git a/paper-server/src/test/java/org/bukkit/registry/RegistryClassTest.java b/paper-server/src/test/java/org/bukkit/registry/RegistryClassTest.java index 04caee19bd69..647c3edf2048 100644 --- a/paper-server/src/test/java/org/bukkit/registry/RegistryClassTest.java +++ b/paper-server/src/test/java/org/bukkit/registry/RegistryClassTest.java @@ -490,30 +490,30 @@ public void testMissingConstants(Class type, ResourceKey missingKeys = new ArrayList<>(); for (Object nmsObject : registry) { - Identifier minecraftKey = registry.getKey(nmsObject); + Identifier key = registry.getKey(nmsObject); try { - Field field = type.getField(this.convertToFieldName(minecraftKey.getPath())); + Field field = type.getField(this.convertToFieldName(key.getPath())); // Only fields which are not Deprecated // and have the right registry item associated with the field count. if (!RegistryClassTest.isValidField(type, field)) { - missingKeys.add(minecraftKey); + missingKeys.add(key); continue; } T keyed = (T) field.get(null); if (keyed == null) { - missingKeys.add(minecraftKey); + missingKeys.add(key); continue; } - if (!keyed.getKey().equals(CraftNamespacedKey.fromMinecraft(minecraftKey))) { - missingKeys.add(minecraftKey); + if (!keyed.getKey().equals(CraftNamespacedKey.fromMinecraft(key))) { + missingKeys.add(key); } } catch (NoSuchFieldException e) { - missingKeys.add(minecraftKey); + missingKeys.add(key); } } diff --git a/paper-server/src/test/java/org/bukkit/registry/RegistryConstantsTest.java b/paper-server/src/test/java/org/bukkit/registry/RegistryConstantsTest.java index 2718c82e0b6b..90d6a0d542d2 100644 --- a/paper-server/src/test/java/org/bukkit/registry/RegistryConstantsTest.java +++ b/paper-server/src/test/java/org/bukkit/registry/RegistryConstantsTest.java @@ -56,7 +56,8 @@ public static void populateIgnored() { DataComponents.BLOCK_ENTITY_DATA, DataComponents.BUCKET_ENTITY_DATA, DataComponents.LOCK, - DataComponents.CREATIVE_SLOT_LOCK + DataComponents.CREATIVE_SLOT_LOCK, + DataComponents.ADDITIONAL_TRADE_COST )); } diff --git a/paper-server/src/test/java/org/bukkit/support/DummyServerHelper.java b/paper-server/src/test/java/org/bukkit/support/DummyServerHelper.java index c6e49a553c30..5811a3bbde12 100644 --- a/paper-server/src/test/java/org/bukkit/support/DummyServerHelper.java +++ b/paper-server/src/test/java/org/bukkit/support/DummyServerHelper.java @@ -40,7 +40,7 @@ public static Server setup() { when(instance.getUnsafe()).then(mock -> CraftMagicNumbers.INSTANCE); - when(instance.createBlockData(any(Material.class))).then(mock -> CraftBlockData.newData(((Material) mock.getArgument(0)).asBlockType(), null)); + when(instance.createBlockData(any(Material.class))).then(mock -> CraftBlockData.fromString(((Material) mock.getArgument(0)).asBlockType(), null)); when(instance.getLootTable(any())).then(mock -> new CraftLootTable(mock.getArgument(0), RegistryHelper.context().datapack().fullRegistries().getLootTable(ResourceKey.create(Registries.LOOT_TABLE, CraftNamespacedKey.toMinecraft(mock.getArgument(0)))))); diff --git a/paper-server/src/test/java/org/bukkit/support/RegistryHelper.java b/paper-server/src/test/java/org/bukkit/support/RegistryHelper.java index 8bf9b94d0bb6..007c49e0de7b 100644 --- a/paper-server/src/test/java/org/bukkit/support/RegistryHelper.java +++ b/paper-server/src/test/java/org/bukkit/support/RegistryHelper.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Locale; -import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Stream; import net.minecraft.SharedConstants; @@ -29,7 +28,6 @@ import net.minecraft.world.flag.FeatureFlags; import net.minecraft.world.level.DataPackConfig; import net.minecraft.world.level.WorldDataConfiguration; -import net.minecraft.world.level.chunk.PalettedContainerFactory; import org.bukkit.Keyed; import org.bukkit.NamespacedKey; @@ -56,8 +54,7 @@ public static SetupContext context() { public record SetupContext( ReloadableServerResources datapack, - RegistryAccess registries, - Supplier palettedContainerFactory + RegistryAccess registries ) { } @@ -78,11 +75,11 @@ public static PackedRegistries createRegistries(ResourceManager resourceManager) List> pendingTags = TagLoader.loadTagsForExistingRegistries(resourceManager, layers.getLayer(RegistryLayer.STATIC)); List> lookupsWithPendingTags = TagLoader.buildUpdatedLookups(layers.getAccessForLoading(RegistryLayer.WORLDGEN), pendingTags); - RegistryAccess.Frozen worldGenRegistries = RegistryDataLoader.load(resourceManager, lookupsWithPendingTags, RegistryDataLoader.WORLDGEN_REGISTRIES); + RegistryAccess.Frozen worldGenRegistries = RegistryDataLoader.load(resourceManager, lookupsWithPendingTags, RegistryDataLoader.WORLDGEN_REGISTRIES, Util.backgroundExecutor()).join(); layers = layers.replaceFrom(RegistryLayer.WORLDGEN, worldGenRegistries); List> staticAndWorldgenLookups = Stream.concat(lookupsWithPendingTags.stream(), worldGenRegistries.listRegistries()).toList(); - RegistryAccess.Frozen dimensionRegistries = RegistryDataLoader.load(resourceManager, staticAndWorldgenLookups, RegistryDataLoader.DIMENSION_REGISTRIES); + RegistryAccess.Frozen dimensionRegistries = RegistryDataLoader.load(resourceManager, staticAndWorldgenLookups, RegistryDataLoader.DIMENSION_REGISTRIES, Util.backgroundExecutor()).join(); layers = layers.replaceFrom(RegistryLayer.DIMENSIONS, dimensionRegistries); // load registry here to ensure bukkit object registry are correctly delayed if needed try { @@ -104,13 +101,12 @@ public static void setup(FeatureFlagSet enabledFeatures) { // Register vanilla packs ReloadableServerResources datapack = ReloadableServerResources.loadResources(resourceManager, registries.layers(), registries.pendingTags(), enabledFeatures, Commands.CommandSelection.DEDICATED, LevelBasedPermissionSet.ALL_PERMISSIONS, Util.backgroundExecutor(), Runnable::run).join(); // Bind tags - datapack.updateStaticRegistryTags(); + datapack.updateComponentsAndStaticRegistryTags(); RegistryAccess registryAccess = registries.access(); setupContext = new SetupContext( datapack, - registryAccess, - () -> PalettedContainerFactory.create(registryAccess) + registryAccess ); } @@ -138,7 +134,7 @@ private static MultiPackResourceManager createResourceManager(FeatureFlagSet ena private static final Pattern ILLEGAL_FIELD_CHARACTERS = Pattern.compile("[.-/]"); - public static String formatKeyAsField(String path) { + private static String formatKeyAsField(String path) { return ILLEGAL_FIELD_CHARACTERS.matcher(path.toUpperCase(Locale.ENGLISH)).replaceAll("_"); } } diff --git a/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java b/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java index 00b949fc0982..0f849c2ffc66 100644 --- a/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java +++ b/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java @@ -13,12 +13,16 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.sounds.SoundEvent; import net.minecraft.world.effect.MobEffect; -import net.minecraft.world.entity.animal.feline.CatVariant; +import net.minecraft.world.entity.animal.chicken.ChickenSoundVariant; import net.minecraft.world.entity.animal.chicken.ChickenVariant; +import net.minecraft.world.entity.animal.cow.CowSoundVariant; import net.minecraft.world.entity.animal.cow.CowVariant; -import net.minecraft.world.entity.animal.pig.PigVariant; -import net.minecraft.world.entity.animal.nautilus.ZombieNautilusVariant; +import net.minecraft.world.entity.animal.feline.CatSoundVariant; +import net.minecraft.world.entity.animal.feline.CatVariant; import net.minecraft.world.entity.animal.frog.FrogVariant; +import net.minecraft.world.entity.animal.nautilus.ZombieNautilusVariant; +import net.minecraft.world.entity.animal.pig.PigSoundVariant; +import net.minecraft.world.entity.animal.pig.PigVariant; import net.minecraft.world.entity.animal.wolf.WolfSoundVariant; import net.minecraft.world.entity.animal.wolf.WolfVariant; import net.minecraft.world.entity.decoration.painting.PaintingVariant; @@ -139,13 +143,17 @@ public Object[] get() { register(Registries.BLOCK, BlockType.class, CraftBlockType.class, net.minecraft.world.level.block.Block.class); register(Registries.FROG_VARIANT, Frog.Variant.class, CraftFrog.CraftVariant.class, FrogVariant.class); register(Registries.CAT_VARIANT, Cat.Type.class, CraftCat.CraftType.class, CatVariant.class); + register(Registries.CAT_SOUND_VARIANT, Cat.SoundVariant.class, CraftCat.CraftSoundVariant.class, CatSoundVariant.class); register(Registries.MAP_DECORATION_TYPE, MapCursor.Type.class, CraftMapCursor.CraftType.class, MapDecorationType.class); register(Registries.BANNER_PATTERN, PatternType.class, CraftPatternType.class, BannerPattern.class); register(Registries.MENU, MenuType.class, CraftMenuType.class, net.minecraft.world.inventory.MenuType.class); register(Registries.DATA_COMPONENT_TYPE, io.papermc.paper.datacomponent.DataComponentType.class, DataComponentTypes.class, io.papermc.paper.datacomponent.PaperDataComponentType.class, net.minecraft.core.component.DataComponentType.class); register(Registries.CHICKEN_VARIANT, Chicken.Variant.class, CraftChicken.CraftVariant.class, ChickenVariant.class); + register(Registries.CHICKEN_SOUND_VARIANT, Chicken.SoundVariant.class, CraftChicken.CraftSoundVariant.class, ChickenSoundVariant.class); register(Registries.COW_VARIANT, Cow.Variant.class, CraftCow.CraftVariant.class, CowVariant.class); + register(Registries.COW_SOUND_VARIANT, Cow.SoundVariant.class, CraftCow.CraftSoundVariant.class, CowSoundVariant.class); register(Registries.PIG_VARIANT, Pig.Variant.class, CraftPig.CraftVariant.class, PigVariant.class); + register(Registries.PIG_SOUND_VARIANT, Pig.SoundVariant.class, CraftPig.CraftSoundVariant.class, PigSoundVariant.class); register(Registries.ZOMBIE_NAUTILUS_VARIANT, ZombieNautilus.Variant.class, CraftZombieNautilus.CraftVariant.class, ZombieNautilusVariant.class); register(Registries.DIALOG, Dialog.class, PaperDialog.class, net.minecraft.server.dialog.Dialog.class); register(Registries.GAME_RULE, GameRule.class, GameRules.class, CraftGameRule.class, net.minecraft.world.level.gamerules.GameRule.class); diff --git a/settings.gradle.kts b/settings.gradle.kts index 9289c95a7301..d7d8681bc89c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -55,6 +55,18 @@ fun optionalInclude(name: String, op: (ProjectDescriptor.() -> Unit)? = null) { } } +gradle.lifecycle.beforeProject { + val mcVersion = providers.gradleProperty("mcVersion").get().trim() + val paperVersionChannel = providers.gradleProperty("channel").get().trim() + val paperBuildNumber = providers.environmentVariable("BUILD_NUMBER").orNull?.trim()?.toInt() + val versionString = if (paperBuildNumber == null) { + "$mcVersion.local-SNAPSHOT" + } else { + "$mcVersion.build.$paperBuildNumber-${paperVersionChannel.lowercase()}" + } + version = versionString +} + if (providers.gradleProperty("paperBuildCacheEnabled").orNull.toBoolean()) { val buildCacheUsername = providers.gradleProperty("paperBuildCacheUsername").orElse("").get() val buildCachePassword = providers.gradleProperty("paperBuildCachePassword").orElse("").get()